diff --git a/packages/web/components/elements/StyledText.tsx b/packages/web/components/elements/StyledText.tsx index 50ad458fa..6c2fd86cb 100644 --- a/packages/web/components/elements/StyledText.tsx +++ b/packages/web/components/elements/StyledText.tsx @@ -39,11 +39,10 @@ const textVariants = { }, settingsSection: { fontWeight: '600', - fontSize: '17px', + fontSize: '22px', fontFamily: '$inter', color: '$grayText', m: '0px', - my: '15px', marginBlockStart: '0px', marginBlockEnd: '0px', }, diff --git a/packages/web/lib/navigations.ts b/packages/web/lib/navigations.ts index 97d275d17..5953e9b9b 100644 --- a/packages/web/lib/navigations.ts +++ b/packages/web/lib/navigations.ts @@ -1 +1 @@ -export const DEFAULT_HOME_PATH = `/l/home` +export const DEFAULT_HOME_PATH = `/home` diff --git a/packages/web/pages/settings/shortcuts.tsx b/packages/web/pages/settings/shortcuts.tsx index 06f7a6d96..da3dde540 100644 --- a/packages/web/pages/settings/shortcuts.tsx +++ b/packages/web/pages/settings/shortcuts.tsx @@ -31,7 +31,7 @@ import { DragIcon } from '../../components/elements/icons/DragIcon' import { CoverImage } from '../../components/elements/CoverImage' import { Label } from '../../lib/networking/fragments/labelFragment' import { usePersistedState } from '../../lib/hooks/usePersistedState' -import { CheckSquare, Square } from '@phosphor-icons/react' +import { CheckSquare, Square, Tag } from '@phosphor-icons/react' import { Button } from '../../components/elements/Button' import { styled } from '@stitches/react' import { SavedSearch } from '../../lib/networking/fragments/savedSearchFragment' @@ -42,7 +42,13 @@ import { Shortcut, useGetShortcuts, useResetShortcuts, + useSetShortcuts, } from '../../lib/networking/shortcuts/useShortcuts' +import * as Switch from '@radix-ui/react-switch' +import { useGetSubscriptions } from '../../lib/networking/subscriptions/useGetSubscriptions' +import { NewsletterIcon } from '../../components/elements/icons/NewsletterIcon' +import { FollowingIcon } from '../../components/elements/icons/FollowingIcon' +import { LIBRARY_LEFT_MENU_WIDTH } from '../../components/templates/navMenu/NavigationMenu' function flattenShortcuts(shortcuts: Shortcut[]): string[] { let result: string[] = [] @@ -59,20 +65,106 @@ function flattenShortcuts(shortcuts: Shortcut[]): string[] { return result } +function removeShortcutById( + shortcuts: Shortcut[] | undefined, + targetId: string +): Shortcut[] | undefined { + if (!shortcuts) { + return undefined + } + return shortcuts + .filter((shortcut) => shortcut.id !== targetId) + .map((shortcut) => ({ + ...shortcut, + children: removeShortcutById(shortcut.children, targetId), + })) +} + +type ListAction = 'RESET' | 'ADD_ITEM' | 'REMOVE_ITEM' + export default function Shortcuts(): JSX.Element { - const { data: shortcuts, isLoading } = useGetShortcuts() + const { data, isLoading } = useGetShortcuts() + const setShortcuts = useSetShortcuts() + + const listReducer = ( + state: { state: string; items: Shortcut[] }, + action: { + type: ListAction + item?: Shortcut + items?: Shortcut[] + } + ) => { + switch (action.type) { + case 'RESET': { + return { state: 'CURRENT', items: action.items ?? [] } + } + case 'ADD_ITEM': { + const item = action.item + if (!item) { + return state + } + const existing = state.items.find( + (existing) => existing.type == item.type && existing.id == item.id + ) + if (existing) { + return state + } + state.items.push(item) + setShortcuts.mutate({ + shortcuts: [...state.items], + }) + return { state: 'CURRENT', items: [...state.items] } + } + case 'REMOVE_ITEM': { + const item = action.item + console.log('removing item: ', item) + if (!item) { + return state + } + const updated = removeShortcutById(state.items, item.id) ?? [] + setShortcuts.mutate({ + shortcuts: [...updated], + }) + return { state: 'CURRENT', items: [...updated] } + } + default: + throw new Error('unknown action') + } + } + + const [shortcuts, dispatchList] = useReducer(listReducer, { + state: 'INITIAL', + items: [], + }) + const shortcutIds = useMemo(() => { - if (shortcuts) { - return flattenShortcuts(shortcuts) + if (shortcuts.items) { + return flattenShortcuts(shortcuts.items) } return [] - }, [shortcuts]) + }, [shortcuts.items]) - console.log('shortcutIds: ', shortcutIds) + useEffect(() => { + if (!isLoading) { + console.log('data: ', data) + dispatchList({ type: 'RESET', items: data }) + } + }, [data]) return ( - + + + + + ) } @@ -85,187 +177,7 @@ export const SectionSeparator = styled(Separator, { type ListProps = { shortcutIds: string[] - // dispatchList: (arg: { type: ListAction; item?: Shortcut | undefined }) => void -} - -const AvailableItems = (props: ListProps): JSX.Element => { - const { data: labels } = useGetLabels() - const { data: savedSearches } = useGetSavedSearches() - const { subscriptions } = useGetSubscriptionsQuery() - - const sortedLabels = useMemo(() => { - if (!labels) { - return [] - } - return labels.sort((a, b) => - a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()) - ) - }, [labels]) - - const sortedSubscriptions = useMemo(() => { - if (!subscriptions) { - return [] - } - return subscriptions.sort((a, b) => - a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()) - ) - }, [subscriptions]) - - const sortedsavedSearches = useMemo(() => { - if (!savedSearches) { - return [] - } - return savedSearches.sort((a, b) => - a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()) - ) - }, [savedSearches]) - - const searchSelected = useCallback( - (search: SavedSearch) => { - return !!props.shortcutIds.find((shortcutId) => shortcutId == search.id) - }, - [props] - ) - - const labelSelected = useCallback( - (label: Label) => { - return !!props.shortcutIds.find((shortcutId) => shortcutId == label.id) - }, - [props] - ) - - const subscriptionSelected = useCallback( - (subscription: Subscription) => { - return !!props.shortcutIds.find( - (shortcutId) => shortcutId == subscription.id - ) - }, - [props] - ) - - console.log('sortedsavedSearchesL: ', sortedsavedSearches) - - return ( - - {/* Available items */} - - - {/* - Labels - {sortedLabels.map((label) => { - console.log('label: ', label) - return ( - - ) - })} - - - Subscriptions - {sortedSubscriptions.map((subscription) => { - console.log('subscription: ', subscription) - return ( - - ) - })} */} - - ) + dispatchList: (arg: { type: ListAction; item?: Shortcut | undefined }) => void } const SavedSearches = (props: ListProps) => { @@ -275,23 +187,266 @@ const SavedSearches = (props: ListProps) => { if (!savedSearches) { return [] } - return savedSearches.sort((a, b) => - a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()) + return ( + savedSearches + // .filter((search) => props.shortcutIds.indexOf(search.id) === -1) + .sort((a, b) => + a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()) + ) ) - }, [savedSearches]) + }, [props, savedSearches]) + + const isChecked = useCallback( + (shortcutId: string) => { + return props.shortcutIds.indexOf(shortcutId) !== -1 + }, + [props.shortcutIds] + ) + return ( - <> - {/* Saved Searches */} - {/* */} - {!isLoading && - (savedSearches ?? []).map((search) => { - return ( - - {search.name} - - ) - })} - {/* */} - + + Saved Searches + + {!isLoading && + (sortedsavedSearches ?? []).map((search) => { + return ( + + {search.name} + + { + if (checked) { + props.dispatchList({ + type: 'ADD_ITEM', + item: { + id: search.id, + type: 'search', + name: search.name, + section: 'library', + filter: search.filter, + }, + }) + } else { + props.dispatchList({ + type: 'REMOVE_ITEM', + item: { + type: 'search', + section: 'library', + ...search, + }, + }) + } + }} + /> + + + ) + })} + + ) } + +const Subscriptions = (props: ListProps) => { + const { data: subscriptions, isLoading } = useGetSubscriptions({}) + + const sortedSubscriptions = useMemo(() => { + if (!subscriptions) { + return [] + } + return subscriptions.sort((a, b) => + a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()) + ) + }, [props, subscriptions]) + + const isChecked = useCallback( + (shortcutId: string) => { + return props.shortcutIds.indexOf(shortcutId) !== -1 + }, + [props.shortcutIds] + ) + + return ( + + Subscriptions + + {!isLoading && + (sortedSubscriptions ?? []).map((subscription) => { + return ( + + {subscription.icon ? ( + + ) : subscription.type == SubscriptionType.NEWSLETTER ? ( + + ) : ( + + )} + {subscription.name} + + { + const item: Shortcut = { + id: subscription.id, + section: 'subscriptions', + name: subscription.name, + icon: subscription.icon, + type: + subscription.type == SubscriptionType.NEWSLETTER + ? 'newsletter' + : 'feed', + filter: + subscription.type == SubscriptionType.NEWSLETTER + ? `subscription:\"${escapeQuotes( + subscription.name + )}\"` + : `rss:\"${subscription.url}\"`, + } + if (checked) { + props.dispatchList({ + type: 'ADD_ITEM', + item, + }) + } else { + props.dispatchList({ + type: 'REMOVE_ITEM', + item, + }) + } + }} + /> + + + ) + })} + + + ) +} + +const Labels = (props: ListProps) => { + const { data: labels, isLoading } = useGetLabels() + + const sortedLabels = useMemo(() => { + if (!labels) { + return [] + } + return labels.sort((a, b) => + a.name.toLocaleLowerCase().localeCompare(b.name.toLocaleLowerCase()) + ) + }, [props, labels]) + + const isChecked = useCallback( + (shortcutId: string) => { + return props.shortcutIds.indexOf(shortcutId) !== -1 + }, + [props.shortcutIds] + ) + + return ( + + Labels + + {!isLoading && + (sortedLabels ?? []).map((label) => { + return ( + + + + {label.name} + + + { + const item: Shortcut = { + id: label.id, + type: 'label', + label: label, + section: 'library', + name: label.name, + filter: `label:\"${escapeQuotes(label.name)}\"`, + } + if (checked) { + props.dispatchList({ + type: 'ADD_ITEM', + item, + }) + } else { + props.dispatchList({ + type: 'REMOVE_ITEM', + item, + }) + } + }} + /> + + + ) + })} + + + ) +} + +type SwitchBoxProps = { + checked: boolean + setChecked: (checked: boolean) => void +} +const SwitchBox = (props: SwitchBoxProps) => { + return ( + { + props.setChecked(checked) + }} + > + + + ) +} + +const SwitchRoot = styled(Switch.Root, { + all: 'unset', + width: 42, + height: 25, + backgroundColor: '$thBorderColor', + borderRadius: '9999px', + position: 'relative', + WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)', + '&:focus': { boxShadow: `0 0 0 2px $thBorderColor` }, + '&[data-state="checked"]': { backgroundColor: '#4BB543' }, +}) + +const SwitchThumb = styled(Switch.Thumb, { + display: 'block', + width: 21, + height: 21, + backgroundColor: '$thTextContrast2', + borderRadius: '9999px', + transition: 'transform 100ms', + transform: 'translateX(2px)', + willChange: 'transform', + '&[data-state="checked"]': { transform: 'translateX(19px)' }, +})