From 7cc410654a9faa3727a180eff878c5dfc8e8ddf2 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 17 Oct 2024 16:47:46 -0400 Subject: [PATCH] Revert "remove NotificationController" This reverts commit 7969de551e4d19ece5fd54095b4478c1c20e01df. --- packages/notification-controller/CHANGELOG.md | 175 ++++++++++++++ packages/notification-controller/LICENSE | 20 ++ packages/notification-controller/README.md | 15 ++ .../notification-controller/jest.config.js | 26 +++ packages/notification-controller/package.json | 71 ++++++ .../src/NotificationController.test.ts | 159 +++++++++++++ .../src/NotificationController.ts | 219 ++++++++++++++++++ packages/notification-controller/src/index.ts | 1 + .../tsconfig.build.json | 10 + .../notification-controller/tsconfig.json | 8 + packages/notification-controller/typedoc.json | 7 + tsconfig.json | 1 + 12 files changed, 712 insertions(+) create mode 100644 packages/notification-controller/CHANGELOG.md create mode 100644 packages/notification-controller/LICENSE create mode 100644 packages/notification-controller/README.md create mode 100644 packages/notification-controller/jest.config.js create mode 100644 packages/notification-controller/package.json create mode 100644 packages/notification-controller/src/NotificationController.test.ts create mode 100644 packages/notification-controller/src/NotificationController.ts create mode 100644 packages/notification-controller/src/index.ts create mode 100644 packages/notification-controller/tsconfig.build.json create mode 100644 packages/notification-controller/tsconfig.json create mode 100644 packages/notification-controller/typedoc.json diff --git a/packages/notification-controller/CHANGELOG.md b/packages/notification-controller/CHANGELOG.md new file mode 100644 index 0000000000..7ece224cc1 --- /dev/null +++ b/packages/notification-controller/CHANGELOG.md @@ -0,0 +1,175 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [7.0.0] + +### Changed + +- **BREAKING**: The `show` function now uses an option bag rather than a single `string` ([#4682](https://github.com/MetaMask/core/pull/4682)) + - This will be used to persist the extra properties needed to show JSX content in Snap notifications. + +## [6.0.1] + +### Changed + +- Bump `@metamask/utils` from `^8.3.0` to `^9.1.0` ([#4516](https://github.com/MetaMask/core/pull/4516), [#4529](https://github.com/MetaMask/core/pull/4529)) +- Bump `@metamask/rpc-errors` from `^6.2.1` to `^6.3.1` ([#4516](https://github.com/MetaMask/core/pull/4516)) +- Bump TypeScript from `~4.9.5` to `~5.2.2` and set `moduleResolution` option to `Node16` ([#3645](https://github.com/MetaMask/core/pull/3645), [#4576](https://github.com/MetaMask/core/pull/4576), [#4584](https://github.com/MetaMask/core/pull/4584)) + +### Fixed + +- Produce and export ESM-compatible TypeScript type declaration files in addition to CommonJS-compatible declaration files ([#4648](https://github.com/MetaMask/core/pull/4648)) + - Previously, this package shipped with only one variant of type declaration + files, and these files were only CommonJS-compatible, and the `exports` + field in `package.json` linked to these files. This is an anti-pattern and + was rightfully flagged by the + ["Are the Types Wrong?"](https://arethetypeswrong.github.io/) tool as + ["masquerading as CJS"](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md). + All of the ATTW checks now pass. +- Remove chunk files ([#4648](https://github.com/MetaMask/core/pull/4648)). + - Previously, the build tool we used to generate JavaScript files extracted + common code to "chunk" files. While this was intended to make this package + more tree-shakeable, it also made debugging more difficult for our + development teams. These chunk files are no longer present. + +## [6.0.0] + +### Changed + +- **BREAKING:** Bump minimum Node version to 18.18 ([#3611](https://github.com/MetaMask/core/pull/3611)) +- Bump `@metamask/base-controller` to `^6.0.0` ([#4352](https://github.com/MetaMask/core/pull/4352)) + +## [5.0.2] + +### Changed + +- Bump `@metamask/base-controller` to `^5.0.2` ([#4232](https://github.com/MetaMask/core/pull/4232)) + +## [5.0.1] + +### Fixed + +- Fix `types` field in `package.json` ([#4047](https://github.com/MetaMask/core/pull/4047)) + +## [5.0.0] + +### Added + +- **BREAKING**: Add ESM build ([#3998](https://github.com/MetaMask/core/pull/3998)) + - It's no longer possible to import files from `./dist` directly. + +### Changed + +- **BREAKING:** Bump `@metamask/base-controller` to `^5.0.0` ([#4039](https://github.com/MetaMask/core/pull/4039)) + - This version has a number of breaking changes. See the changelog for more. + +## [4.0.2] + +### Changed + +- Bump `@metamask/utils` to `^8.3.0` ([#3769](https://github.com/MetaMask/core/pull/3769)) +- Bump `@metamask/base-controller` to `^4.1.1` ([#3760](https://github.com/MetaMask/core/pull/3760), [#3821](https://github.com/MetaMask/core/pull/3821)) + +## [4.0.1] + +### Changed + +- Bump `@metamask/base-controller` to `^4.0.1` ([#3695](https://github.com/MetaMask/core/pull/3695)) + +## [4.0.0] + +### Changed + +- **BREAKING:** Bump `@metamask/base-controller` to ^4.0.0 ([#2063](https://github.com/MetaMask/core/pull/2063)) + - This is breaking because the type of the `messenger` has backward-incompatible changes. See the changelog for this package for more. +- Bump `@metamask/utils` to ^8.2.0 ([#1957](https://github.com/MetaMask/core/pull/1957)) + +## [3.1.3] + +### Changed + +- Bump dependency on `@metamask/utils` to ^8.1.0 ([#1639](https://github.com/MetaMask/core/pull/1639)) +- Bump dependency on `@metamask/base-controller` to ^3.2.3 + +## [3.1.2] + +### Changed + +- Update TypeScript to v4.8.x ([#1718](https://github.com/MetaMask/core/pull/1718)) + +## [3.1.1] + +### Changed + +- Bump dependency on `@metamask/base-controller` to ^3.2.1 + +## [3.1.0] + +### Changed + +- Update `@metamask/utils` to `^6.2.0` ([#1514](https://github.com/MetaMask/core/pull/1514)) + +## [3.0.0] + +### Changed + +- **BREAKING:** Bump to Node 16 ([#1262](https://github.com/MetaMask/core/pull/1262)) +- Add `@metamask/utils` dependency ([#1275](https://github.com/MetaMask/core/pull/1275)) + +## [2.0.0] + +### Removed + +- **BREAKING:** Remove `isomorphic-fetch` ([#1106](https://github.com/MetaMask/controllers/pull/1106)) + - Consumers must now import `isomorphic-fetch` or another polyfill themselves if they are running in an environment without `fetch` + +## [1.0.2] + +### Changed + +- Rename this repository to `core` ([#1031](https://github.com/MetaMask/controllers/pull/1031)) +- Update `@metamask/controller-utils` package ([#1041](https://github.com/MetaMask/controllers/pull/1041)) + +## [1.0.1] + +### Changed + +- Relax dependencies on `@metamask/base-controller` and `@metamask/controller-utils` (use `^` instead of `~`) ([#998](https://github.com/MetaMask/core/pull/998)) + +## [1.0.0] + +### Added + +- Initial release + + - As a result of converting our shared controllers repo into a monorepo ([#831](https://github.com/MetaMask/core/pull/831)), we've created this package from select parts of [`@metamask/controllers` v33.0.0](https://github.com/MetaMask/core/tree/v33.0.0), namely: + + - Everything in `src/notification` + + All changes listed after this point were applied to this package following the monorepo conversion. + +[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@7.0.0...HEAD +[7.0.0]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@6.0.1...@metamask/notification-controller@7.0.0 +[6.0.1]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@6.0.0...@metamask/notification-controller@6.0.1 +[6.0.0]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@5.0.2...@metamask/notification-controller@6.0.0 +[5.0.2]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@5.0.1...@metamask/notification-controller@5.0.2 +[5.0.1]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@5.0.0...@metamask/notification-controller@5.0.1 +[5.0.0]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@4.0.2...@metamask/notification-controller@5.0.0 +[4.0.2]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@4.0.1...@metamask/notification-controller@4.0.2 +[4.0.1]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@4.0.0...@metamask/notification-controller@4.0.1 +[4.0.0]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@3.1.3...@metamask/notification-controller@4.0.0 +[3.1.3]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@3.1.2...@metamask/notification-controller@3.1.3 +[3.1.2]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@3.1.1...@metamask/notification-controller@3.1.2 +[3.1.1]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@3.1.0...@metamask/notification-controller@3.1.1 +[3.1.0]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@3.0.0...@metamask/notification-controller@3.1.0 +[3.0.0]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@2.0.0...@metamask/notification-controller@3.0.0 +[2.0.0]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@1.0.2...@metamask/notification-controller@2.0.0 +[1.0.2]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@1.0.1...@metamask/notification-controller@1.0.2 +[1.0.1]: https://github.com/MetaMask/core/compare/@metamask/notification-controller@1.0.0...@metamask/notification-controller@1.0.1 +[1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/notification-controller@1.0.0 diff --git a/packages/notification-controller/LICENSE b/packages/notification-controller/LICENSE new file mode 100644 index 0000000000..ddfbecf902 --- /dev/null +++ b/packages/notification-controller/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2018 MetaMask + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE diff --git a/packages/notification-controller/README.md b/packages/notification-controller/README.md new file mode 100644 index 0000000000..fd07ff253b --- /dev/null +++ b/packages/notification-controller/README.md @@ -0,0 +1,15 @@ +# `@metamask/notification-controller` + +Manages display of notifications within MetaMask. + +## Installation + +`yarn add @metamask/notification-controller` + +or + +`npm install @metamask/notification-controller` + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme). diff --git a/packages/notification-controller/jest.config.js b/packages/notification-controller/jest.config.js new file mode 100644 index 0000000000..ca08413339 --- /dev/null +++ b/packages/notification-controller/jest.config.js @@ -0,0 +1,26 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/notification-controller/package.json b/packages/notification-controller/package.json new file mode 100644 index 0000000000..003e07918f --- /dev/null +++ b/packages/notification-controller/package.json @@ -0,0 +1,71 @@ +{ + "name": "@metamask/notification-controller", + "version": "7.0.0", + "description": "Manages display of notifications within MetaMask", + "keywords": [ + "MetaMask", + "Ethereum" + ], + "homepage": "https://github.com/MetaMask/core/tree/main/packages/notification-controller#readme", + "bugs": { + "url": "https://github.com/MetaMask/core/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/core.git" + }, + "license": "MIT", + "sideEffects": false, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "files": [ + "dist/" + ], + "scripts": { + "build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references", + "build:docs": "typedoc", + "changelog:update": "../../scripts/update-changelog.sh @metamask/notification-controller", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/notification-controller", + "publish:preview": "yarn npm publish --tag preview", + "since-latest-release": "../../scripts/since-latest-release.sh", + "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", + "test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache", + "test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose", + "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" + }, + "dependencies": { + "@metamask/base-controller": "^7.0.1", + "@metamask/utils": "^9.3.0", + "nanoid": "^3.1.31" + }, + "devDependencies": { + "@metamask/auto-changelog": "^3.4.4", + "@types/jest": "^27.4.1", + "deepmerge": "^4.2.2", + "jest": "^27.5.1", + "ts-jest": "^27.1.4", + "typedoc": "^0.24.8", + "typedoc-plugin-missing-exports": "^2.0.0", + "typescript": "~5.2.2" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + } +} diff --git a/packages/notification-controller/src/NotificationController.test.ts b/packages/notification-controller/src/NotificationController.test.ts new file mode 100644 index 0000000000..ac1638be2e --- /dev/null +++ b/packages/notification-controller/src/NotificationController.test.ts @@ -0,0 +1,159 @@ +import { ControllerMessenger } from '@metamask/base-controller'; + +import type { + NotificationControllerActions, + NotificationControllerStateChange, +} from './NotificationController'; +import { NotificationController } from './NotificationController'; + +const name = 'NotificationController'; + +/** + * Constructs a unrestricted controller messenger. + * + * @returns A unrestricted controller messenger. + */ +function getUnrestrictedMessenger() { + return new ControllerMessenger< + NotificationControllerActions, + NotificationControllerStateChange + >(); +} + +/** + * Constructs a restricted controller messenger. + * + * @param controllerMessenger - An optional unrestricted messenger + * @returns A restricted controller messenger. + */ +function getRestrictedMessenger( + controllerMessenger = getUnrestrictedMessenger(), +) { + return controllerMessenger.getRestricted({ + name, + allowedActions: [], + allowedEvents: [], + }); +} + +const origin = 'snap_test'; +const message = 'foo'; + +describe('NotificationController', () => { + it('action: NotificationController:show', () => { + const unrestricted = getUnrestrictedMessenger(); + const messenger = getRestrictedMessenger(unrestricted); + + const controller = new NotificationController({ + messenger, + }); + + expect( + unrestricted.call('NotificationController:show', origin, { + message, + }), + ).toBeUndefined(); + + expect( + unrestricted.call('NotificationController:show', origin, { + message, + title: 'title', + interfaceId: '1', + }), + ).toBeUndefined(); + const notifications = Object.values(controller.state.notifications); + expect(notifications).toHaveLength(2); + expect(notifications[0]).toStrictEqual({ + createdDate: expect.any(Number), + id: expect.any(String), + message, + origin, + readDate: null, + expandedView: null, + }); + expect(notifications[1]).toStrictEqual({ + createdDate: expect.any(Number), + id: expect.any(String), + message, + origin, + readDate: null, + expandedView: { title: 'title', interfaceId: '1' }, + }); + }); + + it('action: NotificationController:markViewed', () => { + const unrestricted = getUnrestrictedMessenger(); + const messenger = getRestrictedMessenger(unrestricted); + + const controller = new NotificationController({ + messenger, + }); + + expect( + unrestricted.call('NotificationController:show', origin, { + message, + }), + ).toBeUndefined(); + const notifications = Object.values(controller.state.notifications); + expect(notifications).toHaveLength(1); + expect( + unrestricted.call('NotificationController:markRead', [ + notifications[0].id, + 'foo', + ]), + ).toBeUndefined(); + + const newNotifications = Object.values(controller.state.notifications); + expect(newNotifications).toContainEqual({ + ...notifications[0], + readDate: expect.any(Number), + }); + + expect(newNotifications).toHaveLength(1); + }); + + it('action: NotificationController:dismiss', () => { + const unrestricted = getUnrestrictedMessenger(); + const messenger = getRestrictedMessenger(unrestricted); + + const controller = new NotificationController({ + messenger, + }); + + expect( + unrestricted.call('NotificationController:show', origin, { + message, + }), + ).toBeUndefined(); + const notifications = Object.values(controller.state.notifications); + expect(notifications).toHaveLength(1); + expect( + unrestricted.call('NotificationController:dismiss', [ + notifications[0].id, + 'foo', + ]), + ).toBeUndefined(); + + expect(Object.values(controller.state.notifications)).toHaveLength(0); + }); + + it('action: NotificationController:clear', () => { + const unrestricted = getUnrestrictedMessenger(); + const messenger = getRestrictedMessenger(unrestricted); + + const controller = new NotificationController({ + messenger, + }); + + expect( + unrestricted.call('NotificationController:show', origin, { + message, + }), + ).toBeUndefined(); + const notifications = Object.values(controller.state.notifications); + expect(notifications).toHaveLength(1); + expect(unrestricted.call('NotificationController:clear')).toBeUndefined(); + + expect(Object.values(controller.state.notifications)).toHaveLength(0); + }); +}); diff --git a/packages/notification-controller/src/NotificationController.ts b/packages/notification-controller/src/NotificationController.ts new file mode 100644 index 0000000000..31fb22eca9 --- /dev/null +++ b/packages/notification-controller/src/NotificationController.ts @@ -0,0 +1,219 @@ +import type { + ControllerGetStateAction, + ControllerStateChangeEvent, + RestrictedControllerMessenger, +} from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller'; +import { hasProperty } from '@metamask/utils'; +import { nanoid } from 'nanoid'; + +/** + * @typedef NotificationControllerState + * @property notifications - Stores existing notifications to be shown in the UI + */ +export type NotificationControllerState = { + notifications: Record; +}; + +/** + * @typedef Notification - Stores information about in-app notifications, to be shown in the UI + * @property id - A UUID that identifies the notification + * @property origin - The origin that requested the notification + * @property createdDate - The notification creation date in milliseconds elapsed since the UNIX epoch + * @property readDate - The notification read date in milliseconds elapsed since the UNIX epoch or null if unread + * @property message - The notification message + */ +export type Notification = { + id: string; + origin: string; + createdDate: number; + readDate: number | null; + message: string; +}; + +/** + * @typedef NotificationOptions - Notification data to be used to display in the UI + * @property message - The notification message + * @property title - The notification's title displayed in expanded view + * @property interfaceId - The interface id of the content to be displayed in expanded view + * @property footerLink - An object holding link information to be used in the expanded view + */ +export type NotificationOptions = { + message: string; + title?: string; + interfaceId?: string; + footerLink?: { href: string; text: string }; +}; + +const name = 'NotificationController'; + +export type NotificationControllerStateChange = ControllerStateChangeEvent< + typeof name, + NotificationControllerState +>; + +export type GetNotificationControllerState = ControllerGetStateAction< + typeof name, + NotificationControllerState +>; + +export type ShowNotification = { + type: `${typeof name}:show`; + handler: NotificationController['show']; +}; + +export type DismissNotification = { + type: `${typeof name}:dismiss`; + handler: NotificationController['dismiss']; +}; + +export type MarkNotificationRead = { + type: `${typeof name}:markRead`; + handler: NotificationController['markRead']; +}; + +export type ClearNotifications = { + type: `${typeof name}:clear`; + handler: NotificationController['clear']; +}; + +export type NotificationControllerActions = + | GetNotificationControllerState + | ShowNotification + | DismissNotification + | MarkNotificationRead + | ClearNotifications; + +export type NotificationControllerMessenger = RestrictedControllerMessenger< + typeof name, + NotificationControllerActions, + NotificationControllerStateChange, + never, + never +>; + +const metadata = { + notifications: { persist: true, anonymous: false }, +}; + +const defaultState = { + notifications: {}, +}; + +/** + * Controller that handles storing notifications and showing them to the user + */ +export class NotificationController extends BaseController< + typeof name, + NotificationControllerState, + NotificationControllerMessenger +> { + /** + * Creates a NotificationController instance. + * + * @param options - Constructor options. + * @param options.messenger - A reference to the messaging system. + * @param options.state - Initial state to set on this controller. + */ + constructor({ + messenger, + state, + }: { + messenger: NotificationControllerMessenger; + state?: Partial; + }) { + super({ + name, + metadata, + messenger, + state: { ...defaultState, ...state }, + }); + + this.messagingSystem.registerActionHandler( + `${name}:show` as const, + (origin: string, options: NotificationOptions) => + this.show(origin, options), + ); + + this.messagingSystem.registerActionHandler( + `${name}:dismiss` as const, + (ids: string[]) => this.dismiss(ids), + ); + + this.messagingSystem.registerActionHandler( + `${name}:markRead` as const, + (ids: string[]) => this.markRead(ids), + ); + + this.messagingSystem.registerActionHandler(`${name}:clear` as const, () => + this.clear(), + ); + } + + /** + * Shows a notification. + * + * @param origin - The origin trying to send a notification + * @param options - Notification args object + * @param options.title - The title to show in an expanded view + * @param options.interfaceId - A interface id for snap content + * @param options.footerLink - Footer object + * @param options.footerLink.href - Footer href + * @param options.footerLink.text - Link text + */ + show(origin: string, options: NotificationOptions) { + const id = nanoid(); + const { message, ...expandedView } = options; + const notification = { + id, + origin, + createdDate: Date.now(), + readDate: null, + message, + expandedView: Object.keys(expandedView).length > 0 ? expandedView : null, + }; + this.update((state) => { + state.notifications[id] = notification; + }); + } + + /** + * Dimisses a list of notifications. + * + * @param ids - A list of notification IDs + */ + dismiss(ids: string[]) { + this.update((state) => { + for (const id of ids) { + if (hasProperty(state.notifications, id)) { + delete state.notifications[id]; + } + } + }); + } + + /** + * Marks a list of notifications as read. + * + * @param ids - A list of notification IDs + */ + markRead(ids: string[]) { + this.update((state) => { + for (const id of ids) { + if (hasProperty(state.notifications, id)) { + state.notifications[id].readDate = Date.now(); + } + } + }); + } + + /** + * Clears the state of the controller, removing all notifications. + * + */ + clear() { + this.update(() => { + return { ...defaultState }; + }); + } +} diff --git a/packages/notification-controller/src/index.ts b/packages/notification-controller/src/index.ts new file mode 100644 index 0000000000..6c896d4826 --- /dev/null +++ b/packages/notification-controller/src/index.ts @@ -0,0 +1 @@ +export * from './NotificationController'; diff --git a/packages/notification-controller/tsconfig.build.json b/packages/notification-controller/tsconfig.build.json new file mode 100644 index 0000000000..e5fd7422b9 --- /dev/null +++ b/packages/notification-controller/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist", + "rootDir": "./src" + }, + "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "include": ["../../types", "./src"] +} diff --git a/packages/notification-controller/tsconfig.json b/packages/notification-controller/tsconfig.json new file mode 100644 index 0000000000..34354c4b09 --- /dev/null +++ b/packages/notification-controller/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "references": [{ "path": "../base-controller" }], + "include": ["../../types", "./src"] +} diff --git a/packages/notification-controller/typedoc.json b/packages/notification-controller/typedoc.json new file mode 100644 index 0000000000..c9da015dbf --- /dev/null +++ b/packages/notification-controller/typedoc.json @@ -0,0 +1,7 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs", + "tsconfig": "./tsconfig.build.json" +} diff --git a/tsconfig.json b/tsconfig.json index 8a188ea3f2..f886671a63 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,7 @@ { "path": "./packages/message-manager" }, { "path": "./packages/name-controller" }, { "path": "./packages/network-controller" }, + { "path": "./packages/notification-controller" }, { "path": "./packages/notification-services-controller" }, { "path": "./packages/permission-controller" }, { "path": "./packages/permission-log-controller" },