-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
16dec34
commit f3648c5
Showing
4 changed files
with
201 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -216,5 +216,8 @@ | |
"preset": "angular" | ||
} | ||
} | ||
}, | ||
"dependencies": { | ||
"rxjs": "^7.8.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,75 @@ | ||
export const NotificationManager = {} | ||
import {BehaviorSubject} from 'rxjs' | ||
|
||
export enum NotificationTrigger { | ||
'TransactionReceived' = 'TransactionReceived', | ||
'RewardsUpdated' = 'RewardsUpdated', | ||
'PrimaryTokenPriceChanged' = 'PrimaryTokenPriceChanged', | ||
} | ||
|
||
export interface NotificationTransactionReceivedEvent | ||
extends NotificationEventBase { | ||
trigger: NotificationTrigger.TransactionReceived | ||
metadata: { | ||
previousTxsCounter: number | ||
nextTxsCounter: number | ||
txId: string | ||
isSentByUser: boolean // check local pending | ||
} | ||
} | ||
|
||
export type Group = 'transaction-history' | 'portfolio' | ||
|
||
type NotificationTriggerPerGroup = { | ||
'transaction-history': | ||
| NotificationTrigger.TransactionReceived | ||
| NotificationTrigger.RewardsUpdated | ||
'portfolio': NotificationTrigger.PrimaryTokenPriceChanged | ||
} | ||
|
||
export type NotificationEvent = NotificationTransactionReceivedEvent | ||
|
||
type NotificationEventId = string | ||
|
||
interface NotificationEventBase { | ||
id: string // uuid | ||
date: number | ||
isRead: boolean | ||
} | ||
|
||
export type NotificationConfig = { | ||
[NotificationTrigger.PrimaryTokenPriceChanged]: { | ||
notify: boolean | ||
threshold: number | ||
interval: '24h' | '1h' | ||
} | ||
[NotificationTrigger.TransactionReceived]: { | ||
notify: boolean | ||
} | ||
[NotificationTrigger.RewardsUpdated]: { | ||
notify: boolean | ||
} | ||
} | ||
|
||
export type NotificationManager = { | ||
hydrate: () => void // build up subscriptions | ||
unreadCounterByGroup$: BehaviorSubject<Readonly<Map<Group, number>>> | ||
|
||
// NOTE: events represent a notification event that was trigger by a config rule | ||
events: { | ||
markAllAsRead: () => void | ||
markAsRead(id: NotificationEventId): void | ||
read: () => Promise<ReadonlyArray<NotificationEvent>> | ||
save: (event: Readonly<NotificationEvent>) => void | ||
clear: () => void | ||
} | ||
// NOTE: config sets the ground to what, when, and if should notify user | ||
config: { | ||
read: () => Promise<Readonly<NotificationConfig>> // return initial if empty | ||
save: (config: Readonly<NotificationConfig>) => Promise<void> | ||
reset: () => Promise<void> | ||
} | ||
destroy: () => void // tear down subscriptions | ||
clear: () => void | ||
} | ||
|
||
// TODO: Add trackers - these are only pointers to last notification event |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,19 @@ | ||
import {NotificationManager} from './index' | ||
import {notificationManagerMaker} from './notification-manager' | ||
|
||
describe('NotificationManager', () => { | ||
it('should be defined', () => { | ||
expect(NotificationManager).toBeDefined() | ||
expect(notificationManagerMaker()).toBeDefined() | ||
}) | ||
|
||
it('should return default config if not set', async () => { | ||
const manager = notificationManagerMaker() | ||
const config = await manager.config.read() | ||
expect(config).toEqual({ | ||
PrimaryTokenPriceChanged: { | ||
interval: '24h', | ||
notify: true, | ||
threshold: 10, | ||
}, | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { | ||
Group, | ||
NotificationConfig, | ||
NotificationEvent, | ||
NotificationManager, | ||
NotificationTrigger, | ||
} from './index' | ||
import {BehaviorSubject} from 'rxjs' | ||
import {App} from '@yoroi/types' | ||
|
||
type NotificationManagerMakerProps = { | ||
eventsStorage: App.Storage<true, string> | ||
configStorage: App.Storage<true, string> | ||
} | ||
|
||
type EventsStorageData = ReadonlyArray<NotificationEvent> | ||
type ConfigStorageData = NotificationConfig | ||
|
||
export const notificationManagerMaker = ({ | ||
eventsStorage, | ||
configStorage, | ||
}: NotificationManagerMakerProps): NotificationManager => { | ||
const hydrate = () => {} | ||
|
||
const events = eventsManagerMaker({storage: eventsStorage}) | ||
const config = configManagerMaker({storage: configStorage}) | ||
|
||
const destroy = () => {} | ||
|
||
const clear = async () => { | ||
await config.reset() | ||
await events.clear() | ||
} | ||
|
||
const unreadCounterByGroup$ = new BehaviorSubject<Map<Group, number>>( | ||
new Map<Group, number>(), | ||
) | ||
|
||
return {hydrate, clear, destroy, events, config, unreadCounterByGroup$} | ||
} | ||
|
||
const eventsManagerMaker = ({ | ||
storage, | ||
}: { | ||
storage: App.Storage<true, string> | ||
}): NotificationManager['events'] => { | ||
const events = { | ||
markAllAsRead: async () => { | ||
const allEvents = await events.read() | ||
const modifiedEvents = allEvents.map((event) => ({ | ||
...event, | ||
isRead: true, | ||
})) | ||
await storage.setItem<EventsStorageData>('events', modifiedEvents) | ||
}, | ||
markAsRead: async (id: string) => { | ||
const allEvents = await events.read() | ||
const modifiedEvents = allEvents.map((event) => | ||
event.id === id ? {...event, isRead: true} : event, | ||
) | ||
await storage.setItem<EventsStorageData>('events', modifiedEvents) | ||
}, | ||
read: async (): Promise<EventsStorageData> => { | ||
return (await storage.getItem<EventsStorageData>('events')) ?? [] | ||
}, | ||
save: async (event: Readonly<NotificationEvent>) => { | ||
const allEvents = await events.read() | ||
await storage.setItem('events', [...allEvents, event]) | ||
}, | ||
clear: (): Promise<void> => { | ||
return storage.removeItem('events') | ||
}, | ||
} | ||
return events | ||
} | ||
|
||
const configManagerMaker = ({ | ||
storage, | ||
}: { | ||
storage: App.Storage<true, string> | ||
}): NotificationManager['config'] => { | ||
return { | ||
read: async (): Promise<NotificationConfig> => { | ||
return ( | ||
(await storage.getItem<ConfigStorageData>('config')) ?? defaultConfig | ||
) | ||
}, | ||
save: async (config: NotificationConfig): Promise<void> => { | ||
await storage.setItem('config', config) | ||
}, | ||
reset: async (): Promise<void> => { | ||
return storage.removeItem('config') | ||
}, | ||
} | ||
} | ||
|
||
const defaultConfig: NotificationConfig = { | ||
[NotificationTrigger.PrimaryTokenPriceChanged]: { | ||
notify: true, | ||
threshold: 10, | ||
interval: '24h', | ||
}, | ||
[NotificationTrigger.TransactionReceived]: { | ||
notify: true, | ||
}, | ||
[NotificationTrigger.RewardsUpdated]: { | ||
notify: true, | ||
}, | ||
} |