From 885101710008e1382feca187db15c74d404edbaa Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 15:54:07 +0800 Subject: [PATCH 01/10] Use andWhere query to not overwrite the where --- packages/api/src/resolvers/subscriptions/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index 95fc2abc0..116152ff2 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -78,7 +78,7 @@ export const subscriptionsResolver = authorized< // Show all RSS, but only active newsletters queryBuilder - .where({ + .andWhere({ type: SubscriptionType.Newsletter, status: SubscriptionStatus.Active, }) From 32b52efba78cd730fdab61b93430631d169ffb0c Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 15:55:01 +0800 Subject: [PATCH 02/10] Improve the toggle buttons on the library menu --- .../elements/icons/ToggleCaretDownIcon.tsx | 32 +++ .../elements/icons/ToggleCaretLeftIcon.tsx | 32 +++ .../templates/homeFeed/LibraryFilterMenu.tsx | 212 +++++++++--------- 3 files changed, 169 insertions(+), 107 deletions(-) create mode 100644 packages/web/components/elements/icons/ToggleCaretDownIcon.tsx create mode 100644 packages/web/components/elements/icons/ToggleCaretLeftIcon.tsx diff --git a/packages/web/components/elements/icons/ToggleCaretDownIcon.tsx b/packages/web/components/elements/icons/ToggleCaretDownIcon.tsx new file mode 100644 index 000000000..946ea3a86 --- /dev/null +++ b/packages/web/components/elements/icons/ToggleCaretDownIcon.tsx @@ -0,0 +1,32 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class ToggleCaretDownIcon extends React.Component { + render() { + const size = (this.props.size || 26).toString() + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + ) + } +} diff --git a/packages/web/components/elements/icons/ToggleCaretLeftIcon.tsx b/packages/web/components/elements/icons/ToggleCaretLeftIcon.tsx new file mode 100644 index 000000000..85796ecab --- /dev/null +++ b/packages/web/components/elements/icons/ToggleCaretLeftIcon.tsx @@ -0,0 +1,32 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class ToggleCaretLeftIcon extends React.Component { + render() { + const size = (this.props.size || 26).toString() + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + ) + } +} diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index 79fb71724..986ba3736 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -1,9 +1,8 @@ -import { ReactNode, useMemo, useState } from 'react' +import { ReactNode, useMemo } from 'react' import { StyledText } from '../../elements/StyledText' import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives' -import { Dropdown, DropdownOption } from '../../elements/DropdownElements' import { Button } from '../../elements/Button' -import { CaretRight, Circle, DotsThree, Plus } from 'phosphor-react' +import { CaretRight, Circle } from 'phosphor-react' import { useGetSubscriptionsQuery } from '../../../lib/networking/queries/useGetSubscriptionsQuery' import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery' import { Label } from '../../../lib/networking/fragments/labelFragment' @@ -11,6 +10,10 @@ import { theme } from '../../tokens/stitches.config' import { useRegisterActions } from 'kbar' import { LogoBox } from '../../elements/LogoBox' import { usePersistedState } from '../../../lib/hooks/usePersistedState' +import { ToggleCaretDownIcon } from '../../elements/icons/ToggleCaretDownIcon' +import { ToggleCaretLeftIcon } from '../../elements/icons/ToggleCaretLeftIcon' +import Link from 'next/link' +import { ArrowRightIcon } from '../../elements/icons/ArrowRightIcon' export const LIBRARY_LEFT_MENU_WIDTH = '233px' @@ -130,16 +133,26 @@ function SavedSearches(props: LibraryFilterMenuProps): JSX.Element { [] ) + const [collapsed, setCollapsed] = usePersistedState({ + key: `--saved-searches-collapsed`, + initialValue: false, + }) + return ( - - {items.map((item) => ( - - ))} + + {!collapsed && + items.map((item) => ( + + ))} @@ -148,8 +161,8 @@ function SavedSearches(props: LibraryFilterMenuProps): JSX.Element { function Subscriptions(props: LibraryFilterMenuProps): JSX.Element { const { subscriptions } = useGetSubscriptionsQuery() - const [viewAll, setViewAll] = usePersistedState({ - key: `--subscriptions-view-all`, + const [collapsed, setCollapsed] = usePersistedState({ + key: `--subscriptions-collapsed`, initialValue: false, }) @@ -173,15 +186,10 @@ function Subscriptions(props: LibraryFilterMenuProps): JSX.Element { return ( { - window.location.href = '/settings/subscriptions' - }} - viewAll={() => { - setViewAll(true) - }} + collapsed={collapsed} + setCollapsed={setCollapsed} > - {viewAll ? ( + {!collapsed ? ( <> ) })} - + ) : ( @@ -210,8 +221,8 @@ function Subscriptions(props: LibraryFilterMenuProps): JSX.Element { function Labels(props: LibraryFilterMenuProps): JSX.Element { const { labels } = useGetLabelsQuery() - const [viewAll, setViewAll] = usePersistedState({ - key: `--labels-view-all`, + const [collapsed, setCollapsed] = usePersistedState({ + key: `--labels-collapsed`, initialValue: false, }) @@ -224,16 +235,18 @@ function Labels(props: LibraryFilterMenuProps): JSX.Element { return ( { - window.location.href = '/settings/labels' - }} + collapsed={collapsed} + setCollapsed={setCollapsed} > - {sortedLabels.slice(0, viewAll ? undefined : 4).map((item) => { - return - })} - + {!collapsed && ( + <> + {sortedLabels.map((item) => { + return + })} + + + )} ) } @@ -241,10 +254,11 @@ function Labels(props: LibraryFilterMenuProps): JSX.Element { type MenuPanelProps = { title: string children: ReactNode - editFunc?: () => void - editTitle?: string + hideBottomBorder?: boolean - viewAll?: () => void + + collapsed: boolean + setCollapsed: (collapsed: boolean) => void } function MenuPanel(props: MenuPanelProps): JSX.Element { @@ -278,57 +292,32 @@ function MenuPanel(props: MenuPanelProps): JSX.Element { - {props.editTitle && props.editFunc && ( - - - - } - > - {props.viewAll && ( - { - if (props.viewAll) { - props.viewAll() - } - }} - /> - )} - { - if (props.editFunc) { - props.editFunc() - } - }} + {props.children} @@ -492,34 +481,43 @@ function LabelButton(props: LabelButtonProps): JSX.Element { ) } -type ViewAllButtonProps = { - state: boolean - setState: (state: boolean) => void +type EditButtonProps = { + title: string + destination: string } -function ViewAllButton(props: ViewAllButtonProps): JSX.Element { +function EditButton(props: EditButtonProps): JSX.Element { return ( - + + + {props.title} + + ) } From bd84f34a7c827d8c11af3865c95c5bdc4ca046f6 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 16:12:01 +0800 Subject: [PATCH 03/10] Improve test --- .../api/test/resolvers/subscriptions.test.ts | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/api/test/resolvers/subscriptions.test.ts b/packages/api/test/resolvers/subscriptions.test.ts index 6eda49441..f63cedee7 100644 --- a/packages/api/test/resolvers/subscriptions.test.ts +++ b/packages/api/test/resolvers/subscriptions.test.ts @@ -155,23 +155,27 @@ describe('Subscriptions API', () => { it('should not return other users subscriptions', async () => { // create test user and login - const user2 = await createTestUser('fakeUser') - await createTestSubscription( - user2, - 'sub_other', - undefined, - SubscriptionStatus.Unsubscribed, - undefined, - SubscriptionType.Rss - ) - const res = await graphqlRequest(query, authToken).expect(200) + const user2 = await createTestUser('fakeUser2') + try { + await createTestSubscription( + user2, + 'sub_other', + undefined, + SubscriptionStatus.Unsubscribed, + undefined, + SubscriptionType.Rss + ) + const res = await graphqlRequest(query, authToken).expect(200) - expect(res.body.data.subscriptions.subscriptions).to.eql( - subscriptions.map((sub) => ({ - id: sub.id, - name: sub.name, - })) - ) + expect(res.body.data.subscriptions.subscriptions).to.eql( + subscriptions.map((sub) => ({ + id: sub.id, + name: sub.name, + })) + ) + } finally { + deleteTestUser(user2.id) + } }) it('responds status code 400 when invalid query', async () => { From 561dbf3cc762060dae618e1e0c51aabf05ae3acc Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 16:12:17 +0800 Subject: [PATCH 04/10] Fix toggle arrow directions --- .../elements/icons/ToggleCaretRightIcon.tsx | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 packages/web/components/elements/icons/ToggleCaretRightIcon.tsx diff --git a/packages/web/components/elements/icons/ToggleCaretRightIcon.tsx b/packages/web/components/elements/icons/ToggleCaretRightIcon.tsx new file mode 100644 index 000000000..c8ba41e59 --- /dev/null +++ b/packages/web/components/elements/icons/ToggleCaretRightIcon.tsx @@ -0,0 +1,32 @@ +/* eslint-disable functional/no-class */ +/* eslint-disable functional/no-this-expression */ +import { IconProps } from './IconProps' + +import React from 'react' + +export class ToggleCaretRightIcon extends React.Component { + render() { + const size = (this.props.size || 26).toString() + const color = (this.props.color || '#2A2A2A').toString() + + return ( + + + + + + ) + } +} From 9f0a5dfd034ed2b655c86042dddf8907ac1f5465 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 16:15:59 +0800 Subject: [PATCH 05/10] Add Oldest First, toggle to the right when closed --- .../components/templates/homeFeed/LibraryFilterMenu.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index 986ba3736..b6462449c 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -14,6 +14,7 @@ import { ToggleCaretDownIcon } from '../../elements/icons/ToggleCaretDownIcon' import { ToggleCaretLeftIcon } from '../../elements/icons/ToggleCaretLeftIcon' import Link from 'next/link' import { ArrowRightIcon } from '../../elements/icons/ArrowRightIcon' +import { ToggleCaretRightIcon } from '../../elements/icons/ToggleCaretRightIcon' export const LIBRARY_LEFT_MENU_WIDTH = '233px' @@ -106,6 +107,10 @@ function SavedSearches(props: LibraryFilterMenuProps): JSX.Element { name: 'Unlabeled', term: 'no:label', }, + { + name: 'Oldest First', + term: 'sort:saved-desc', + }, { name: 'Files', term: 'type:file', @@ -307,7 +312,7 @@ function MenuPanel(props: MenuPanelProps): JSX.Element { }} > {props.collapsed ? ( - From 9b181633d94a19bb81d26474d6340aed09c82429 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 16:25:28 +0800 Subject: [PATCH 06/10] Fix positioning of toggle buttons --- .../components/templates/homeFeed/LibraryFilterMenu.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx index b6462449c..a363f9a54 100644 --- a/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx +++ b/packages/web/components/templates/homeFeed/LibraryFilterMenu.tsx @@ -280,7 +280,7 @@ function MenuPanel(props: MenuPanelProps): JSX.Element { alignment="start" distribution="start" > - + From 3565c7000eb90d7dda8e84495cddf5514c4e4d1b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 17:03:49 +0800 Subject: [PATCH 07/10] Update subscriptions query --- .../api/src/resolvers/subscriptions/index.ts | 22 ++++++++++--------- .../api/test/resolvers/subscriptions.test.ts | 1 - 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index 116152ff2..799b9b8a6 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -31,6 +31,7 @@ import { Merge } from '../../util' import { analytics } from '../../utils/analytics' import { enqueueRssFeedFetch } from '../../utils/createTask' import { authorized } from '../../utils/helpers' +import { Brackets } from 'typeorm' type PartialSubscription = Omit @@ -76,20 +77,21 @@ export const subscriptionsResolver = authorized< user: { id: uid }, }) - // Show all RSS, but only active newsletters - queryBuilder - .andWhere({ - type: SubscriptionType.Newsletter, - status: SubscriptionStatus.Active, - }) - .orWhere({ - type: SubscriptionType.Rss, - }) - if (type) { queryBuilder.andWhere({ type, }) + } else { + queryBuilder.andWhere( + new Brackets((qb) => { + qb.where({ + type: SubscriptionType.Newsletter, + status: SubscriptionStatus.Active, + }).orWhere({ + type: SubscriptionType.Rss, + }) + }) + ) } const subscriptions = await queryBuilder diff --git a/packages/api/test/resolvers/subscriptions.test.ts b/packages/api/test/resolvers/subscriptions.test.ts index f63cedee7..6b57510f8 100644 --- a/packages/api/test/resolvers/subscriptions.test.ts +++ b/packages/api/test/resolvers/subscriptions.test.ts @@ -166,7 +166,6 @@ describe('Subscriptions API', () => { SubscriptionType.Rss ) const res = await graphqlRequest(query, authToken).expect(200) - expect(res.body.data.subscriptions.subscriptions).to.eql( subscriptions.map((sub) => ({ id: sub.id, From 6c1484cd09e1c0c013f45dab46990b65e5875a9a Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 20:06:28 +0800 Subject: [PATCH 08/10] Add type filter --- .../api/src/resolvers/subscriptions/index.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index 799b9b8a6..b28dcb9e6 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -81,19 +81,19 @@ export const subscriptionsResolver = authorized< queryBuilder.andWhere({ type, }) - } else { - queryBuilder.andWhere( - new Brackets((qb) => { - qb.where({ - type: SubscriptionType.Newsletter, - status: SubscriptionStatus.Active, - }).orWhere({ - type: SubscriptionType.Rss, - }) - }) - ) } + queryBuilder.andWhere( + new Brackets((qb) => { + qb.where({ + type: SubscriptionType.Newsletter, + status: SubscriptionStatus.Active, + }).orWhere({ + type: SubscriptionType.Rss, + }) + }) + ) + const subscriptions = await queryBuilder .orderBy('subscription.' + sortBy, sortOrder) .getMany() From fe3d3be7f54b43b29196010644056b41adfa6fde Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 20:17:13 +0800 Subject: [PATCH 09/10] Make query more clear --- .../api/src/resolvers/subscriptions/index.ts | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index b28dcb9e6..beb246649 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -77,22 +77,27 @@ export const subscriptionsResolver = authorized< user: { id: uid }, }) - if (type) { + if (type && type == SubscriptionType.Newsletter) { queryBuilder.andWhere({ type, + status: SubscriptionStatus.Active, }) - } - - queryBuilder.andWhere( - new Brackets((qb) => { - qb.where({ - type: SubscriptionType.Newsletter, - status: SubscriptionStatus.Active, - }).orWhere({ - type: SubscriptionType.Rss, + } else if (type && type == SubscriptionType.Rss) { + queryBuilder.where({ + type, + }) + } else { + queryBuilder.andWhere( + new Brackets((qb) => { + qb.where({ + type: SubscriptionType.Newsletter, + status: SubscriptionStatus.Active, + }).orWhere({ + type: SubscriptionType.Rss, + }) }) - }) - ) + ) + } const subscriptions = await queryBuilder .orderBy('subscription.' + sortBy, sortOrder) From 527930f89bdd7299b9cb587607661d7a18e2de4d Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 29 Aug 2023 20:40:51 +0800 Subject: [PATCH 10/10] Update test --- .../api/test/resolvers/subscriptions.test.ts | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/api/test/resolvers/subscriptions.test.ts b/packages/api/test/resolvers/subscriptions.test.ts index 6b57510f8..71d412bec 100644 --- a/packages/api/test/resolvers/subscriptions.test.ts +++ b/packages/api/test/resolvers/subscriptions.test.ts @@ -10,7 +10,10 @@ import { SubscriptionStatus, SubscriptionType, } from '../../src/generated/graphql' -import { UNSUBSCRIBE_EMAIL_TEXT } from '../../src/services/subscriptions' +import { + UNSUBSCRIBE_EMAIL_TEXT, + unsubscribe, +} from '../../src/services/subscriptions' import * as sendEmail from '../../src/utils/sendEmail' import { createTestSubscription, createTestUser, deleteTestUser } from '../db' import { graphqlRequest, request } from '../util' @@ -134,23 +137,28 @@ describe('Subscriptions API', () => { undefined, SubscriptionType.Rss ) - await createTestSubscription( - user, - 'sub_6', - undefined, - SubscriptionStatus.Unsubscribed, - undefined, - SubscriptionType.Newsletter - ) - const allSubscriptions = [sub5, ...subscriptions] - const res = await graphqlRequest(query, authToken).expect(200) - expect(res.body.data.subscriptions.subscriptions).to.eql( - allSubscriptions.map((sub) => ({ - id: sub.id, - name: sub.name, - })) - ) + try { + await createTestSubscription( + user, + 'sub_6', + undefined, + SubscriptionStatus.Unsubscribed, + undefined, + SubscriptionType.Newsletter + ) + const allSubscriptions = [sub5, ...subscriptions] + const res = await graphqlRequest(query, authToken).expect(200) + + expect(res.body.data.subscriptions.subscriptions).to.eql( + allSubscriptions.map((sub) => ({ + id: sub.id, + name: sub.name, + })) + ) + } finally { + unsubscribe(sub5) + } }) it('should not return other users subscriptions', async () => {