From b403dea247dd07ee0dbd9737242335c93156c704 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 1 Aug 2023 22:07:53 +0800 Subject: [PATCH 1/3] add a button to pause rss feed --- packages/api/src/generated/graphql.ts | 1 + packages/api/src/generated/schema.graphql | 1 + .../api/src/resolvers/subscriptions/index.ts | 7 ++- packages/api/src/schema.ts | 1 + .../mutations/updateSubscriptionMutation.ts | 6 ++- packages/web/pages/settings/rss/index.tsx | 44 +++++++++++++++++++ 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index 07daba72c..99fa8611d 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -3031,6 +3031,7 @@ export type UpdateSubscriptionInput = { id: Scalars['ID']; lastFetchedAt?: InputMaybe; name?: InputMaybe; + status?: InputMaybe; }; export type UpdateSubscriptionResult = UpdateSubscriptionError | UpdateSubscriptionSuccess; diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index b001391ca..f06cea0fa 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -2353,6 +2353,7 @@ input UpdateSubscriptionInput { id: ID! lastFetchedAt: Date name: String + status: SubscriptionStatus } union UpdateSubscriptionResult = UpdateSubscriptionError | UpdateSubscriptionSuccess diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index a74c49a81..de2d62c5a 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -71,7 +71,10 @@ export const subscriptionsResolver = authorized< .leftJoinAndSelect('subscription.newsletterEmail', 'newsletterEmail') .where({ user: { id: uid }, - status: SubscriptionStatus.Active, + status: + subscriptionType == SubscriptionType.Newsletter + ? SubscriptionStatus.Active + : undefined, // only return active subscriptions for newsletter type: subscriptionType || SubscriptionType.Newsletter, // default to newsletter }) .orderBy('subscription.' + sortBy, sortOrder) @@ -315,7 +318,6 @@ export const updateSubscriptionResolver = authorized< const subscription = await getRepository(Subscription).findOneBy({ id: input.id, user: { id: uid }, - status: SubscriptionStatus.Active, }) if (!subscription) { log.info('subscription not found') @@ -332,6 +334,7 @@ export const updateSubscriptionResolver = authorized< lastFetchedAt: input.lastFetchedAt ? new Date(input.lastFetchedAt) : undefined, + status: input.status || undefined, }) return { diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index 74b3f283b..15b5d31c1 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -2511,6 +2511,7 @@ const schema = gql` name: String description: String lastFetchedAt: Date + status: SubscriptionStatus } union UpdateSubscriptionResult = diff --git a/packages/web/lib/networking/mutations/updateSubscriptionMutation.ts b/packages/web/lib/networking/mutations/updateSubscriptionMutation.ts index b623ff482..08e06d6e6 100644 --- a/packages/web/lib/networking/mutations/updateSubscriptionMutation.ts +++ b/packages/web/lib/networking/mutations/updateSubscriptionMutation.ts @@ -1,6 +1,9 @@ import { gql } from 'graphql-request' import { gqlFetcher } from '../networkHelpers' -import { Subscription } from '../queries/useGetSubscriptionsQuery' +import { + Subscription, + SubscriptionStatus, +} from '../queries/useGetSubscriptionsQuery' interface UpdateSubscriptionResult { updateSubscription: UpdateSubscription @@ -22,6 +25,7 @@ interface UpdateSubscriptionInput { lastFetchedAt?: Date name?: string description?: string + status?: SubscriptionStatus } export async function updateSubscriptionMutation( diff --git a/packages/web/pages/settings/rss/index.tsx b/packages/web/pages/settings/rss/index.tsx index 5e75cdebe..a7105c62f 100644 --- a/packages/web/pages/settings/rss/index.tsx +++ b/packages/web/pages/settings/rss/index.tsx @@ -15,6 +15,7 @@ import { formattedDateTime } from '../../../lib/dateFormatting' import { unsubscribeMutation } from '../../../lib/networking/mutations/unsubscribeMutation' import { updateSubscriptionMutation } from '../../../lib/networking/mutations/updateSubscriptionMutation' import { + SubscriptionStatus, SubscriptionType, useGetSubscriptionsQuery, } from '../../../lib/networking/queries/useGetSubscriptionsQuery' @@ -30,6 +31,8 @@ export default function Rss(): JSX.Element { const [onDeleteId, setOnDeleteId] = useState('') const [onEditId, setOnEditId] = useState('') const [onEditName, setOnEditName] = useState('') + const [onPauseId, setOnPauseId] = useState('') + const [onEditStatus, setOnEditStatus] = useState() async function updateSubscription(): Promise { const result = await updateSubscriptionMutation({ @@ -61,6 +64,19 @@ export default function Rss(): JSX.Element { revalidate() } + async function onPause(): Promise { + const result = await updateSubscriptionMutation({ + id: onPauseId, + status: onEditStatus, + }) + if (result) { + showSuccessToast('RSS feed paused', { position: 'bottom-right' }) + } else { + showErrorToast('Failed to pause', { position: 'bottom-right' }) + } + revalidate() + } + applyStoredTheme(false) return ( @@ -152,7 +168,18 @@ export default function Rss(): JSX.Element { console.log('onDelete triggered: ', subscription.id) setOnDeleteId(subscription.id) }} + onEdit={() => { + setOnEditStatus( + subscription.status == 'UNSUBSCRIBED' + ? 'ACTIVE' + : 'UNSUBSCRIBED' + ) + setOnPauseId(subscription.id) + }} deleteTitle="Delete" + editTitle={ + subscription.status === 'UNSUBSCRIBED' ? 'Unpause' : 'Pause' + } sublineElement={ setOnDeleteId('')} /> )} + + {onPauseId && ( + { + setOnPauseId('') + setOnEditStatus(undefined) + await onPause() + }} + onOpenChange={() => { + setOnPauseId('') + setOnEditStatus(undefined) + }} + /> + )} ) } From f3994a8a06d7b77ebe33f400de86da2dd90a1b9a Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 2 Aug 2023 22:00:09 +0800 Subject: [PATCH 2/3] fix test --- .../api/src/resolvers/subscriptions/index.ts | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index de2d62c5a..fe4b494ea 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -44,52 +44,61 @@ export const subscriptionsResolver = authorized< SubscriptionsSuccessPartial, SubscriptionsError, QuerySubscriptionsArgs ->(async (_obj, { sort, type: subscriptionType }, { claims: { uid }, log }) => { - log.info('subscriptionsResolver') +>( + async ( + _obj, + { sort, type = SubscriptionType.Newsletter }, // default to newsletter + { claims: { uid }, log } + ) => { + log.info('subscriptionsResolver') - analytics.track({ - userId: uid, - event: 'subscriptions', - properties: { - env: env.server.apiEnv, - }, - }) + analytics.track({ + userId: uid, + event: 'subscriptions', + properties: { + env: env.server.apiEnv, + }, + }) + + try { + const sortBy = + sort?.by === SortBy.UpdatedTime ? 'lastFetchedAt' : 'createdAt' + const sortOrder = sort?.order === SortOrder.Ascending ? 'ASC' : 'DESC' + const user = await getRepository(User).findOneBy({ id: uid }) + if (!user) { + return { + errorCodes: [SubscriptionsErrorCode.Unauthorized], + } + } + + const queryBuilder = getRepository(Subscription) + .createQueryBuilder('subscription') + .leftJoinAndSelect('subscription.newsletterEmail', 'newsletterEmail') + .where({ + user: { id: uid }, + type, + }) + + // only return active subscriptions for newsletter + if (type === SubscriptionType.Newsletter) { + queryBuilder.andWhere({ status: SubscriptionStatus.Active }) + } + + const subscriptions = await queryBuilder + .orderBy('subscription.' + sortBy, sortOrder) + .getMany() - try { - const sortBy = - sort?.by === SortBy.UpdatedTime ? 'lastFetchedAt' : 'createdAt' - const sortOrder = sort?.order === SortOrder.Ascending ? 'ASC' : 'DESC' - const user = await getRepository(User).findOneBy({ id: uid }) - if (!user) { return { - errorCodes: [SubscriptionsErrorCode.Unauthorized], + subscriptions, + } + } catch (error) { + log.error(error) + return { + errorCodes: [SubscriptionsErrorCode.BadRequest], } } - - const subscriptions = await getRepository(Subscription) - .createQueryBuilder('subscription') - .leftJoinAndSelect('subscription.newsletterEmail', 'newsletterEmail') - .where({ - user: { id: uid }, - status: - subscriptionType == SubscriptionType.Newsletter - ? SubscriptionStatus.Active - : undefined, // only return active subscriptions for newsletter - type: subscriptionType || SubscriptionType.Newsletter, // default to newsletter - }) - .orderBy('subscription.' + sortBy, sortOrder) - .getMany() - - return { - subscriptions, - } - } catch (error) { - log.error(error) - return { - errorCodes: [SubscriptionsErrorCode.BadRequest], - } } -}) +) export type UnsubscribeSuccessPartial = Merge< UnsubscribeSuccess, From 27b8bafee73a02dfe647250681e686ae8e2a52ac Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 2 Aug 2023 22:28:20 +0800 Subject: [PATCH 3/3] show status on the row --- packages/web/pages/settings/rss/index.tsx | 49 +++++++++++++++-------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/web/pages/settings/rss/index.tsx b/packages/web/pages/settings/rss/index.tsx index a7105c62f..edd4e075a 100644 --- a/packages/web/pages/settings/rss/index.tsx +++ b/packages/web/pages/settings/rss/index.tsx @@ -8,7 +8,7 @@ import { ConfirmationModal } from '../../../components/patterns/ConfirmationModa import { EmptySettingsRow, SettingsTable, - SettingsTableRow, + SettingsTableRow } from '../../../components/templates/settings/SettingsTable' import { theme } from '../../../components/tokens/stitches.config' import { formattedDateTime } from '../../../lib/dateFormatting' @@ -17,7 +17,7 @@ import { updateSubscriptionMutation } from '../../../lib/networking/mutations/up import { SubscriptionStatus, SubscriptionType, - useGetSubscriptionsQuery, + useGetSubscriptionsQuery } from '../../../lib/networking/queries/useGetSubscriptionsQuery' import { applyStoredTheme } from '../../../lib/themeUpdater' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' @@ -64,15 +64,23 @@ export default function Rss(): JSX.Element { revalidate() } - async function onPause(): Promise { + async function onPause( + id: string, + status: SubscriptionStatus = 'UNSUBSCRIBED' + ): Promise { const result = await updateSubscriptionMutation({ - id: onPauseId, - status: onEditStatus, + id, + status, }) + + const action = status == 'UNSUBSCRIBED' ? 'pause' : 'resume' + if (result) { - showSuccessToast('RSS feed paused', { position: 'bottom-right' }) + showSuccessToast(`RSS feed ${action}d`, { + position: 'bottom-right', + }) } else { - showErrorToast('Failed to pause', { position: 'bottom-right' }) + showErrorToast(`Failed to ${action}`, { position: 'bottom-right' }) } revalidate() } @@ -170,16 +178,12 @@ export default function Rss(): JSX.Element { }} onEdit={() => { setOnEditStatus( - subscription.status == 'UNSUBSCRIBED' - ? 'ACTIVE' - : 'UNSUBSCRIBED' + subscription.status == 'ACTIVE' ? 'UNSUBSCRIBED' : 'ACTIVE' ) setOnPauseId(subscription.id) }} deleteTitle="Delete" - editTitle={ - subscription.status === 'UNSUBSCRIBED' ? 'Unpause' : 'Pause' - } + editTitle={subscription.status === 'ACTIVE' ? 'Pause' : 'Resume'} sublineElement={ { router.push(`/home?q=in:inbox rss:"${subscription.url}"`) }} + extraElement={ + + {subscription.status === 'ACTIVE' ? 'Active' : 'Paused'} + + } /> ) }) @@ -218,13 +231,15 @@ export default function Rss(): JSX.Element { {onPauseId && ( { + await onPause(onPauseId, onEditStatus) setOnPauseId('') setOnEditStatus(undefined) - await onPause() }} onOpenChange={() => { setOnPauseId('')