-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add startup notificaitons defined by remote GH source
- Loading branch information
Showing
10 changed files
with
250 additions
and
32 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 |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Extension metadata | ||
|
||
**DO NOT DELETE THIS FOLDER UNLESS YOU KNOW WHAT YOU ARE DOING** | ||
|
||
This folder contains remotely-updated metadata to provide updates to the Cadence VSCode Extension without requiring a new release of the extension itself. When consuming this metadata, the latest commit to the default repository branch should be assumed to be the latest version of the extension metadata. | ||
|
||
Currently, it is only used by the Cadence VSCode Extension to fetch any notifications that should be displayed to the user. | ||
|
||
## Notfications schema | ||
|
||
```ts | ||
interface Notification { | ||
_type: 'Notification' | ||
id: string | ||
type: 'error' | 'info' | 'warning' | ||
text: string | ||
buttons?: Array<{ | ||
label: string | ||
link: string | ||
}> | ||
suppressable?: boolean | ||
compatibility?: { | ||
'vscode-cadence'?: string | ||
'flow-cli'?: string | ||
} | ||
} | ||
``` | ||
|
||
### Fields | ||
|
||
- `_type`: The type of the object. Should always be `"Notification"`. | ||
- `id`: A unique identifier for the notification. This is used to determine if the notification has already been displayed to the user. | ||
- `type`: The type of notification. Can be `"info"`, `"warning"`, or `"error"`. | ||
- `text`: The text to display to the user. | ||
- `buttons`: An array of buttons to display to the user. Each button should have a `text` field and a `link` field. The `link` field should be a URL to open when the button is clicked. | ||
- `suppressable`: Whether or not the user should be able to suppress the notification. (defaults to `true`) | ||
- `compatibility`: An object containing compatibility information for the notification. If all of the specified compatibility requirements are met, the notification will be displayed to the user. If not, the notification will be ignored. The following compatibility requirements are supported: | ||
- `vscode-cadence`: The version of the Cadence VSCode Extension that the user must be running. Can be a specific version number (e.g. `"0.0.1"`) or a semver range (e.g. `"^0.0.1"`). | ||
- `flow-cli`: The version of the Flow CLI that the user must be running. Can be a specific version number (e.g. `"0.25.0"`) or a semver range (e.g. `"^0.25.0"`). |
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,19 @@ | ||
[ | ||
{ | ||
"_type": "Notification", | ||
"id": "1", | ||
"type": "info", | ||
"text": "Cadence 1.0 pre-release builds are now available! Developers should begin upgrading their projects - see the Cadence 1.0 Upgrade Plan for more details.", | ||
"buttons": [ | ||
{ | ||
"text": "Learn More", | ||
"link": "https://forum.flow.com/t/cadence-1-0-upgrade-plan/5477#what-does-it-mean-for-me-if-i-am-a-2" | ||
} | ||
], | ||
"suppressable": true, | ||
"versions": { | ||
"vscode-cadence": "*", | ||
"flow-cli": "*" | ||
} | ||
} | ||
] |
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
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
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
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,21 @@ | ||
import { Memento } from 'vscode' | ||
|
||
interface State { | ||
dismissedNotifications: string[] | ||
} | ||
|
||
export class StorageProvider { | ||
#globalState: Memento | ||
|
||
constructor (globalState: Memento) { | ||
this.#globalState = globalState | ||
} | ||
|
||
get<T extends keyof State>(key: T, fallback: State[T]): State[T] { | ||
return this.#globalState.get(key, fallback) | ||
} | ||
|
||
async set<T extends keyof State>(key: T, value: State[T]): Promise<void> { | ||
return await (this.#globalState.update(key, value) as Promise<void>) | ||
} | ||
} |
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,96 @@ | ||
import { StorageProvider } from '../storage/storage-provider' | ||
import { promptUserErrorMessage, promptUserInfoMessage, promptUserWarningMessage } from './prompts' | ||
import * as vscode from 'vscode' | ||
import * as semver from 'semver' | ||
|
||
const NOTIFICATIONS_URL = 'https://raw.githubusercontent.com/onflow/vscode-cadence/.metadata/notifications.json' | ||
|
||
export interface Notification { | ||
_type: 'Notification' | ||
id: string | ||
type: 'error' | 'info' | 'warning' | ||
text: string | ||
buttons?: Array<{ | ||
label: string | ||
link: string | ||
}> | ||
suppressable?: boolean | ||
compatibility?: { | ||
'vscode-cadence'?: string | ||
'flow-cli'?: string | ||
} | ||
} | ||
|
||
export function displayNotifications (notifications: Notification[], storageProvider: StorageProvider): void { | ||
notifications.forEach(notification => { | ||
displayNotification(notification, storageProvider) | ||
}) | ||
} | ||
|
||
export function displayNotification (notification: Notification, storageProvider: StorageProvider): void { | ||
const transformButton = (button: { label: string, link: string }) => { | ||
return { | ||
label: button.label, | ||
callback: () => { | ||
void vscode.env.openExternal(vscode.Uri.parse(button.link)) | ||
} | ||
} | ||
} | ||
const transformButtons = (buttons?: Array<{ label: string, link: string }>): Array<{ label: string, callback: () => void }> => { | ||
return [{ | ||
label: 'Don\'t show again', | ||
callback: () => { | ||
dismissNotification(notification, storageProvider) | ||
} | ||
}].concat(buttons?.map(transformButton) ?? []) | ||
} | ||
|
||
if (notification.type === 'error') { | ||
promptUserErrorMessage(notification.text, transformButtons(notification.buttons)) | ||
} else if (notification.type === 'info') { | ||
promptUserInfoMessage(notification.text, transformButtons(notification.buttons)) | ||
} else if (notification.type === 'warning') { | ||
promptUserWarningMessage(notification.text, transformButtons(notification.buttons)) | ||
} | ||
} | ||
|
||
export function filterNotifications (notifications: Notification[], storageProvider: StorageProvider, currentVersions: { 'vscode-cadence': string, 'flow-cli': string }): Notification[] { | ||
return notifications.filter(notification => { | ||
if (notification.suppressable && isNotificationDismissed(notification, storageProvider)) { | ||
return false | ||
} | ||
|
||
// Check compatibility filters | ||
const satisfies = (version: string, range?: string): boolean => { | ||
if (range == null) return true | ||
return semver.satisfies(version, range, { includePrerelease: true }) | ||
} | ||
const allSatisfied = Object.keys(currentVersions).every((key) => { | ||
return satisfies(currentVersions[key as keyof typeof currentVersions], notification.compatibility?.[key as keyof typeof notification.compatibility]) | ||
}) | ||
|
||
if (!allSatisfied) { | ||
return false | ||
} | ||
|
||
return true | ||
}) | ||
} | ||
|
||
export async function fetchNotifications (filterNotifications: (notifications: Notification[]) => Notification[]): Promise<Notification[]> { | ||
return await fetch(NOTIFICATIONS_URL).then(async res => await res.json()).then((notifications: Notification[]) => { | ||
return filterNotifications(notifications) | ||
}).catch(() => { | ||
return [] | ||
}) | ||
} | ||
|
||
export function dismissNotification (notification: Notification, storageProvider: StorageProvider): void { | ||
const dismissedNotifications = storageProvider.get('dismissedNotifications', []) | ||
void storageProvider.set('dismissedNotifications', [...dismissedNotifications, notification.id]) | ||
} | ||
|
||
export function isNotificationDismissed (notification: Notification, storageProvider: StorageProvider): boolean { | ||
const dismissedNotifications = storageProvider.get('dismissedNotifications', []) | ||
return dismissedNotifications.includes(notification.id) | ||
} |
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,24 +1,43 @@ | ||
/* Information and error prompts */ | ||
import { window } from 'vscode' | ||
|
||
export function promptUserInfoMessage (message: string, buttonText: string, callback: Function): void { | ||
export interface PromptButton { | ||
label: string | ||
callback: Function | ||
} | ||
|
||
export function promptUserInfoMessage (message: string, buttons: PromptButton[] = []): void { | ||
window.showInformationMessage( | ||
message, | ||
buttonText | ||
...buttons.map((button) => button.label) | ||
).then((choice) => { | ||
if (choice === buttonText) { | ||
callback() | ||
const button = buttons.find((button) => button.label === choice) | ||
if (button != null) { | ||
button.callback() | ||
} | ||
}, () => {}) | ||
} | ||
|
||
export function promptUserErrorMessage (message: string, buttonText: string, callback: Function): void { | ||
export function promptUserErrorMessage (message: string, buttons: PromptButton[] = []): void { | ||
window.showErrorMessage( | ||
message, | ||
buttonText | ||
...buttons.map((button) => button.label) | ||
).then((choice) => { | ||
const button = buttons.find((button) => button.label === choice) | ||
if (button != null) { | ||
button.callback() | ||
} | ||
}, () => {}) | ||
} | ||
|
||
export function promptUserWarningMessage (message: string, buttons: PromptButton[] = []): void { | ||
window.showWarningMessage( | ||
message, | ||
...buttons.map((button) => button.label) | ||
).then((choice) => { | ||
if (choice === buttonText) { | ||
callback() | ||
const button = buttons.find((button) => button.label === choice) | ||
if (button != null) { | ||
button.callback() | ||
} | ||
}, () => {}) | ||
} |
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