diff --git a/app/javascript/mastodon/actions/notification_groups.ts b/app/javascript/mastodon/actions/notification_groups.ts new file mode 100644 index 0000000000..b804148eeb --- /dev/null +++ b/app/javascript/mastodon/actions/notification_groups.ts @@ -0,0 +1,36 @@ +import { apiFetchNotifications } from 'mastodon/api/notifications'; +import type { ApiAccountJSON } from 'mastodon/api_types/accounts'; +import type { ApiStatusJSON } from 'mastodon/api_types/statuses'; +import { createDataLoadingThunk } from 'mastodon/store/typed_functions'; + +import { importFetchedAccounts, importFetchedStatuses } from './importer'; + +export const fetchNotifications = createDataLoadingThunk( + 'notificationGroups/fetch', + apiFetchNotifications, + (notifications, { dispatch }) => { + const fetchedAccounts: ApiAccountJSON[] = []; + const fetchedStatuses: ApiStatusJSON[] = []; + + notifications.forEach((notification) => { + if ('sample_accounts' in notification) { + fetchedAccounts.push(...notification.sample_accounts); + } + + // if (notification.type === 'admin.report') { + // fetchedAccounts.push(...notification.report.target_account); + // } + + if ('target_status' in notification) { + fetchedStatuses.push(notification.target_status); + } + }); + + if (fetchedAccounts.length > 0) + dispatch(importFetchedAccounts(fetchedAccounts)); + + if (fetchedStatuses.length > 0) importFetchedStatuses(fetchedStatuses); + + // dispatch(submitMarkers()); + }, +); diff --git a/app/javascript/mastodon/api/notifications.ts b/app/javascript/mastodon/api/notifications.ts new file mode 100644 index 0000000000..a32520de79 --- /dev/null +++ b/app/javascript/mastodon/api/notifications.ts @@ -0,0 +1,6 @@ +import { apiRequest } from 'mastodon/api'; +import type { NotificationGroupJSON } from 'mastodon/api_types/notifications'; + +export const apiFetchNotifications = () => { + return apiRequest('GET', '/v2_alpha/notifications'); +}; diff --git a/app/javascript/mastodon/api_types/notifications.ts b/app/javascript/mastodon/api_types/notifications.ts new file mode 100644 index 0000000000..53dd68227b --- /dev/null +++ b/app/javascript/mastodon/api_types/notifications.ts @@ -0,0 +1,60 @@ +// See app/serializers/rest/notification_group_serializer.rb + +import type { ApiAccountJSON } from './accounts'; +import type { ApiStatusJSON } from './statuses'; + +// See app/model/notification.rb +export type NotificationWithStatusType = + | 'favourite' + | 'reblog' + | 'status' + | 'mention' + | 'poll' + | 'update'; + +export type NotificationType = + | NotificationWithStatusType + | 'follow' + | 'follow_request' + | 'moderation_warning' + | 'severed_relationships' + | 'admin.sign_up' + | 'admin.report'; + +export interface BaseNotificationGroupJSON { + group_key: string; + notifications_count: number; + type: NotificationType; + sample_accounts: ApiAccountJSON[]; + latest_page_notification_at?: string; + page_min_id?: string; + page_max_id?: string; +} + +interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON { + type: NotificationWithStatusType; + target_status: ApiStatusJSON; +} + +interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON { + type: 'admin.report'; + report: unknown; +} + +interface ModerationWarningNotificationGroupJSON + extends BaseNotificationGroupJSON { + type: 'moderation_warning'; + moderation_warning: unknown; +} + +interface AccountRelationshipSeveranceNotificationGroupJSON + extends BaseNotificationGroupJSON { + type: 'severed_relationships'; + account_relationship_severance_event: unknown; +} + +export type NotificationGroupJSON = + | ReportNotificationGroupJSON + | AccountRelationshipSeveranceNotificationGroupJSON + | NotificationGroupWithStatusJSON + | ModerationWarningNotificationGroupJSON; diff --git a/app/javascript/mastodon/models/notification_group.ts b/app/javascript/mastodon/models/notification_group.ts new file mode 100644 index 0000000000..da2c5f5f61 --- /dev/null +++ b/app/javascript/mastodon/models/notification_group.ts @@ -0,0 +1,59 @@ +import type { + BaseNotificationGroupJSON, + NotificationGroupJSON, + NotificationType, + NotificationWithStatusType, +} from 'mastodon/api_types/notifications'; +import type { ApiStatusJSON } from 'mastodon/api_types/statuses'; + +type BaseNotificationGroup = BaseNotificationGroupJSON; + +interface BaseNotificationWithStatus + extends BaseNotificationGroup { + type: Type; + status: ApiStatusJSON; +} + +interface BaseNotification + extends BaseNotificationGroup { + type: Type; +} + +export type NotificationGroupFavourite = + BaseNotificationWithStatus<'favourite'>; +export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>; +export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>; +export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>; +export type NotificationGroupPoll = BaseNotificationWithStatus<'poll'>; +export type NotificationGroupUpdate = BaseNotificationWithStatus<'update'>; +export type NotificationGroupFollow = BaseNotification<'follow'>; +export type NotificationGroupFollowRequest = BaseNotification<'follow_request'>; +export type NotificationGroupAdminSignUp = BaseNotification<'admin.sign_up'>; + +// TODO: those two will need special types +export type NotificationGroupModerationWarning = + BaseNotification<'moderation_warning'>; +export type NotificationGroupAdminReport = BaseNotification<'admin.report'>; +export type NotificationGroupSeveredRelationships = + BaseNotification<'severed_relationships'>; + +export type NotificationGroup = + | NotificationGroupFavourite + | NotificationGroupReblog + | NotificationGroupStatus + | NotificationGroupMention + | NotificationGroupPoll + | NotificationGroupUpdate + | NotificationGroupFollow + | NotificationGroupFollowRequest + | NotificationGroupModerationWarning + | NotificationGroupSeveredRelationships + | NotificationGroupAdminSignUp + | NotificationGroupAdminReport; + +export function createNotificationGroupFromJSON( + groupJson: NotificationGroupJSON, +): NotificationGroup { + // @ts-expect-error -- FIXME: properly convert the special notifications here + return groupJson; +} diff --git a/app/javascript/mastodon/reducers/notifications_groups.ts b/app/javascript/mastodon/reducers/notifications_groups.ts new file mode 100644 index 0000000000..13b9c74ebe --- /dev/null +++ b/app/javascript/mastodon/reducers/notifications_groups.ts @@ -0,0 +1,26 @@ +import { createReducer } from '@reduxjs/toolkit'; + +import { fetchNotifications } from 'mastodon/actions/notification_groups'; +import { createNotificationGroupFromJSON } from 'mastodon/models/notification_group'; +import type { NotificationGroup } from 'mastodon/models/notification_group'; + +interface NotificationGroupsState { + groups: NotificationGroup[]; + unread: number; +} + +const initialState: NotificationGroupsState = { + groups: [], + unread: 0, +}; + +export const notificationGroupsReducer = createReducer( + initialState, + (builder) => { + builder.addCase(fetchNotifications.fulfilled, (state, action) => { + state.groups = action.payload.map((json) => + createNotificationGroupFromJSON(json), + ); + }); + }, +);