Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom feed plus #3963

Merged
merged 51 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
f31d555
feat: remove coming soon for custom feed modal
capJavert Dec 11, 2024
ded4232
Merge branch 'main' into AS-814-custom-feeds-plus
capJavert Dec 12, 2024
2effdd7
Merge branch 'main' into AS-814-custom-feeds-plus
rebelchris Dec 13, 2024
4adf356
feat: feed settings modal (#3971)
capJavert Dec 13, 2024
6b6dff4
fix: add truncate on long names (#3973)
rebelchris Dec 13, 2024
e685899
feat: feed settings general (#3972)
capJavert Dec 16, 2024
8246012
feat: feed settings filters (#3983)
capJavert Dec 17, 2024
ba40907
feat: feed settings tags (#3974)
capJavert Dec 17, 2024
cbd8825
feat: add type for feed
capJavert Dec 17, 2024
07f23b4
feat: create feed popup (#3984)
capJavert Dec 17, 2024
f541906
feat: content source section (#3988)
rebelchris Dec 17, 2024
4a6d2f6
feat: start of blocked content (#3989)
rebelchris Dec 18, 2024
148000b
feat: edit my feed in new UI (#3990)
capJavert Dec 18, 2024
eba228b
refactor: again..
capJavert Dec 18, 2024
2fe2772
chore: blocked doesn't need userid (#3991)
rebelchris Dec 18, 2024
3f1b6d7
feat: tabs naming and dview fix
capJavert Dec 18, 2024
e25c6fd
feat: search tags adjustments and auto save
capJavert Dec 18, 2024
05b37c3
feat: adjust header and save button
capJavert Dec 18, 2024
006a367
feat: entry point in settings sidebar
capJavert Dec 18, 2024
20451c5
feat: setup empty feed after create
capJavert Dec 18, 2024
36b013e
fix: the placeholders (#3993)
rebelchris Dec 19, 2024
32a72c7
fix: user follow fixes (#3995)
rebelchris Dec 19, 2024
13f6fd1
feat: Reusable 3 dots for adding to custom feed (#3996)
AmarTrebinjac Dec 19, 2024
8b31074
feat: Show errors on content thresholds per guideline (#4004)
AmarTrebinjac Dec 19, 2024
f210b0a
feat: display custom feed as default when set (#3985)
AmarTrebinjac Dec 19, 2024
86538fc
fix: ensure blocked words work per feed (#4001)
rebelchris Dec 19, 2024
2a82124
feat: Add to custom feed modal (#4003)
AmarTrebinjac Dec 19, 2024
c182a70
fix: context optional in components
capJavert Dec 19, 2024
cc0e12a
fix: invalidate on search (#4005)
rebelchris Dec 19, 2024
c479fbc
feat: block custom feed view from date
capJavert Dec 19, 2024
11b277c
feat: Post options menu based on context (#4007)
AmarTrebinjac Dec 19, 2024
488204e
feat: load custom feed under edit page instead of preview
capJavert Dec 20, 2024
549d9ac
feat: lazy load sections
capJavert Dec 20, 2024
5451255
feat: load main feed under create page
capJavert Dec 20, 2024
33cbe2a
fix: build
capJavert Dec 20, 2024
96564b9
fix: default selection in extension
capJavert Dec 20, 2024
8053dc3
Merge branch 'main' into AS-814-custom-feeds-plus
capJavert Dec 20, 2024
5074710
fix: make everything uniform (#4009)
rebelchris Dec 20, 2024
6efca6c
fix: preferences mobile wrap
capJavert Dec 20, 2024
ccd1251
feat: adjust feedId to feed_id to match other events
capJavert Dec 20, 2024
b85483a
fix: truncating via JS to avoid inline issues
rebelchris Dec 20, 2024
5004efa
fix: bump test case fix again
rebelchris Dec 20, 2024
00f0b1c
fix: show buttons on edit form
rebelchris Dec 20, 2024
b0943ea
feat: custom feeds ready feature plus
capJavert Dec 20, 2024
f249730
fix: remove if not enrolled (#4010)
rebelchris Dec 20, 2024
b1ad3c4
fix: mobile modal list fixes (#4011)
rebelchris Dec 20, 2024
d5d1463
feat: go back after adding to custom feed
capJavert Dec 20, 2024
02f24f4
Merge branch 'main' into AS-814-custom-feeds-plus
capJavert Dec 20, 2024
b83d39d
fix: update copy
rebelchris Dec 20, 2024
b78370b
feat: block on after downvote on custom feed
capJavert Dec 20, 2024
e4e424f
feat: no going back now
capJavert Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/extension/src/newtab/MainFeedPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext';
import { useFeedLayout } from '@dailydotdev/shared/src/hooks';
import { useDndContext } from '@dailydotdev/shared/src/contexts/DndContext';
import { FeedLayoutProvider } from '@dailydotdev/shared/src/contexts/FeedContext';
import useCustomDefaultFeed from '@dailydotdev/shared/src/hooks/feed/useCustomDefaultFeed';
import ShortcutLinks from './ShortcutLinks/ShortcutLinks';
import DndBanner from './DndBanner';
import { CompanionPopupButton } from '../companion/CompanionPopupButton';
Expand Down Expand Up @@ -51,6 +52,7 @@ export default function MainFeedPage({
const { shouldUseListFeedLayout } = useFeedLayout({ feedRelated: false });
useCompanionSettings();
const { isActive: isDndActive, showDnd, setShowDnd } = useDndContext();
const { isCustomDefaultFeed } = useCustomDefaultFeed();

const onNavTabClick = useCallback(
(tab: string): void => {
Expand All @@ -73,6 +75,11 @@ export default function MainFeedPage({
return '/search';
}

// default page when user selected custom default feed
if (isCustomDefaultFeed && feedName === 'default') {
return '/';
}

const feed = getFeedName(feedName, {
hasUser: !!user,
hasFiltered: !alerts?.filter,
Expand Down
41 changes: 41 additions & 0 deletions packages/shared/src/components/CustomFeedEmptyScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { ReactElement } from 'react';
import { useRouter } from 'next/router';
import {
EmptyScreenButton,
EmptyScreenContainer,
EmptyScreenDescription,
EmptyScreenIcon,
EmptyScreenTitle,
} from './EmptyScreen';
import { HashtagIcon } from './icons';
import { PageContainer } from './utilities';
import { ButtonSize } from './buttons/common';
import { webappUrl } from '../lib/constants';

export const CustomFeedEmptyScreen = (): ReactElement => {
const router = useRouter();

return (
<PageContainer className="mx-auto">
<EmptyScreenContainer>
<HashtagIcon
className={EmptyScreenIcon.className}
style={EmptyScreenIcon.style}
/>
<EmptyScreenTitle>Let&apos;s set up your feed!</EmptyScreenTitle>
<EmptyScreenDescription>
Start by configuring your feed settings to tailor content to your
interests. Add tags, filters, and sources to make it truly yours.
</EmptyScreenDescription>
<EmptyScreenButton
onClick={() => {
router.push(`${webappUrl}feeds/${router.query.slugOrId}/edit`);
}}
size={ButtonSize.Large}
>
Set up feed
</EmptyScreenButton>
</EmptyScreenContainer>
</PageContainer>
);
};
103 changes: 103 additions & 0 deletions packages/shared/src/components/CustomFeedOptionsMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { type ReactElement } from 'react';
import classNames from 'classnames';
import { ContextMenuIds } from '../hooks/constants';
import type { MenuItemProps } from './fields/ContextMenu';
import ContextMenu from './fields/ContextMenu';
import { HashtagIcon, MenuIcon as DotsIcon, ShareIcon } from './icons';
import { MenuIcon } from './MenuIcon';
import useContextMenu from '../hooks/useContextMenu';
import { Button, ButtonSize, ButtonVariant } from './buttons/Button';
import {
useShareOrCopyLink,
type UseShareOrCopyLinkProps,
} from '../hooks/useShareOrCopyLink';
import { useFeeds, usePlusSubscription } from '../hooks';
import { LazyModal } from './modals/common/types';
import { useLazyModal } from '../hooks/useLazyModal';

type CustomFeedOptionsMenuProps = {
onCreateNewFeed?: () => void;
onAdd: (feedId: string) => void;
onUndo?: (feedId: string) => void;
className?: string;
shareProps: UseShareOrCopyLinkProps;
};

const CustomFeedOptionsMenu = ({
className,
shareProps,
onAdd,
onUndo,
onCreateNewFeed,
}: CustomFeedOptionsMenuProps): ReactElement => {
const { showPlusSubscription, isPlus } = usePlusSubscription();
const { openModal } = useLazyModal();
const [, onShareOrCopyLink] = useShareOrCopyLink(shareProps);
const { isOpen, onMenuClick } = useContextMenu({
id: ContextMenuIds.CustomFeedContext,
});
const { feeds } = useFeeds();

const handleOpenModal = () => {
if (!isPlus) {
return openModal({
type: LazyModal.AdvancedCustomFeedSoon,
});
}
if (feeds?.edges?.length > 0) {
return openModal({
type: LazyModal.AddToCustomFeed,
props: {
onAdd,
onUndo,
onCreateNewFeed,
},
});
}
return onCreateNewFeed();
};

const options: MenuItemProps[] = [
{
icon: <MenuIcon Icon={ShareIcon} />,
label: 'Share',
action: () => onShareOrCopyLink(),
},
{
icon: <MenuIcon Icon={HashtagIcon} />,
label: 'Add to custom feed',
action: handleOpenModal,
},
];

if (!showPlusSubscription) {
return (
<Button
variant={ButtonVariant.Float}
size={ButtonSize.Small}
icon={<ShareIcon />}
onClick={() => onShareOrCopyLink()}
/>
);
}

return (
<>
<Button
className={classNames('!px-1.5', className)}
onClick={onMenuClick}
size={ButtonSize.Small}
variant={ButtonVariant.Float}
icon={<DotsIcon />}
/>
<ContextMenu
disableBoundariesCheck
id={ContextMenuIds.CustomFeedContext}
options={options}
isOpen={isOpen}
/>
</>
);
};

export default CustomFeedOptionsMenu;
16 changes: 4 additions & 12 deletions packages/shared/src/components/FeedEmptyScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ import {
EmptyScreenTitle,
} from './EmptyScreen';
import { FilterIcon } from './icons';
import { PageContainer, SharedFeedPage } from './utilities';
import { PageContainer } from './utilities';
import { ButtonSize } from './buttons/common';
import { useLazyModal } from '../hooks/useLazyModal';
import { LazyModal } from './modals/common/types';
import { getFeedName } from '../lib/feed';
import { webappUrl } from '../lib/constants';
import { useAuthContext } from '../contexts/AuthContext';

function FeedEmptyScreen(): ReactElement {
const { openModal } = useLazyModal();
const router = useRouter();
const { user } = useAuthContext();

return (
<PageContainer className="mx-auto">
Expand All @@ -33,13 +31,7 @@ function FeedEmptyScreen(): ReactElement {
</EmptyScreenDescription>
<EmptyScreenButton
onClick={() => {
const feedName = getFeedName(router.pathname);

if (feedName === SharedFeedPage.Custom && router.query?.slugOrId) {
router.replace(`${webappUrl}feeds/${router.query.slugOrId}/edit`);
} else {
openModal({ type: LazyModal.FeedFilters });
}
router.push(`${webappUrl}feeds/${user.id}/edit`);
}}
size={ButtonSize.Large}
>
Expand Down
50 changes: 46 additions & 4 deletions packages/shared/src/components/MainFeedLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
FOLLOWING_FEED_QUERY,
MOST_DISCUSSED_FEED_QUERY,
MOST_UPVOTED_FEED_QUERY,
PREVIEW_FEED_QUERY,
SEARCH_POSTS_QUERY,
} from '../graphql/feed';
import { generateQueryKey, OtherFeedPage, RequestKey } from '../lib/query';
Expand Down Expand Up @@ -55,6 +54,7 @@ import { Origin } from '../lib/log';
import { ExploreTabs, tabToUrl, urlToTab } from './header';
import { QueryStateKeys, useQueryState } from '../hooks/utils/useQueryState';
import { useSearchResultsLayout } from '../hooks/search/useSearchResultsLayout';
import useCustomDefaultFeed from '../hooks/feed/useCustomDefaultFeed';

const FeedExploreHeader = dynamic(
() =>
Expand All @@ -81,6 +81,12 @@ const FollowingFeedEmptyScreen = dynamic(
),
);

const CustomFeedEmptyScreen = dynamic(() =>
import(
/* webpackChunkName: "customFeedEmptyScreen" */ './CustomFeedEmptyScreen'
).then((mod) => mod.CustomFeedEmptyScreen),
);

type FeedQueryProps = {
query: string;
queryIfLogged?: string;
Expand Down Expand Up @@ -119,9 +125,11 @@ const propsByFeed: Record<SharedFeedPage & OtherFeedPage, FeedQueryProps> = {
},
[SharedFeedPage.Custom]: {
query: CUSTOM_FEED_QUERY,
emptyScreen: <CustomFeedEmptyScreen />,
},
[SharedFeedPage.CustomForm]: {
query: PREVIEW_FEED_QUERY,
query: CUSTOM_FEED_QUERY,
emptyScreen: <CustomFeedEmptyScreen />,
},
[OtherFeedPage.Following]: {
query: FOLLOWING_FEED_QUERY,
Expand Down Expand Up @@ -190,6 +198,7 @@ export default function MainFeedLayout({
hasFiltered: !alerts?.filter,
hasUser: !!user,
});
const { isCustomDefaultFeed, defaultFeedId } = useCustomDefaultFeed();
const isLaptop = useViewSize(ViewSize.Laptop);
const feedVersion = useFeature(feature.feedVersion);
const {
Expand Down Expand Up @@ -224,14 +233,21 @@ export default function MainFeedLayout({
feedId: router.query?.slugOrId as string,
},
},
[SharedFeedPage.CustomForm]: {
queryIfLogged: FEED_QUERY,
variables: {
feedId: (router.query?.slugOrId as string) || user?.id,
},
},
};

return {
query: getQueryBasedOnLogin(
tokenRefreshed,
user,
propsByFeed[feedName].query,
propsByFeed[feedName].queryIfLogged,
dynamicPropsByFeed[feedName]?.query || propsByFeed[feedName].query,
dynamicPropsByFeed[feedName]?.queryIfLogged ||
propsByFeed[feedName].queryIfLogged,
),
variables: {
...propsByFeed[feedName].variables,
Expand Down Expand Up @@ -272,6 +288,29 @@ export default function MainFeedLayout({
return null;
}

if (feedNameProp === 'default' && isCustomDefaultFeed) {
return {
feedName: SharedFeedPage.Custom,
feedQueryKey: generateQueryKey(
SharedFeedPage.Custom,
user,
defaultFeedId,
),
query: CUSTOM_FEED_QUERY,
variables: {
feedId: user.defaultFeedId,
feedName: SharedFeedPage.Custom,
},
emptyScreen: propsByFeed[feedName].emptyScreen || <FeedEmptyScreen />,
actionButtons: feedWithActions && (
<SearchControlHeader
algoState={[selectedAlgo, setSelectedAlgo]}
feedName={feedName}
/>
),
};
}

if (isSearchOn && searchQuery) {
const searchVersion = getFeatureValue(feature.searchVersion);
return {
Expand Down Expand Up @@ -362,6 +401,9 @@ export default function MainFeedLayout({
selectedPeriod,
setSelectedAlgo,
router.pathname,
isCustomDefaultFeed,
feedNameProp,
defaultFeedId,
]);

useEffect(() => {
Expand Down
27 changes: 21 additions & 6 deletions packages/shared/src/components/PostOptionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,28 @@ export default function PostOptionsMenu({
const { post: loadedPost } = usePostById({
id: initialPost?.id,
});
const feedContextData = useActiveFeedContext();
const { queryKey: feedQueryKey, logOpts } = feedContextData;
const isCustomFeed = feedQueryKey?.[0] === 'custom';
const customFeedId = isCustomFeed ? (feedQueryKey?.[2] as string) : undefined;
const post = loadedPost ?? initialPost;
const { showPlusSubscription, isPlus } = usePlusSubscription();
const { feedSettings, advancedSettings, checkSettingsEnabledState } =
useFeedSettings({ enabled: isPostOptionsOpen });
const { onUpdateSettings } = useAdvancedSettings({ enabled: false });
useFeedSettings({
enabled: isPostOptionsOpen,
feedId: customFeedId,
});
const { onUpdateSettings } = useAdvancedSettings({
enabled: false,
feedId: customFeedId,
});
const { logEvent } = useContext(LogContext);
const { hidePost, unhidePost } = useReportPost();
const { openSharePost } = useSharePost(origin);
const { follow, unfollow } = useContentPreference();

const { openModal } = useLazyModal();
const feedContextData = useActiveFeedContext();
const { queryKey: feedQueryKey, logOpts } = feedContextData;

const {
onBlockSource,
onBlockTags,
Expand All @@ -137,16 +146,19 @@ export default function PostOptionsMenu({
origin: Origin.PostContextMenu,
postId: post?.id,
shouldInvalidateQueries: false,
feedId: customFeedId,
});

const isSourceBlocked = useMemo(() => {
return !!feedSettings?.excludeSources?.some(
(excludedSource) => excludedSource.id === post?.source?.id,
);
}, [feedSettings?.excludeSources, post?.source?.id]);

const shouldShowSubscribe =
isLoggedIn && !isSourceBlocked && post?.source?.type === SourceType.Machine;
isLoggedIn &&
!isSourceBlocked &&
post?.source?.type === SourceType.Machine &&
!isCustomFeed;

const sourceSubscribe = useSourceActionsNotify({
source: shouldShowSubscribe ? post?.source : undefined,
Expand Down Expand Up @@ -487,6 +499,9 @@ export default function PostOptionsMenu({
post?.tags?.forEach((tag) => {
if (tag.length) {
const isBlocked = feedSettings?.blockedTags?.includes(tag);
if (isBlocked && isCustomFeed) {
return;
}
postOptions.push({
icon: <MenuIcon Icon={isBlocked ? PlusIcon : BlockIcon} />,
label: isBlocked ? `Follow #${tag}` : `Not interested in #${tag}`,
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/SharedByUserBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const SharedByUserBanner = ({
</Typography>
<FollowButton
className="ml-auto"
userId={userid}
entityId={userid}
type={ContentPreferenceType.User}
entityName={`@${user.username}`}
status={contentPreference?.status}
Expand Down
Loading
Loading