Skip to content

Commit

Permalink
Merge pull request #2050 from daostack/feature/CW-2017-mark-feed-item…
Browse files Browse the repository at this point in the history
…-as-unread

FE mark as unread\read functionality #2017
  • Loading branch information
budnik9 authored Sep 11, 2023
2 parents 3f74a4e + e7460da commit dde1cf6
Show file tree
Hide file tree
Showing 20 changed files with 150 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ const DiscussionFeedCard = forwardRef<FeedItemRef, DiscussionFeedCardProps>(
commonMember,
feedItemFollow,
getNonAllowedItems,
feedItemUserMetadata,
},
{
report: onReportModalOpen,
Expand Down Expand Up @@ -324,6 +325,7 @@ const DiscussionFeedCard = forwardRef<FeedItemRef, DiscussionFeedCardProps>(
isLoading={isLoading}
menuItems={menuItems}
seenOnce={feedItemUserMetadata?.seenOnce}
seen={feedItemUserMetadata?.seen}
ownerId={item.userId}
discussionPredefinedType={discussion?.predefinedType}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Trash2Icon,
UnfollowIcon,
UnpinIcon,
Message3Icon,
} from "@/shared/icons";
import { ContextMenuItem as Item, UploadFile } from "@/shared/interfaces";
import { parseStringToTextEditorValue } from "@/shared/ui-kit";
Expand All @@ -31,7 +32,13 @@ export const useMenuItems = (
actions: Actions,
): Item[] => {
const dispatch = useDispatch();
const { discussion, commonId, feedItem, feedItemFollow } = options;
const {
discussion,
commonId,
feedItem,
feedItemFollow,
feedItemUserMetadata,
} = options;
const { report, share, remove } = actions;
const allowedMenuItems = getAllowedItems({ ...options, feedItemFollow });
const items: Item[] = [
Expand Down Expand Up @@ -59,6 +66,38 @@ export const useMenuItems = (
onClick: share,
icon: <Share3Icon />,
},
{
id: FeedItemMenuItem.MarkUnread,
text: "Mark as unread",
onClick: async () => {
if (!commonId || !feedItem) {
return;
}

await CommonFeedService.markCommonFeedItemAsUnseen(
commonId,
feedItem.id,
);
},
icon: <Message3Icon />,
},
{
id: FeedItemMenuItem.MarkRead,
text: "Mark as read",
onClick: async () => {
if (!commonId || !feedItem) {
return;
}

await CommonFeedService.markCommonFeedItemAsSeen({
commonId,
feedObjectId: feedItem.id,
lastSeenId: feedItemUserMetadata?.lastSeen?.id,
type: feedItemUserMetadata?.lastSeen?.type,
});
},
icon: <Message3Icon />,
},
{
id: FeedItemMenuItem.Report,
text: "Report",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CommonFeedType } from "@/shared/models";
import { notEmpty } from "@/shared/utils/notEmpty";
import { FeedItemMenuItem, FeedItemPinAction } from "../../FeedItem/constants";
import { GetAllowedItemsOptions } from "../../FeedItem/types";
import { checkIsEditItemAllowed } from "./checkIsEditItemAllowed";
Expand Down Expand Up @@ -27,6 +28,16 @@ const MENU_ITEM_TO_CHECK_FUNCTION_MAP: Record<
!options.feedItemFollow.isDisabled && options.feedItemFollow.isFollowing
);
},
[FeedItemMenuItem.MarkUnread]: ({ feedItemUserMetadata }) => {
const { count, seen } = feedItemUserMetadata || {};

return notEmpty(count) && notEmpty(seen) && count === 0 && seen;
},
[FeedItemMenuItem.MarkRead]: ({ feedItemUserMetadata }) => {
const { count, seen } = feedItemUserMetadata || {};

return Boolean(count) || !seen;
},
};

export const getAllowedItems = (
Expand All @@ -38,6 +49,8 @@ export const getAllowedItems = (
FeedItemMenuItem.Pin,
FeedItemMenuItem.Unpin,
FeedItemMenuItem.Share,
FeedItemMenuItem.MarkUnread,
FeedItemMenuItem.MarkRead,
FeedItemMenuItem.Report,
FeedItemMenuItem.Edit,
FeedItemMenuItem.Remove,
Expand Down
3 changes: 3 additions & 0 deletions src/pages/common/components/FeedCard/FeedCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type FeedCardProps = PropsWithChildren<{
type?: CommonFeedType;
menuItems?: ContextMenuItem[];
seenOnce?: boolean;
seen?: boolean;
ownerId?: string;
discussionPredefinedType?: PredefinedTypes;
hasFiles?: boolean;
Expand Down Expand Up @@ -77,6 +78,7 @@ export const FeedCard = forwardRef<FeedCardRef, FeedCardProps>((props, ref) => {
type,
menuItems,
seenOnce,
seen,
ownerId,
discussionPredefinedType,
hasImages,
Expand Down Expand Up @@ -197,6 +199,7 @@ export const FeedCard = forwardRef<FeedCardRef, FeedCardProps>((props, ref) => {
isFollowing,
type,
seenOnce,
seen,
ownerId,
discussionPredefinedType,
hasFiles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@
margin-left: 0.75rem;
color: $c-gray-800;
}

.unseen {
width: 1.4375rem;
border-radius: 50%;
background-color: $c-pink-primary;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import classNames from "classnames";
import { selectUser } from "@/pages/Auth/store/selectors";
import { PinIcon, StarIcon } from "@/shared/icons";
import { CommonFeedType } from "@/shared/models";
import { notEmpty } from "@/shared/utils/notEmpty";
import styles from "./FeedCardTags.module.scss";

interface FeedCardTagsProps {
unreadMessages?: number;
type?: CommonFeedType;
seenOnce?: boolean;
seen?: boolean;
ownerId?: string;
isActive: boolean;
isPinned?: boolean;
Expand All @@ -21,6 +23,7 @@ export const FeedCardTags: FC<FeedCardTagsProps> = (props) => {
unreadMessages,
type,
seenOnce,
seen,
ownerId,
isActive,
isPinned,
Expand Down Expand Up @@ -69,6 +72,9 @@ export const FeedCardTags: FC<FeedCardTagsProps> = (props) => {
{unreadMessages}
</div>
)}
{!unreadMessages && notEmpty(seen) && !seen && (
<div className={classNames(styles.tag, styles.unseen)}></div>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { FC, MouseEventHandler, useRef, useState } from "react";
import classNames from "classnames";
import { useLongPress } from "use-long-press";
import { PredefinedTypes } from "@/shared/models";
import {
checkIsTextEditorValueEmpty,
ContextMenu,
Expand Down Expand Up @@ -29,6 +28,7 @@ export const FeedItemBaseContent: FC<FeedItemBaseContentProps> = (props) => {
type,
menuItems,
seenOnce,
seen,
ownerId,
renderLeftContent,
isPinned,
Expand Down Expand Up @@ -149,6 +149,7 @@ export const FeedItemBaseContent: FC<FeedItemBaseContentProps> = (props) => {
unreadMessages={unreadMessages}
type={type}
seenOnce={seenOnce}
seen={seen}
ownerId={ownerId}
isActive={isActive}
isPinned={isPinned}
Expand Down
12 changes: 11 additions & 1 deletion src/pages/common/components/FeedItem/FeedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DiscussionFeedCard } from "../DiscussionFeedCard";
import { ProposalFeedCard } from "../ProposalFeedCard";
import { ProjectFeedItem } from "./components";
import { useFeedItemContext } from "./context";
import { useFeedItemCounters } from "./hooks";
import { FeedItemRef } from "./types";

interface FeedItemProps {
Expand Down Expand Up @@ -67,6 +68,8 @@ const FeedItem = forwardRef<FeedItemRef, FeedItemProps>((props, ref) => {
const { onFeedItemUpdate, getLastMessage, getNonAllowedItems, onUserSelect } =
useFeedItemContext();
useFeedItemSubscription(item.id, commonId, onFeedItemUpdate);
const { projectUnreadStreamsCount, projectUnreadMessages } =
useFeedItemCounters(item.id, commonId);

if (
shouldCheckItemVisibility &&
Expand Down Expand Up @@ -115,7 +118,14 @@ const FeedItem = forwardRef<FeedItemRef, FeedItemProps>((props, ref) => {
}

if (item.data.type === CommonFeedType.Project) {
return <ProjectFeedItem item={item} isMobileVersion={isMobileVersion} />;
return (
<ProjectFeedItem
item={item}
isMobileVersion={isMobileVersion}
unreadStreamsCount={projectUnreadStreamsCount}
unreadMessages={projectUnreadMessages}
/>
);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,21 @@ import { OpenIcon } from "@/shared/icons";
import { CommonFeed } from "@/shared/models";
import { CommonAvatar, parseStringToTextEditorValue } from "@/shared/ui-kit";
import { checkIsProject } from "@/shared/utils";
import { useFeedItemCounters } from "../../hooks";
import styles from "./ProjectFeedItem.module.scss";

interface ProjectFeedItemProps {
item: CommonFeed;
isMobileVersion: boolean;
unreadStreamsCount?: number;
unreadMessages?: number;
}

export const ProjectFeedItem: FC<ProjectFeedItemProps> = (props) => {
const { item, isMobileVersion } = props;
const { item, isMobileVersion, unreadStreamsCount, unreadMessages } = props;
const history = useHistory();
const { getCommonPagePath } = useRoutesContext();
const { renderFeedItemBaseContent } = useFeedItemContext();
const { data: common, fetched: isCommonFetched, fetchCommon } = useCommon();
const { unreadStreamsCount, unreadMessages } = useFeedItemCounters(
item.id,
common?.directParent?.commonId,
);
const commonId = item.data.id;
const lastMessage = parseStringToTextEditorValue(
Number.isInteger(unreadStreamsCount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ export enum FeedItemMenuItem {
Remove = "remove",
Follow = "follow",
Unfollow = "unfollow",
MarkUnread = "markUnread",
MarkRead = "markRead",
}
1 change: 1 addition & 0 deletions src/pages/common/components/FeedItem/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface FeedItemBaseContentProps {
menuItems?: ContextMenuItem[];
type?: CommonFeedType;
seenOnce?: boolean;
seen?: boolean;
ownerId?: string;
commonName?: string;
renderImage?: (className?: string) => ReactNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useCommonMember } from "@/pages/OldCommon/hooks";
import { useGovernanceByCommonId } from "@/shared/hooks/useCases";

interface Return {
unreadStreamsCount?: number;
unreadMessages?: number;
projectUnreadStreamsCount?: number;
projectUnreadMessages?: number;
}

export const useFeedItemCounters = (
Expand All @@ -28,7 +28,7 @@ export const useFeedItemCounters = (
}, [fetchGovernance, commonId]);

return {
unreadStreamsCount: streamsUnreadCountByProjectStream?.[feedItemId],
unreadMessages: unreadCountByProjectStream?.[feedItemId],
projectUnreadStreamsCount: streamsUnreadCountByProjectStream?.[feedItemId],
projectUnreadMessages: unreadCountByProjectStream?.[feedItemId],
};
};
2 changes: 2 additions & 0 deletions src/pages/common/components/FeedItem/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Discussion,
Proposal,
CommonFeedType,
CommonFeedObjectUserUnique,
} from "@/shared/models";
import { FeedItemMenuItem } from "./constants";

Expand All @@ -25,6 +26,7 @@ export interface GetAllowedItemsOptions {
commonMember?: CommonMember | null;
feedItemFollow: FeedItemFollowState;
getNonAllowedItems?: GetNonAllowedItemsOptions;
feedItemUserMetadata: CommonFeedObjectUserUnique | null;
}

export type MenuItemOptions = Omit<GetAllowedItemsOptions, "feedItemFollow">;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ const ProposalFeedCard = forwardRef<FeedItemRef, ProposalFeedCardProps>(
commonMember,
feedItemFollow,
getNonAllowedItems,
feedItemUserMetadata,
},
{
report: () => {},
Expand Down Expand Up @@ -426,6 +427,7 @@ const ProposalFeedCard = forwardRef<FeedItemRef, ProposalFeedCardProps>(
isLoading={isLoading}
type={item.data.type}
seenOnce={feedItemUserMetadata?.seenOnce}
seen={feedItemUserMetadata?.seen}
menuItems={menuItems}
ownerId={item.userId}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const FeedItemBaseContent: FC<FeedItemBaseContentProps> = (props) => {
type,
menuItems,
seenOnce,
seen,
ownerId,
commonName,
renderImage,
Expand Down Expand Up @@ -155,6 +156,7 @@ export const FeedItemBaseContent: FC<FeedItemBaseContentProps> = (props) => {
unreadMessages={unreadMessages}
type={type}
seenOnce={seenOnce}
seen={seen}
ownerId={ownerId}
isActive={isActive}
isPinned={false}
Expand Down
10 changes: 10 additions & 0 deletions src/services/CommonFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ class CommonFeedService {
return convertObjectDatesToFirestoreTimestamps(data);
};

public markCommonFeedItemAsUnseen = (
commonId: string,
feedObjectId: string,
) => {
return Api.post(ApiEndpoint.MarkFeedObjectUnseenForUser, {
commonId,
feedObjectId,
});
};

public subscribeToCommonFeedItem = (
commonId: string,
feedItemId: string,
Expand Down
1 change: 1 addition & 0 deletions src/shared/constants/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const ApiEndpoint = {
UpdateCommon: "/commons/update",
CreateSubCommon: "/commons/subcommon/create",
MarkFeedObjectSeenForUser: "/commons/mark-feed-object-seen-for-user",
MarkFeedObjectUnseenForUser: "/commons/mark-feed-object-unseen-for-user",
AcceptRules: "/commons/accept-rules",
GetCommonFeedItems: "/commons/:commonId/feed-items",
GetCommonPinnedFeedItems: "/commons/:commonId/pinned-feed-items",
Expand Down
1 change: 1 addition & 0 deletions src/shared/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export { default as UploadIcon } from "./upload.icon";
export { default as WalletIcon } from "./wallet.icon";
export { default as MessageIcon } from "./message.icon";
export { default as Message2Icon } from "./message2.icon";
export { default as Message3Icon } from "./message3.icon";
export { default as Trash2Icon } from "./trash2.icon";
export { UnfollowIcon } from "./unfollow.icon";
export { UnpinIcon } from "./unpin.icon";
Expand Down
Loading

0 comments on commit dde1cf6

Please sign in to comment.