Skip to content

Commit 3a9dc76

Browse files
committed
feat(web): add delete all notifications button to archive view in notifications sidebar
1 parent b2b2b75 commit 3a9dc76

File tree

10 files changed

+73
-10
lines changed

10 files changed

+73
-10
lines changed

api/src/graphql/generated/api/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ export type Mutation = {
632632
connectSignIn: Scalars['Boolean']['output'];
633633
connectSignOut: Scalars['Boolean']['output'];
634634
createNotification: Notification;
635+
deleteAllNotifications: NotificationOverview;
635636
deleteNotification: NotificationOverview;
636637
/** Delete a user */
637638
deleteUser?: Maybe<User>;
@@ -1723,7 +1724,7 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs
17231724

17241725

17251726
/** Mapping of interface types */
1726-
export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = ResolversObject<{
1727+
export type ResolversInterfaceTypes<_RefType extends Record<string, unknown>> = ResolversObject<{
17271728
Node: ( ArrayType ) | ( Config ) | ( Connect ) | ( Docker ) | ( Info ) | ( Network ) | ( Notification ) | ( Notifications ) | ( Service ) | ( Vars );
17281729
UserAccount: ( Me ) | ( User );
17291730
}>;
@@ -2355,6 +2356,7 @@ export type MutationResolvers<ContextType = Context, ParentType extends Resolver
23552356
connectSignIn?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationconnectSignInArgs, 'input'>>;
23562357
connectSignOut?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
23572358
createNotification?: Resolver<ResolversTypes['Notification'], ParentType, ContextType, RequireFields<MutationcreateNotificationArgs, 'input'>>;
2359+
deleteAllNotifications?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType>;
23582360
deleteNotification?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, RequireFields<MutationdeleteNotificationArgs, 'id' | 'type'>>;
23592361
deleteUser?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType, RequireFields<MutationdeleteUserArgs, 'input'>>;
23602362
enableDynamicRemoteAccess?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationenableDynamicRemoteAccessArgs, 'input'>>;

api/src/graphql/generated/client/fragment-masking.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,45 @@ export function useFragment<TType>(
2020
_documentNode: DocumentTypeDecoration<TType, any>,
2121
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
2222
): TType;
23+
// return nullable if `fragmentType` is undefined
24+
export function useFragment<TType>(
25+
_documentNode: DocumentTypeDecoration<TType, any>,
26+
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined
27+
): TType | undefined;
2328
// return nullable if `fragmentType` is nullable
29+
export function useFragment<TType>(
30+
_documentNode: DocumentTypeDecoration<TType, any>,
31+
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null
32+
): TType | null;
33+
// return nullable if `fragmentType` is nullable or undefined
2434
export function useFragment<TType>(
2535
_documentNode: DocumentTypeDecoration<TType, any>,
2636
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
2737
): TType | null | undefined;
2838
// return array of non-nullable if `fragmentType` is array of non-nullable
39+
export function useFragment<TType>(
40+
_documentNode: DocumentTypeDecoration<TType, any>,
41+
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>
42+
): Array<TType>;
43+
// return array of nullable if `fragmentType` is array of nullable
44+
export function useFragment<TType>(
45+
_documentNode: DocumentTypeDecoration<TType, any>,
46+
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
47+
): Array<TType> | null | undefined;
48+
// return readonly array of non-nullable if `fragmentType` is array of non-nullable
2949
export function useFragment<TType>(
3050
_documentNode: DocumentTypeDecoration<TType, any>,
3151
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
3252
): ReadonlyArray<TType>;
33-
// return array of nullable if `fragmentType` is array of nullable
53+
// return readonly array of nullable if `fragmentType` is array of nullable
3454
export function useFragment<TType>(
3555
_documentNode: DocumentTypeDecoration<TType, any>,
3656
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
3757
): ReadonlyArray<TType> | null | undefined;
3858
export function useFragment<TType>(
3959
_documentNode: DocumentTypeDecoration<TType, any>,
40-
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
41-
): TType | ReadonlyArray<TType> | null | undefined {
60+
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | Array<FragmentType<DocumentTypeDecoration<TType, any>>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
61+
): TType | Array<TType> | ReadonlyArray<TType> | null | undefined {
4262
return fragmentType as any;
4363
}
4464

api/src/graphql/generated/client/gql.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
1111
* 3. It does not support dead code elimination, so it will add unused operations.
1212
*
1313
* Therefore it is highly recommended to use the babel or swc plugin for production.
14+
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
1415
*/
1516
const documents = {
1617
"\n mutation sendRemoteGraphQLResponse($input: RemoteGraphQLServerInput!) {\n remoteGraphQLResponse(input: $input)\n }\n": types.sendRemoteGraphQLResponseDocument,

api/src/graphql/schema/types/notifications/notifications.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Query {
1717
type Mutation {
1818
createNotification(input: NotificationData!): Notification!
1919
deleteNotification(id: String!, type: NotificationType!): NotificationOverview!
20+
deleteAllNotifications: NotificationOverview!
2021
"""
2122
Marks a notification as archived.
2223
"""

api/src/unraid-api/graph/resolvers/notifications/notifications.resolver.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ export class NotificationsResolver {
7777
return overview;
7878
}
7979

80+
@Mutation()
81+
public async deleteAllNotifications(): Promise<NotificationOverview> {
82+
return this.notificationsService.deleteAllNotifications();
83+
}
84+
8085
@Mutation()
8186
public archiveNotification(@Args('id') id: string) {
8287
return this.notificationsService.archiveNotification({ id });

web/components/Notifications/Sidebar.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/co
33
import { useMutation } from '@vue/apollo-composable';
44
// eslint-disable-next-line @typescript-eslint/consistent-type-imports -- false positive :(
55
import { Importance, NotificationType } from '~/composables/gql/graphql';
6-
import { archiveAllNotifications } from './graphql/notification.query';
6+
import { archiveAllNotifications, deleteAllNotifications } from './graphql/notification.query';
77
88
const { mutate: archiveAll, loading: loadingArchiveAll } = useMutation(archiveAllNotifications);
9+
const { mutate: deleteAll, loading: loadingDeleteAll } = useMutation(deleteAllNotifications);
910
const { teleportTarget, determineTeleportTarget } = useTeleport();
1011
const importance = ref<Importance | undefined>(undefined);
1112
</script>
@@ -51,9 +52,11 @@ const importance = ref<Importance | undefined>(undefined);
5152
</TabsContent>
5253
<TabsContent value="archived">
5354
<Button
55+
:disabled="loadingDeleteAll"
5456
variant="link"
5557
size="sm"
5658
class="text-muted-foreground text-base p-0"
59+
@click="deleteAll"
5760
>
5861
Delete All
5962
</Button>

web/components/Notifications/graphql/notification.query.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@ export const deleteNotification = graphql(/* GraphQL */ `
6767
}
6868
`);
6969

70+
export const deleteAllNotifications = graphql(/* GraphQL */ `
71+
mutation DeleteAllNotifications {
72+
deleteAllNotifications {
73+
archive {
74+
total
75+
}
76+
unread {
77+
total
78+
}
79+
}
80+
}
81+
`);
82+
7083
export const unreadOverview = graphql(/* GraphQL */ `
7184
query Overview {
7285
notifications {

web/composables/gql/gql.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const documents = {
2020
"\n mutation ArchiveNotification($id: String!) {\n archiveNotification(id: $id) {\n ...NotificationFragment\n }\n }\n": types.ArchiveNotificationDocument,
2121
"\n mutation ArchiveAllNotifications {\n archiveAll {\n unread {\n total\n }\n archive {\n info\n warning\n alert\n total\n }\n }\n }\n": types.ArchiveAllNotificationsDocument,
2222
"\n mutation DeleteNotification($id: String!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n": types.DeleteNotificationDocument,
23+
"\n mutation DeleteAllNotifications {\n deleteAllNotifications {\n archive {\n total\n }\n unread {\n total\n }\n }\n }\n": types.DeleteAllNotificationsDocument,
2324
"\n query Overview {\n notifications {\n overview {\n unread {\n info\n warning\n alert\n total\n }\n }\n }\n }\n": types.OverviewDocument,
2425
"\n mutation ConnectSignIn($input: ConnectSignInInput!) {\n connectSignIn(input: $input)\n }\n": types.ConnectSignInDocument,
2526
"\n mutation SignOut {\n connectSignOut\n }\n": types.SignOutDocument,
@@ -69,6 +70,10 @@ export function graphql(source: "\n mutation ArchiveAllNotifications {\n arc
6970
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
7071
*/
7172
export function graphql(source: "\n mutation DeleteNotification($id: String!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n"): (typeof documents)["\n mutation DeleteNotification($id: String!, $type: NotificationType!) {\n deleteNotification(id: $id, type: $type) {\n archive {\n total\n }\n }\n }\n"];
73+
/**
74+
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
75+
*/
76+
export function graphql(source: "\n mutation DeleteAllNotifications {\n deleteAllNotifications {\n archive {\n total\n }\n unread {\n total\n }\n }\n }\n"): (typeof documents)["\n mutation DeleteAllNotifications {\n deleteAllNotifications {\n archive {\n total\n }\n unread {\n total\n }\n }\n }\n"];
7277
/**
7378
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
7479
*/

web/composables/gql/graphql.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,11 @@ export type DeleteNotificationMutationVariables = Exact<{
16941694

16951695
export type DeleteNotificationMutation = { __typename?: 'Mutation', deleteNotification: { __typename?: 'NotificationOverview', archive: { __typename?: 'NotificationCounts', total: number } } };
16961696

1697+
export type DeleteAllNotificationsMutationVariables = Exact<{ [key: string]: never; }>;
1698+
1699+
1700+
export type DeleteAllNotificationsMutation = { __typename?: 'Mutation', deleteAllNotifications: { __typename?: 'NotificationOverview', archive: { __typename?: 'NotificationCounts', total: number }, unread: { __typename?: 'NotificationCounts', total: number } } };
1701+
16971702
export type OverviewQueryVariables = Exact<{ [key: string]: never; }>;
16981703

16991704

@@ -1752,6 +1757,7 @@ export const NotificationsDocument = {"kind":"Document","definitions":[{"kind":"
17521757
export const ArchiveNotificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ArchiveNotification"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archiveNotification"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NotificationFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Notification"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"importance"}},{"kind":"Field","name":{"kind":"Name","value":"link"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}}]}}]} as unknown as DocumentNode<ArchiveNotificationMutation, ArchiveNotificationMutationVariables>;
17531758
export const ArchiveAllNotificationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ArchiveAllNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archiveAll"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"warning"}},{"kind":"Field","name":{"kind":"Name","value":"alert"}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode<ArchiveAllNotificationsMutation, ArchiveAllNotificationsMutationVariables>;
17541759
export const DeleteNotificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteNotification"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"type"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationType"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteNotification"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"type"},"value":{"kind":"Variable","name":{"kind":"Name","value":"type"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode<DeleteNotificationMutation, DeleteNotificationMutationVariables>;
1760+
export const DeleteAllNotificationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteAllNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteAllNotifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archive"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"unread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode<DeleteAllNotificationsMutation, DeleteAllNotificationsMutationVariables>;
17551761
export const OverviewDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Overview"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"notifications"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"overview"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unread"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"warning"}},{"kind":"Field","name":{"kind":"Name","value":"alert"}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]}}]} as unknown as DocumentNode<OverviewQuery, OverviewQueryVariables>;
17561762
export const ConnectSignInDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ConnectSignIn"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ConnectSignInInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"connectSignIn"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<ConnectSignInMutation, ConnectSignInMutationVariables>;
17571763
export const SignOutDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SignOut"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"connectSignOut"}}]}}]} as unknown as DocumentNode<SignOutMutation, SignOutMutationVariables>;

web/helpers/apollo-cache/index.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { InMemoryCache, type InMemoryCacheConfig } from "@apollo/client/core/index.js";
2-
import { mergeAndDedup } from "./merge";
3-
import { NotificationType } from "../../composables/gql/typename";
1+
import { InMemoryCache, type InMemoryCacheConfig } from '@apollo/client/core/index.js';
2+
import { NotificationType } from '../../composables/gql/typename';
3+
import { mergeAndDedup } from './merge';
44

55
/**------------------------------------------------------------------------
66
* ! Understanding Cache Type Policies
@@ -36,7 +36,7 @@ const defaultCacheConfig: InMemoryCacheConfig = {
3636
* i.e. this means [filter.type, filter.importance],
3737
* not [filter, type, importance]
3838
*---------------------------------------------**/
39-
keyArgs: ["filter", ["type", "importance"]],
39+
keyArgs: ['filter', ['type', 'importance']],
4040

4141
/**
4242
* Merges incoming data into the correct offset position.
@@ -72,7 +72,7 @@ const defaultCacheConfig: InMemoryCacheConfig = {
7272
* @returns the value to cache for this operation
7373
*/
7474
merge(_, incoming, { cache }) {
75-
cache.evict({ fieldName: "notifications" });
75+
cache.evict({ fieldName: 'notifications' });
7676
cache.gc(); // Run garbage collection to prevent orphans & incorrect cache state
7777
return incoming; // Return the incoming data so Apollo knows the result of the mutation
7878
},
@@ -101,6 +101,13 @@ const defaultCacheConfig: InMemoryCacheConfig = {
101101
return incoming;
102102
},
103103
},
104+
deleteAllNotifications: {
105+
merge(_, incoming, { cache }) {
106+
cache.evict({ fieldName: 'notifications' });
107+
cache.gc();
108+
return incoming;
109+
},
110+
},
104111
},
105112
},
106113
},

0 commit comments

Comments
 (0)