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 601bbfb0c..e073207a4 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -47,49 +47,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: SubscriptionStatus.Active, - 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, @@ -318,7 +330,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') @@ -335,6 +346,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..edd4e075a 100644 --- a/packages/web/pages/settings/rss/index.tsx +++ b/packages/web/pages/settings/rss/index.tsx @@ -8,15 +8,16 @@ 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' import { unsubscribeMutation } from '../../../lib/networking/mutations/unsubscribeMutation' import { updateSubscriptionMutation } from '../../../lib/networking/mutations/updateSubscriptionMutation' import { + SubscriptionStatus, SubscriptionType, - useGetSubscriptionsQuery, + useGetSubscriptionsQuery } from '../../../lib/networking/queries/useGetSubscriptionsQuery' import { applyStoredTheme } from '../../../lib/themeUpdater' import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' @@ -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,27 @@ export default function Rss(): JSX.Element { revalidate() } + async function onPause( + id: string, + status: SubscriptionStatus = 'UNSUBSCRIBED' + ): Promise { + const result = await updateSubscriptionMutation({ + id, + status, + }) + + const action = status == 'UNSUBSCRIBED' ? 'pause' : 'resume' + + if (result) { + showSuccessToast(`RSS feed ${action}d`, { + position: 'bottom-right', + }) + } else { + showErrorToast(`Failed to ${action}`, { position: 'bottom-right' }) + } + revalidate() + } + applyStoredTheme(false) return ( @@ -152,7 +176,14 @@ export default function Rss(): JSX.Element { console.log('onDelete triggered: ', subscription.id) setOnDeleteId(subscription.id) }} + onEdit={() => { + setOnEditStatus( + subscription.status == 'ACTIVE' ? 'UNSUBSCRIBED' : 'ACTIVE' + ) + setOnPauseId(subscription.id) + }} deleteTitle="Delete" + editTitle={subscription.status === 'ACTIVE' ? 'Pause' : 'Resume'} sublineElement={ { router.push(`/home?q=in:inbox rss:"${subscription.url}"`) }} + extraElement={ + + {subscription.status === 'ACTIVE' ? 'Active' : 'Paused'} + + } /> ) }) @@ -188,6 +228,25 @@ export default function Rss(): JSX.Element { onOpenChange={() => setOnDeleteId('')} /> )} + + {onPauseId && ( + { + await onPause(onPauseId, onEditStatus) + setOnPauseId('') + setOnEditStatus(undefined) + }} + onOpenChange={() => { + setOnPauseId('') + setOnEditStatus(undefined) + }} + /> + )} ) }