mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-22 21:45:56 +03:00
feat(activity): show owner info in CollectionPreviewBlock
This commit is contained in:
parent
1cfabf7f5e
commit
5eac0f1a9f
@ -216,3 +216,6 @@ export const grids = {
|
||||
mobile: { width: 344, rowGap: 8 },
|
||||
},
|
||||
};
|
||||
|
||||
export const profileDefaultPicture =
|
||||
"https://slate.textile.io/ipfs/bafkreick3nscgixwfpq736forz7kzxvvhuej6kszevpsgmcubyhsx2pf7i";
|
||||
|
@ -71,7 +71,12 @@ export default function ActivityCollectionGroup({
|
||||
<div css={Styles.COLLECTIONS_PREVIEW_GRID}>
|
||||
{elements.map((collection) => (
|
||||
<Link key={collection.id} href={`/$/slate/${collection.id}`} onAction={onAction}>
|
||||
<CollectionPreviewBlock collection={collection} viewer={viewer} />
|
||||
<CollectionPreviewBlock
|
||||
collection={collection}
|
||||
viewer={viewer}
|
||||
owner={collection.owner}
|
||||
onAction={onAction}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
{showMore &&
|
||||
@ -84,12 +89,22 @@ export default function ActivityCollectionGroup({
|
||||
key={collection.id}
|
||||
>
|
||||
<Link key={collection.id} href={`/$/slate/${collection.id}`} onAction={onAction}>
|
||||
<CollectionPreviewBlock collection={collection} viewer={viewer} />
|
||||
<CollectionPreviewBlock
|
||||
collection={collection}
|
||||
viewer={viewer}
|
||||
owner={collection.owner}
|
||||
onAction={onAction}
|
||||
/>
|
||||
</Link>
|
||||
</motion.div>
|
||||
) : (
|
||||
<Link key={collection.id} href={`/$/slate/${collection.id}`} onAction={onAction}>
|
||||
<CollectionPreviewBlock collection={collection} viewer={viewer} />
|
||||
<CollectionPreviewBlock
|
||||
collection={collection}
|
||||
viewer={viewer}
|
||||
owner={collection.owner}
|
||||
onAction={onAction}
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import * as Styles from "~/common/styles";
|
||||
import * as Utilities from "~/common/utilities";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { Link } from "~/components/core/Link";
|
||||
import { css } from "@emotion/react";
|
||||
@ -58,7 +59,12 @@ export default function ProfileInfo({ owner, viewer, time, action, onAction }) {
|
||||
return (
|
||||
<Link href={`/$/user/${owner.id}`} onAction={onAction}>
|
||||
<div css={STYLES_PROFILE_CONTAINER}>
|
||||
<img src={photo} alt={`${username} profile`} css={STYLES_PROFILE} />
|
||||
<img
|
||||
src={photo}
|
||||
alt={`${username} profile`}
|
||||
css={STYLES_PROFILE}
|
||||
onError={(e) => (e.target.src = Constants.profileDefaultPicture)}
|
||||
/>
|
||||
<div css={STYLES_MOBILE_ALIGN}>
|
||||
<span>
|
||||
<H4 color="textBlack" css={[STYLES_TEXT_BLACK, Styles.HEADING_04]}>
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Typography from "~/components/system/components/Typography";
|
||||
import * as Styles from "~/common/styles";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { Divider } from "~/components/system/components/Divider";
|
||||
import { Logo } from "~/common/logo";
|
||||
@ -10,6 +11,7 @@ import { LikeButton, SaveButton } from "~/components/core/ObjectPreview/componen
|
||||
import { useLikeHandler, useSaveHandler } from "~/common/hooks";
|
||||
import { FollowButton } from "~/components/core/CollectionPreviewBlock/components";
|
||||
import { useFollowHandler } from "~/components/core/CollectionPreviewBlock/hooks";
|
||||
import { Link } from "~/components/core/Link";
|
||||
|
||||
import ObjectPlaceholder from "~/components/core/ObjectPreview/placeholders";
|
||||
|
||||
@ -152,7 +154,7 @@ const useCollectionCarrousel = ({ objects }) => {
|
||||
return { selectBatchIdx, selectedBatch, selectedIdx };
|
||||
};
|
||||
|
||||
export default function CollectionPreview({ collection, viewer }) {
|
||||
export default function CollectionPreview({ collection, viewer, owner, onAction }) {
|
||||
const { follow, followCount, isFollowed } = useFollowHandler({ collection, viewer });
|
||||
const filePreviews = React.useMemo(() => {
|
||||
const files = collection?.objects || [];
|
||||
@ -242,16 +244,34 @@ export default function CollectionPreview({ collection, viewer }) {
|
||||
{followCount}
|
||||
</Typography.P1>
|
||||
</div>
|
||||
<div css={Styles.CONTAINER_CENTERED}>
|
||||
<img
|
||||
css={STYLES_PROFILE_IMAGE}
|
||||
src="https://slate.textile.io/ipfs/bafkreick3nscgixwfpq736forz7kzxvvhuej6kszevpsgmcubyhsx2pf7i"
|
||||
alt="owner profile"
|
||||
/>
|
||||
<Typography.P2 style={{ marginLeft: 8 }} color="textGrayDark">
|
||||
Wes Anderson
|
||||
</Typography.P2>
|
||||
</div>
|
||||
|
||||
{owner && (
|
||||
<div style={{ alignItems: "end" }} css={Styles.CONTAINER_CENTERED}>
|
||||
<Link
|
||||
href={`/$/user/${owner.id}`}
|
||||
onAction={onAction}
|
||||
aria-label={`Visit ${owner.username}'s profile`}
|
||||
title={`Visit ${owner.username}'s profile`}
|
||||
>
|
||||
<img
|
||||
css={STYLES_PROFILE_IMAGE}
|
||||
src={owner.data.photo}
|
||||
alt={`${owner.username} profile`}
|
||||
onError={(e) => (e.target.src = Constants.profileDefaultPicture)}
|
||||
/>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/$/user/${owner.id}`}
|
||||
onAction={onAction}
|
||||
aria-label={`Visit ${owner.username}'s profile`}
|
||||
title={`Visit ${owner.username}'s profile`}
|
||||
>
|
||||
<Typography.P2 style={{ marginLeft: 8 }} color="textGrayDark">
|
||||
{owner.username}
|
||||
</Typography.P2>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Typography from "~/components/system/components/Typography";
|
||||
import * as Styles from "~/common/styles";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { useInView } from "~/common/hooks";
|
||||
import { isBlurhashValid } from "blurhash";
|
||||
@ -9,6 +10,7 @@ import { Blurhash } from "react-blurhash";
|
||||
import { css } from "@emotion/react";
|
||||
import { FollowButton } from "~/components/core/CollectionPreviewBlock/components";
|
||||
import { useFollowHandler } from "~/components/core/CollectionPreviewBlock/hooks";
|
||||
import { Link } from "~/components/core/Link";
|
||||
|
||||
const STYLES_CONTAINER = (theme) => css`
|
||||
display: flex;
|
||||
@ -129,7 +131,7 @@ const useCollectionCarrousel = ({ objects }) => {
|
||||
};
|
||||
};
|
||||
|
||||
export default function ImageCollectionPreview({ collection, viewer }) {
|
||||
export default function ImageCollectionPreview({ collection, viewer, owner, onAction }) {
|
||||
const { follow, followCount, isFollowed } = useFollowHandler({ collection, viewer });
|
||||
|
||||
const filePreviews = React.useMemo(() => {
|
||||
@ -147,14 +149,8 @@ export default function ImageCollectionPreview({ collection, viewer }) {
|
||||
ref: previewerRef,
|
||||
});
|
||||
|
||||
const {
|
||||
isLoaded,
|
||||
blurhash,
|
||||
selectedImage,
|
||||
handleLoading,
|
||||
selectedIdx,
|
||||
selectImageByIdx,
|
||||
} = useCollectionCarrousel({ objects: filePreviews });
|
||||
const { isLoaded, blurhash, selectedImage, handleLoading, selectedIdx, selectImageByIdx } =
|
||||
useCollectionCarrousel({ objects: filePreviews });
|
||||
|
||||
const nbrOfFiles = collection?.objects?.length || 0;
|
||||
|
||||
@ -218,16 +214,33 @@ export default function ImageCollectionPreview({ collection, viewer }) {
|
||||
{followCount}
|
||||
</Typography.P1>
|
||||
</div>
|
||||
<div css={Styles.CONTAINER_CENTERED}>
|
||||
<img
|
||||
css={STYLES_PROFILE_IMAGE}
|
||||
src="https://slate.textile.io/ipfs/bafkreick3nscgixwfpq736forz7kzxvvhuej6kszevpsgmcubyhsx2pf7i"
|
||||
alt="owner profile"
|
||||
/>
|
||||
<Typography.P2 style={{ marginLeft: 8 }} color="textGrayDark">
|
||||
Wes Anderson
|
||||
</Typography.P2>
|
||||
</div>
|
||||
{owner && (
|
||||
<div style={{ alignItems: "end" }} css={Styles.CONTAINER_CENTERED}>
|
||||
<Link
|
||||
href={`/$/user/${owner.id}`}
|
||||
onAction={onAction}
|
||||
aria-label={`Visit ${owner.username}'s profile`}
|
||||
title={`Visit ${owner.username}'s profile`}
|
||||
>
|
||||
<img
|
||||
css={STYLES_PROFILE_IMAGE}
|
||||
src={owner.data.photo}
|
||||
alt={`${owner.username} profile`}
|
||||
onError={(e) => (e.target.src = Constants.profileDefaultPicture)}
|
||||
/>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/$/user/${owner.id}`}
|
||||
onAction={onAction}
|
||||
aria-label={`Visit ${owner.username}'s profile`}
|
||||
title={`Visit ${owner.username}'s profile`}
|
||||
>
|
||||
<Typography.P2 style={{ marginLeft: 8 }} color="textGrayDark">
|
||||
{owner.username}
|
||||
</Typography.P2>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,14 +4,28 @@ import * as Validations from "~/common/validations";
|
||||
import ImageCollectionPreview from "./ImageCollectionPreview";
|
||||
import FilesCollectionPreview from "./FilesCollectionPreview";
|
||||
|
||||
export default function CollectionPreview({ collection, viewer }) {
|
||||
export default function CollectionPreview({ collection, viewer, owner, onAction }) {
|
||||
const objects = collection.objects.filter((file) =>
|
||||
Validations.isPreviewableImage(file.data.type)
|
||||
);
|
||||
|
||||
if (objects.length > 0) {
|
||||
return <ImageCollectionPreview collection={{ ...collection, objects }} viewer={viewer} />;
|
||||
return (
|
||||
<ImageCollectionPreview
|
||||
collection={{ ...collection, objects }}
|
||||
viewer={viewer}
|
||||
owner={owner}
|
||||
onAction={onAction}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <FilesCollectionPreview collection={collection} viewer={viewer} />;
|
||||
return (
|
||||
<FilesCollectionPreview
|
||||
collection={collection}
|
||||
viewer={viewer}
|
||||
owner={owner}
|
||||
onAction={onAction}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ export default function ObjectPreviewPremitive({
|
||||
css={STYLES_PROFILE_IMAGE}
|
||||
src={owner.data.photo}
|
||||
alt={`${owner.username} profile`}
|
||||
onError={(e) => (e.target.src = Constants.profileDefaultPicture)}
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
|
@ -354,7 +354,7 @@ function CollectionsPage({
|
||||
onAction={onAction}
|
||||
collection={collection}
|
||||
viewer={viewer}
|
||||
owner={user}
|
||||
owner={tab === "collections" ? user : collection.owner}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import * as Styles from "~/common/styles";
|
||||
import * as Typography from "~/components/system/components/Typography";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { Divider } from "~/components/system/components/Divider";
|
||||
import { Logo } from "~/common/logo";
|
||||
@ -142,7 +143,12 @@ export default function ProfilePreviewBlock({ onAction, viewer, profile }) {
|
||||
return (
|
||||
<div css={STYLES_CONTAINER}>
|
||||
<div css={[STYLES_PROFILE_DESCRIPTION, Styles.HORIZONTAL_CONTAINER]}>
|
||||
<img css={STYLES_PROFILE_PREVIEW} src={profile.data.photo} alt={`${profile.username}`} />
|
||||
<img
|
||||
css={STYLES_PROFILE_PREVIEW}
|
||||
src={profile.data.photo}
|
||||
alt={`${profile.username}`}
|
||||
onError={(e) => (e.target.src = Constants.profileDefaultPicture)}
|
||||
/>
|
||||
<div style={{ marginLeft: 16 }} css={Styles.VERTICAL_CONTAINER}>
|
||||
<div>
|
||||
<Typography.H4>{profile.username}</Typography.H4>
|
||||
|
@ -30,8 +30,20 @@ export default async ({
|
||||
const slateFilesFields = ["files", "slate_files.createdAt", "files.id", "objects"];
|
||||
const slateFilesQuery = `coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??`;
|
||||
|
||||
const slateFields = [
|
||||
const slateOwnerFields = [
|
||||
"slate_table",
|
||||
"slate_with_objects.*",
|
||||
...Constants.userPreviewProperties,
|
||||
"owner",
|
||||
"slate_with_objects",
|
||||
"users",
|
||||
"slate_with_objects.ownerId",
|
||||
"users.id",
|
||||
];
|
||||
const slateOwnerQuery = `?? as (SELECT ??, json_build_object('id', ??, 'data', ??, 'username', ??) as ?? FROM ?? LEFT JOIN ?? ON ?? = ?? ) `;
|
||||
|
||||
const slateFields = [
|
||||
"slate_with_objects",
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
@ -48,11 +60,13 @@ export default async ({
|
||||
"files.id",
|
||||
"slate_files.fileId",
|
||||
"slates.id",
|
||||
...slateOwnerFields,
|
||||
];
|
||||
const slateQuery = `WITH ?? as (SELECT ??, ??, ??, ??, ??, ??, ??, ${slateFilesQuery} FROM ?? LEFT JOIN ?? on ?? = ?? LEFT JOIN ?? on ?? = ?? GROUP BY ??)`;
|
||||
const slateQuery = `WITH ?? as (SELECT ??, ??, ??, ??, ??, ??, ??, ${slateFilesQuery} FROM ?? LEFT JOIN ?? on ?? = ?? LEFT JOIN ?? on ?? = ?? GROUP BY ??), ${slateOwnerQuery}`;
|
||||
|
||||
const userFilesFields = ["files", "files.createdAt", "files.id", "objects"];
|
||||
const userFilesQuery = `coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??`;
|
||||
|
||||
const userFields = [
|
||||
"user_table",
|
||||
"users.id",
|
||||
|
@ -6,8 +6,20 @@ export default async ({ earliestTimestamp, latestTimestamp }) => {
|
||||
const slateFilesFields = ["files", "slate_files.createdAt", "files.id", "objects"];
|
||||
const slateFilesQuery = `coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??`;
|
||||
|
||||
const slateFields = [
|
||||
const slateOwnerFields = [
|
||||
"slate_table",
|
||||
"slate_with_objects.*",
|
||||
...Constants.userPreviewProperties,
|
||||
"owner",
|
||||
"slate_with_objects",
|
||||
"users",
|
||||
"slate_with_objects.ownerId",
|
||||
"users.id",
|
||||
];
|
||||
const slateOwnerQuery = `?? as (SELECT ??, json_build_object('id', ??, 'data', ??, 'username', ??) as ?? FROM ?? LEFT JOIN ?? ON ?? = ?? ) `;
|
||||
|
||||
const slateFields = [
|
||||
"slate_with_objects",
|
||||
"slates.id",
|
||||
"slates.slatename",
|
||||
"slates.data",
|
||||
@ -24,11 +36,13 @@ export default async ({ earliestTimestamp, latestTimestamp }) => {
|
||||
"files.id",
|
||||
"slate_files.fileId",
|
||||
"slates.id",
|
||||
...slateOwnerFields,
|
||||
];
|
||||
const slateQuery = `WITH ?? as (SELECT ??, ??, ??, ??, ??, ??, ??, ${slateFilesQuery} FROM ?? LEFT JOIN ?? on ?? = ?? LEFT JOIN ?? on ?? = ?? GROUP BY ??)`;
|
||||
const slateQuery = `WITH ?? as (SELECT ??, ??, ??, ??, ??, ??, ??, ${slateFilesQuery} FROM ?? LEFT JOIN ?? on ?? = ?? LEFT JOIN ?? on ?? = ?? GROUP BY ??), ${slateOwnerQuery}`;
|
||||
|
||||
const userFilesFields = ["files", "files.createdAt", "files.id", "objects"];
|
||||
const userFilesQuery = `coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??`;
|
||||
|
||||
const userFields = [
|
||||
"user_table",
|
||||
"users.id",
|
||||
|
@ -11,6 +11,12 @@ export default async ({ ownerId }) => {
|
||||
// const slateFiles = () =>
|
||||
// DB.raw("json_agg(?? order by ?? asc) as ??", ["files", "slate_files.createdAt", "objects"]);
|
||||
|
||||
const ownerQueryFields = ["*", ...Constants.userPreviewProperties, "owner"];
|
||||
const ownerQuery = DB.raw(
|
||||
`??, json_build_object('id', ??, 'data', ??, 'username', ??) as ??`,
|
||||
ownerQueryFields
|
||||
);
|
||||
|
||||
const slateFiles = () =>
|
||||
DB.raw("coalesce(json_agg(?? order by ?? asc) filter (where ?? is not null), '[]') as ??", [
|
||||
"files",
|
||||
@ -19,14 +25,20 @@ export default async ({ ownerId }) => {
|
||||
"objects",
|
||||
]);
|
||||
|
||||
const query = await DB.select(...Serializers.slateProperties, slateFiles())
|
||||
const query = await DB.with("slates", (db) =>
|
||||
db
|
||||
.select(...Serializers.slateProperties, slateFiles())
|
||||
.from("slates")
|
||||
.join("subscriptions", "subscriptions.slateId", "=", "slates.id")
|
||||
.join("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.join("files", "slate_files.fileId", "=", "files.id")
|
||||
.where({ "subscriptions.ownerId": ownerId, "slates.isPublic": true })
|
||||
// .orderBy("subscriptions.createdAt", "desc");
|
||||
.groupBy("slates.id")
|
||||
)
|
||||
.select(ownerQuery)
|
||||
.from("slates")
|
||||
.join("subscriptions", "subscriptions.slateId", "=", "slates.id")
|
||||
.join("slate_files", "slate_files.slateId", "=", "slates.id")
|
||||
.join("files", "slate_files.fileId", "=", "files.id")
|
||||
.where({ "subscriptions.ownerId": ownerId, "slates.isPublic": true })
|
||||
// .orderBy("subscriptions.createdAt", "desc");
|
||||
.groupBy("slates.id");
|
||||
.join("users", "slates.ownerId", "users.id");
|
||||
|
||||
if (!query || query.error) {
|
||||
return [];
|
||||
@ -39,7 +51,7 @@ export default async ({ ownerId }) => {
|
||||
|
||||
return JSON.parse(JSON.stringify(serialized));
|
||||
},
|
||||
errorFn: async (e) => {
|
||||
errorFn: async () => {
|
||||
Logging.error({
|
||||
error: true,
|
||||
decorator: "GET_SUBSCRIPTIONS_BY_USER_ID",
|
||||
|
@ -31,6 +31,7 @@ export const sanitizeSlate = (entity) => {
|
||||
ownerId: entity.ownerId,
|
||||
isPublic: entity.isPublic,
|
||||
objects: entity.objects,
|
||||
owner: entity.owner,
|
||||
user: entity.user, //NOTE(martina): this is not in the database. It is added after
|
||||
data: {
|
||||
name: entity.data?.name,
|
||||
|
@ -72,6 +72,8 @@ export default class SceneSlates extends React.Component {
|
||||
key={slate.id}
|
||||
collection={slate}
|
||||
viewer={this.props.viewer}
|
||||
owner={this.props.viewer}
|
||||
onAction={this.props.onAction}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
@ -101,7 +103,9 @@ export default class SceneSlates extends React.Component {
|
||||
<CollectionPreviewBlock
|
||||
key={slate.id}
|
||||
collection={slate}
|
||||
owner={slate.owner}
|
||||
viewer={this.props.viewer}
|
||||
onAction={this.props.onAction}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
|
Loading…
Reference in New Issue
Block a user