feat: New 'What's New' section in Preferences (#2049) (skip e2e)

This commit is contained in:
Mo 2022-11-24 05:46:44 -06:00 committed by GitHub
parent c40b17bd4c
commit 0a01ddb430
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 10100 additions and 23 deletions

View file

@ -27,14 +27,14 @@ jobs:
cache: 'yarn'
- run: yarn install --immutable
- run: yarn build:desktop
- run: echo APP_VERSION=$(node -p "require('./package.json').version") >> $GITHUB_ENV
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- name: Compile Mac
run: yarn run webpack --config desktop.webpack.prod.js
- name: MacX64
run: |
yarn run electron-builder --mac --x64 --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
node scripts/fixMacZip.js
node scripts/fixMacZip.js ${{ env.APP_VERSION }}
- name: MacArm64
run: yarn run electron-builder --mac --arm64 --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
@ -69,7 +69,7 @@ jobs:
- run: yarn install --immutable
- run: yarn build:desktop
- run: echo APP_VERSION=$(node -p "require('./package.json').version") >> $GITHUB_ENV
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- name: Compile for AppImage
run: yarn run webpack --config desktop.webpack.prod.js
@ -139,7 +139,7 @@ jobs:
- run: yarn install --immutable
- run: yarn build:desktop
- run: yarn run webpack --config desktop.webpack.prod.js
- run: echo APP_VERSION=$(node -p "require('./package.json').version") >> $GITHUB_ENV
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- run: yarn run electron-builder --windows --x64 --ia32 --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: Upload
@ -160,6 +160,8 @@ jobs:
run:
working-directory: packages/desktop
steps:
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
@ -194,8 +196,8 @@ jobs:
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.CI_PAT_TOKEN }}
tag_name: "@standardnotes/desktop@${{ steps.package-version.outputs.current-version}}"
name: "Desktop ${{ steps.package-version.outputs.current-version }}"
tag_name: "@standardnotes/desktop@${{ env.APP_VERSION }}"
name: "Desktop ${{ env.APP_VERSION }}"
body: ${{ steps.release-notes.outputs.result }}
prerelease: true
draft: false
@ -214,4 +216,4 @@ jobs:
sudo snap install snapcraft --classic
echo "${{ secrets.SNAPCRAFT_LOGIN_FILE }}" >> snapauth.txt
snapcraft login --with=snapauth.txt
snapcraft upload dist/standard-notes-${{ steps.package-version.outputs.current-version}}-linux-amd64.snap --release stable,candidate,beta,edge
snapcraft upload dist/standard-notes-${{ env.APP_VERSION }}-linux-amd64.snap --release stable,candidate,beta,edge

View file

@ -23,7 +23,7 @@ jobs:
- run: yarn install --immutable
- run: yarn build:desktop
- run: yarn run webpack --config desktop.webpack.prod.js
- run: echo APP_VERSION=$(node -p "require('./package.json').version") >> $GITHUB_ENV
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- run: yarn run electron-builder --windows --x64 --ia32 --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: Upload

View file

@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v3
- name: Export version from package.json
run:
echo "PACKAGE_VERSION=$(grep '"version"' package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
echo "PACKAGE_VERSION=$(grep '"version"' ../web/package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
- name: Setup react-native kernel and increase watchers
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
- name: Decode Production Android keystore
@ -82,7 +82,7 @@ jobs:
uses: actions/checkout@v3
- name: Export version from package.json
run:
echo "PACKAGE_VERSION=$(grep '"version"' package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
echo "PACKAGE_VERSION=$(grep '"version"' ../web/package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
- name: Install dependencies
run: yarn install --immutable && yarn install:pods
- run: yarn build:mobile

View file

@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v3
- name: Export version from package.json
run: |
echo "PACKAGE_VERSION=$(grep '"version"' package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
echo "PACKAGE_VERSION=$(grep '"version"' ../web/package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
- name: Setup react-native kernel and increase watchers
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
- name: Decode Dev Android keystore
@ -61,7 +61,7 @@ jobs:
uses: actions/checkout@v3
- name: Export version from package.json
run: |
echo "PACKAGE_VERSION=$(grep '"version"' package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
echo "PACKAGE_VERSION=$(grep '"version"' ../web/package.json | cut -d '"' -f 4 | cut -d "-" -f 1)" >> $GITHUB_ENV
- name: Ruby Setup for Fastlane
uses: ruby/setup-ruby@v1
- name: Install dependencies

View file

@ -31,7 +31,8 @@
"reset": "find . -type dir -name node_modules | xargs rm -rf && rm -rf yarn.lock && yarn install",
"release:prod": "lerna version --conventional-commits --yes -m \"chore(release): publish\"",
"publish:prod": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
"version": "yarn install --no-immutable && git add yarn.lock",
"version": "yarn install --no-immutable && git add yarn.lock && yarn changelog:json",
"changelog:json": "node scripts/ChangelogToJson.js && git add .",
"postversion": "./scripts/push-tags-one-by-one.sh",
"workspace:list": " yarn lerna list -all",
"upgrade:snjs": "ncu -u '@standardnotes/*' && yarn workspaces foreach --verbose run upgrade:snjs"

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.101.2",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,

View file

@ -31,7 +31,7 @@ async function getBlockMapInfo(fileName) {
;(async () => {
try {
const { version } = JSON.parse(await fs.promises.readFile('package.json'))
const version = process.argv.slice(2)[0]
const zipName = `standard-notes-${version}-mac-x64.zip`
const zipPath = `dist/${zipName}`
console.log(`Removing ${zipPath}`)

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
{
"name": "@standardnotes/mobile",
"version": "3.46.31",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",

View file

@ -4,7 +4,6 @@ import { Keyboard, Platform } from 'react-native'
import VersionInfo from 'react-native-version-info'
import { WebView, WebViewMessageEvent } from 'react-native-webview'
import { OnShouldStartLoadWithRequest } from 'react-native-webview/lib/WebViewTypes'
import pjson from '../package.json'
import { AndroidBackHandlerService } from './AndroidBackHandlerService'
import { AppStateObserverService } from './AppStateObserverService'
import { ColorSchemeObserverService } from './ColorSchemeObserverService'
@ -129,7 +128,7 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
const WebProcessDeviceInterface = `
class WebProcessDeviceInterface {
constructor(messageSender) {
this.appVersion = '${pjson.version} (${VersionInfo.buildVersion})'
this.appVersion = '${VersionInfo.appVersion} (${VersionInfo.buildVersion})'
this.environment = 3
this.platform = ${device.platform}
this.databases = []

View file

@ -5,13 +5,13 @@ import { writeJson, ensureDirExists } from '../../../scripts/ScriptUtils.mjs'
const CdnUrl = 'https://github.com/standardnotes/app/releases/download/'
const DesktopPackageName = '@standardnotes/desktop'
const DesktopVersion = Desktop.version
const DesktopVersion = Web.version
const BaseFileName = `standard-notes-${DesktopVersion}`
const ReleaseUrl = `${CdnUrl}${DesktopPackageName}@${DesktopVersion}/${BaseFileName}`.replaceAll('@', '%40')
const Versions = {
[Desktop.name]: Desktop.version,
[Mobile.name]: Mobile.version,
[Desktop.name]: Web.version,
[Mobile.name]: Web.version,
[Web.name]: Web.version,
}

View file

@ -39,6 +39,7 @@ export enum StorageKey {
DeinitMode = 'deinit_mode',
CodeVerifier = 'code_verifier',
LaunchPriorityUuids = 'launch_priority_uuids',
LastReadChangelogVersion = 'last_read_changelog_version',
}
export enum NonwrappedStorageKey {

View file

@ -291,6 +291,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.alertService
}
public get storage(): ExternalServices.StorageServiceInterface {
return this.diskStorageService
}
public computePrivateUsername(username: string): Promise<string | undefined> {
return ComputePrivateUsername(this.options.crypto, username)
}

View file

@ -0,0 +1,13 @@
export type ChangelogVersion = {
version: string | null
title: string
date: string | null
body: string
parsed: Record<string, string[]>
}
export interface Changelog {
title: string
description: string
versions: Array<ChangelogVersion>
}

View file

@ -0,0 +1,78 @@
import { Environment } from '@standardnotes/models'
import { StorageServiceInterface, StorageKey } from '@standardnotes/services'
import { Changelog, ChangelogVersion } from './Changelog'
import { ChangelogServiceInterface } from './ChangelogServiceInterface'
import { LegacyWebToDesktopVersionMapping } from './LegacyDesktopMapping'
import { LegacyWebToMobileVersionMapping } from './LegacyMobileMapping'
const WebChangelogUrl = 'https://raw.githubusercontent.com/standardnotes/app/main/packages/web/CHANGELOG.md.json'
const DesktopDownloadsUrlBase = 'https://github.com/standardnotes/app/releases/tag/%40standardnotes%2Fdesktop%40'
export class ChangelogService implements ChangelogServiceInterface {
private changeLog?: Changelog
constructor(private environment: Environment, private diskService: StorageServiceInterface) {}
private async performDownloadChangelog(): Promise<Changelog> {
const response = await fetch(WebChangelogUrl)
const changelog = await response.text()
const parsedData = JSON.parse(changelog)
return parsedData
}
public async getChangelog(): Promise<Changelog> {
if (this.changeLog) {
return this.changeLog
}
this.changeLog = await this.performDownloadChangelog()
if (this.environment !== Environment.Web) {
const legacyMapping = this.getLegacyMapping()
this.changeLog.versions = this.changeLog.versions.map((versionRecord) => {
const versionString = versionRecord.version || ''
return {
...versionRecord,
version: legacyMapping[versionString] || versionRecord.version,
}
})
}
return this.changeLog
}
public markAsRead(): void {
if (!this.changeLog) {
return
}
this.diskService.setValue(StorageKey.LastReadChangelogVersion, this.changeLog.versions[0].version)
}
public getLastReadVersion(): string | undefined {
return this.diskService.getValue(StorageKey.LastReadChangelogVersion)
}
public async getVersions(): Promise<ChangelogVersion[]> {
const changelog = await this.getChangelog()
return changelog.versions
}
private getLegacyMapping(): Record<string, string> {
return this.environment === Environment.Desktop
? LegacyWebToDesktopVersionMapping
: this.environment === Environment.Mobile
? LegacyWebToMobileVersionMapping
: {}
}
public getDesktopDownloadsUrl(version: string): string {
return DesktopDownloadsUrlBase + version
}
public getDesktopVersionForWebVersion(webVersion: string): string {
return LegacyWebToDesktopVersionMapping[webVersion] ?? webVersion
}
}

View file

@ -0,0 +1,10 @@
import { Changelog, ChangelogVersion } from './Changelog'
export interface ChangelogServiceInterface {
getChangelog(): Promise<Changelog>
getVersions(): Promise<ChangelogVersion[]>
getDesktopDownloadsUrl(version: string): string
getDesktopVersionForWebVersion(webVersion: string): string
markAsRead(): void
getLastReadVersion(): string | undefined
}

View file

@ -0,0 +1,351 @@
/**
* Before 11/23/2022, web and desktop used independent semantic versioning. After this point, desktop automatically
* uses web's versioning. The mapping below allows us to see what particular web version a legacy desktop version maps to
*/
export const LegacyWebToDesktopVersionMapping: Record<string, string> = {
'3.107.0': '3.101.2',
'3.106.0': '3.101.1',
'3.105.0': '3.101.0',
'3.104.1': '3.100.18',
'3.104.0': '3.100.17',
'3.103.2': '3.100.16',
'3.103.1': '3.100.15',
'3.103.0': '3.100.14',
'3.102.0': '3.100.13',
'3.101.2': '3.100.12',
'3.101.1': '3.100.11',
'3.101.0': '3.100.10',
'3.100.8': '3.100.9',
'3.100.7': '3.100.8',
'3.100.6': '3.100.7',
'3.100.5': '3.100.6',
'3.100.4': '3.100.5',
'3.100.3': '3.100.4',
'3.100.2': '3.100.3',
'3.100.1': '3.100.2',
'3.100.0': '3.100.1',
'3.99.0': '3.100.0',
'3.98.2': '3.23.301',
'3.98.1': '3.23.300',
'3.98.0': '3.23.299',
'3.97.0': '3.23.298',
'3.96.1': '3.23.297',
'3.96.0': '3.23.296',
'3.95.1': '3.23.295',
'3.95.0': '3.23.294',
'3.94.2': '3.23.293',
'3.94.1': '3.23.292',
'3.94.0': '3.23.291',
'3.93.19': '3.23.290',
'3.93.18': '3.23.289',
'3.93.17': '3.23.288',
'3.93.16': '3.23.287',
'3.93.15': '3.23.286',
'3.93.14': '3.23.285',
'3.93.13': '3.23.284',
'3.93.12': '3.23.283',
'3.93.11': '3.23.282',
'3.93.10': '3.23.281',
'3.93.9': '3.23.280',
'3.93.8': '3.23.279',
'3.93.7': '3.23.278',
'3.93.6': '3.23.277',
'3.93.5': '3.23.276',
'3.93.4': '3.23.275',
'3.93.3': '3.23.274',
'3.93.2': '3.23.273',
'3.93.1': '3.23.272',
'3.93.0': '3.23.271',
'3.92.0': '3.23.270',
'3.91.1': '3.23.269',
'3.91.0': '3.23.268',
'3.90.11': '3.23.267',
'3.90.10': '3.23.266',
'3.90.9': '3.23.265',
'3.90.8': '3.23.264',
'3.90.7': '3.23.263',
'3.90.6': '3.23.262',
'3.90.5': '3.23.261',
'3.90.4': '3.23.260',
'3.90.3': '3.23.259',
'3.90.2': '3.23.258',
'3.90.1': '3.23.257',
'3.90.0': '3.23.256',
'3.89.0': '3.23.255',
'3.88.1': '3.23.254',
'3.88.0': '3.23.253',
'3.87.2': '3.23.252',
'3.87.1': '3.23.251',
'3.87.0': '3.23.250',
'3.86.0': '3.23.249',
'3.85.2': '3.23.248',
'3.85.1': '3.23.247',
'3.85.0': '3.23.246',
'3.84.7': '3.23.245',
'3.84.6': '3.23.244',
'3.84.5': '3.23.243',
'3.84.4': '3.23.242',
'3.84.3': '3.23.241',
'3.84.2': '3.23.240',
'3.84.1': '3.23.239',
'3.84.0': '3.23.238',
'3.83.0': '3.23.237',
'3.82.6': '3.23.236',
'3.82.5': '3.23.235',
'3.82.4': '3.23.234',
'3.82.3': '3.23.233',
'3.82.2': '3.23.232',
'3.82.1': '3.23.231',
'3.82.0': '3.23.230',
'3.81.0': '3.23.229',
'3.80.1': '3.23.228',
'3.80.0': '3.23.227',
'3.79.0': '3.23.226',
'3.78.1': '3.23.225',
'3.78.0': '3.23.224',
'3.77.1': '3.23.223',
'3.77.0': '3.23.222',
'3.76.2': '3.23.221',
'3.76.1': '3.23.220',
'3.76.0': '3.23.219',
'3.75.1': '3.23.218',
'3.75.0': '3.23.217',
'3.74.0': '3.23.216',
'3.73.0': '3.23.215',
'3.72.4': '3.23.214',
'3.72.3': '3.23.213',
'3.72.2': '3.23.212',
'3.72.1': '3.23.211',
'3.72.0': '3.23.210',
'3.71.8': '3.23.209',
'3.71.7': '3.23.208',
'3.71.6': '3.23.207',
'3.71.5': '3.23.206',
'3.71.4': '3.23.205',
'3.71.3': '3.23.204',
'3.71.2': '3.23.203',
'3.71.1': '3.23.202',
'3.71.0': '3.23.201',
'3.70.5': '3.23.200',
'3.70.4': '3.23.199',
'3.70.3': '3.23.198',
'3.70.2': '3.23.197',
'3.70.1': '3.23.196',
'3.70.0': '3.23.195',
'3.69.1': '3.23.194',
'3.69.0': '3.23.193',
'3.68.6': '3.23.192',
'3.68.5': '3.23.191',
'3.68.4': '3.23.190',
'3.68.3': '3.23.189',
'3.68.2': '3.23.188',
'3.68.1': '3.23.187',
'3.68.0': '3.23.186',
'3.67.4': '3.23.185',
'3.67.3': '3.23.184',
'3.67.2': '3.23.183',
'3.67.1': '3.23.182',
'3.67.0': '3.23.181',
'3.66.0': '3.23.180',
'3.65.0': '3.23.179',
'3.64.0': '3.23.178',
'3.63.3': '3.23.177',
'3.63.2': '3.23.176',
'3.63.1': '3.23.175',
'3.63.0': '3.23.174',
'3.62.4': '3.23.173',
'3.62.3': '3.23.172',
'3.62.2': '3.23.171',
'3.62.1': '3.23.170',
'3.62.0': '3.23.169',
'3.61.0': '3.23.168',
'3.60.0': '3.23.167',
'3.59.3': '3.23.166',
'3.59.2': '3.23.165',
'3.59.1': '3.23.164',
'3.59.0': '3.23.163',
'3.58.1': '3.23.162',
'3.58.0': '3.23.161',
'3.57.3': '3.23.160',
'3.57.2': '3.23.159',
'3.57.1': '3.23.158',
'3.57.0': '3.23.157',
'3.56.0': '3.23.156',
'3.55.2': '3.23.155',
'3.55.1': '3.23.154',
'3.55.0': '3.23.153',
'3.54.6': '3.23.152',
'3.54.5': '3.23.151',
'3.54.4': '3.23.150',
'3.54.3': '3.23.149',
'3.54.2': '3.23.148',
'3.54.1': '3.23.147',
'3.54.0': '3.23.146',
'3.53.0': '3.23.145',
'3.52.1': '3.23.144',
'3.52.0': '3.23.143',
'3.51.0': '3.23.142',
'3.50.6': '3.23.141',
'3.50.5': '3.23.140',
'3.50.4': '3.23.139',
'3.50.3': '3.23.138',
'3.50.2': '3.23.137',
'3.50.1': '3.23.136',
'3.50.0': '3.23.135',
'3.49.3': '3.23.134',
'3.49.2': '3.23.133',
'3.49.1': '3.23.132',
'3.49.0': '3.23.131',
'3.48.3': '3.23.130',
'3.48.2': '3.23.129',
'3.48.1': '3.23.128',
'3.48.0': '3.23.127',
'3.47.3': '3.23.126',
'3.47.2': '3.23.125',
'3.47.1': '3.23.124',
'3.47.0': '3.23.123',
'3.46.3': '3.23.122',
'3.46.2': '3.23.121',
'3.46.1': '3.23.120',
'3.46.0': '3.23.119',
'3.45.18': '3.23.118',
'3.45.17': '3.23.117',
'3.45.16': '3.23.116',
'3.45.15': '3.23.115',
'3.45.14': '3.23.114',
'3.45.13': '3.23.113',
'3.45.12': '3.23.112',
'3.45.11': '3.23.111',
'3.45.10': '3.23.110',
'3.45.9': '3.23.109',
'3.45.8': '3.23.108',
'3.45.7': '3.23.107',
'3.45.6': '3.23.106',
'3.45.5': '3.23.105',
'3.45.4': '3.23.104',
'3.45.3': '3.23.103',
'3.45.2': '3.23.102',
'3.45.1': '3.23.101',
'3.45.0': '3.23.100',
'3.44.7': '3.23.99',
'3.44.6': '3.23.98',
'3.44.5': '3.23.97',
'3.44.4': '3.23.96',
'3.44.3': '3.23.95',
'3.44.2': '3.23.94',
'3.44.1': '3.23.93',
'3.44.0': '3.23.92',
'3.43.1': '3.23.91',
'3.43.0': '3.23.90',
'3.42.1': '3.23.89',
'3.42.0': '3.23.88',
'3.41.3': '3.23.87',
'3.41.2': '3.23.86',
'3.41.1': '3.23.85',
'3.41.0': '3.23.84',
'3.40.7': '3.23.83',
'3.40.6': '3.23.82',
'3.40.5': '3.23.81',
'3.40.4': '3.23.80',
'3.40.3': '3.23.79',
'3.40.2': '3.23.78',
'3.40.1': '3.23.77',
'3.40.0': '3.23.76',
'3.39.1': '3.23.75',
'3.39.0': '3.23.74',
'3.38.6': '3.23.73',
'3.38.5': '3.23.72',
'3.38.4': '3.23.71',
'3.38.3': '3.23.70',
'3.38.2': '3.23.69',
'3.38.1': '3.23.68',
'3.38.0': '3.23.67',
'3.37.7': '3.23.66',
'3.37.6': '3.23.65',
'3.37.5': '3.23.64',
'3.37.4': '3.23.63',
'3.37.3': '3.23.62',
'3.37.2': '3.23.61',
'3.37.1': '3.23.60',
'3.37.0': '3.23.59',
'3.36.0': '3.23.58',
'3.35.2': '3.23.57',
'3.35.1': '3.23.56',
'3.35.0': '3.23.55',
'3.34.0': '3.23.54',
'3.33.8': '3.23.53',
'3.33.7': '3.23.52',
'3.33.6': '3.23.51',
'3.33.5': '3.23.50',
'3.33.4': '3.23.49',
'3.33.3': '3.23.48',
'3.33.2': '3.23.47',
'3.33.1': '3.23.46',
'3.33.0': '3.23.45',
'3.32.0': '3.23.44',
'3.31.1': '3.23.43',
'3.31.0': '3.23.42',
'3.30.0': '3.23.41',
'3.29.2': '3.23.40',
'3.29.1': '3.23.39',
'3.29.0': '3.23.38',
'3.28.1': '3.23.37',
'3.28.0': '3.23.36',
'3.27.7': '3.23.35',
'3.27.6': '3.23.34',
'3.27.5': '3.23.33',
'3.27.4': '3.23.32',
'3.27.3': '3.23.31',
'3.27.2': '3.23.30',
'3.27.1': '3.23.29',
'3.27.0': '3.23.28',
'3.26.4': '3.23.27',
'3.26.3': '3.23.26',
'3.26.2': '3.23.25',
'3.26.1': '3.23.24',
'3.26.0': '3.23.23',
'3.25.0': '3.23.22',
'3.24.6': '3.23.21',
'3.24.5': '3.23.20',
'3.24.4': '3.23.19',
'3.24.3': '3.23.18',
'3.24.3-alpha.1': '3.23.17',
'3.24.3-alpha.0': '3.23.16',
'3.24.2': '3.23.15',
'3.24.2-alpha.1': '3.23.14',
'3.24.2-alpha.0': '3.23.13',
'3.24.1': '3.23.12',
'3.24.1-alpha.0': '3.23.11',
'3.24.0': '3.23.10',
'3.24.0-alpha.7': '3.23.9',
'3.24.0-alpha.6': '3.23.8',
'3.24.0-alpha.5': '3.23.7',
'3.24.0-alpha.4': '3.23.6',
'3.24.0-alpha.3': '3.23.5',
'3.24.0-alpha.2': '3.23.4',
'3.24.0-alpha.1': '3.23.3',
'3.24.0-alpha.0': '3.23.2',
'3.23.0': '3.23.1',
'3.23.0-alpha.1': '3.23.0',
'3.23.0-alpha.0': '3.22.21',
'3.22.7-alpha.0': '3.22.20',
'3.22.6': '3.22.19',
'3.22.6-alpha.0': '3.22.18',
'3.22.5': '3.22.17',
'3.22.4': '3.22.13-alpha.9',
'3.22.3': '3.22.13-alpha.8',
'3.22.3-alpha.9': '3.22.16-alpha.1',
'3.22.3-alpha.8': '3.22.16-alpha.0',
'3.22.3-alpha.7': '3.22.15',
'3.22.3-alpha.6': '3.22.15-alpha.3',
'3.22.3-alpha.5': '3.22.15-alpha.2',
'3.22.3-alpha.4': '3.22.15-alpha.1',
'3.22.3-alpha.3': '3.22.15-alpha.0',
'3.22.3-alpha.2': '3.22.14',
'3.22.3-alpha.1': '3.22.14-alpha.0',
'3.22.3-alpha.0': '3.22.13',
'3.22.2': '3.22.13-alpha.10',
'3.22.1': '3.22.13-alpha.7',
'3.22.0': '3.22.13-alpha.6',
'3.21.0': '3.22.13-alpha.5',
}

View file

@ -0,0 +1,351 @@
/**
* Before 11/23/2022, web and mobile used independent semantic versioning. After this point, mobile automatically
* uses web's versioning. The mapping below allows us to see what particular web version a legacy mobile version maps to
*/
export const LegacyWebToMobileVersionMapping: Record<string, string> = {
'3.107.0': '3.46.31',
'3.106.0': '3.46.30',
'3.105.0': '3.46.29',
'3.104.1': '3.46.28',
'3.104.0': '3.46.27',
'3.103.2': '3.46.26',
'3.103.1': '3.46.25',
'3.103.0': '3.46.24',
'3.102.0': '3.46.23',
'3.101.2': '3.46.22',
'3.101.1': '3.46.21',
'3.101.0': '3.46.20',
'3.100.8': '3.46.19',
'3.100.7': '3.46.18',
'3.100.6': '3.46.17',
'3.100.5': '3.46.16',
'3.100.4': '3.46.15',
'3.100.3': '3.46.14',
'3.100.2': '3.46.13',
'3.100.1': '3.46.12',
'3.100.0': '3.46.11',
'3.99.0': '3.46.10',
'3.98.2': '3.46.9',
'3.98.1': '3.46.8',
'3.98.0': '3.46.7',
'3.97.0': '3.46.6',
'3.96.1': '3.46.5',
'3.96.0': '3.46.4',
'3.95.1': '3.46.3',
'3.95.0': '3.46.2',
'3.94.2': '3.46.1',
'3.94.1': '3.46.0',
'3.94.0': '3.45.42',
'3.93.19': '3.45.41',
'3.93.18': '3.45.40',
'3.93.17': '3.45.39',
'3.93.16': '3.45.38',
'3.93.15': '3.45.37',
'3.93.14': '3.45.36',
'3.93.13': '3.45.35',
'3.93.12': '3.45.34',
'3.93.11': '3.45.33',
'3.93.10': '3.45.32',
'3.93.9': '3.45.31',
'3.93.8': '3.45.30',
'3.93.7': '3.45.29',
'3.93.6': '3.45.28',
'3.93.5': '3.45.27',
'3.93.4': '3.45.26',
'3.93.3': '3.45.25',
'3.93.2': '3.45.24',
'3.93.1': '3.45.23',
'3.93.0': '3.45.22',
'3.92.0': '3.45.21',
'3.91.1': '3.45.20',
'3.91.0': '3.45.19',
'3.90.11': '3.45.18',
'3.90.10': '3.45.17',
'3.90.9': '3.45.16',
'3.90.8': '3.45.15',
'3.90.7': '3.45.14',
'3.90.6': '3.45.13',
'3.90.5': '3.45.12',
'3.90.4': '3.45.11',
'3.90.3': '3.45.10',
'3.90.2': '3.45.9',
'3.90.1': '3.45.8',
'3.90.0': '3.45.7',
'3.89.0': '3.45.6',
'3.88.1': '3.45.5',
'3.88.0': '3.45.4',
'3.87.2': '3.45.3',
'3.87.1': '3.45.2',
'3.87.0': '3.45.1',
'3.86.0': '3.45.0',
'3.85.2': '3.44.5',
'3.85.1': '3.44.4',
'3.85.0': '3.44.3',
'3.84.7': '3.44.2',
'3.84.6': '3.44.1',
'3.84.5': '3.44.0',
'3.84.4': '3.43.33',
'3.84.3': '3.43.32',
'3.84.2': '3.43.31',
'3.84.1': '3.43.30',
'3.84.0': '3.43.29',
'3.83.0': '3.43.28',
'3.82.6': '3.43.27',
'3.82.5': '3.43.26',
'3.82.4': '3.43.25',
'3.82.3': '3.43.24',
'3.82.2': '3.43.23',
'3.82.1': '3.43.22',
'3.82.0': '3.43.21',
'3.81.0': '3.43.20',
'3.80.1': '3.43.19',
'3.80.0': '3.43.18',
'3.79.0': '3.43.17',
'3.78.1': '3.43.16',
'3.78.0': '3.43.15',
'3.77.1': '3.43.14',
'3.77.0': '3.43.13',
'3.76.2': '3.43.12',
'3.76.1': '3.43.11',
'3.76.0': '3.43.10',
'3.75.1': '3.43.9',
'3.75.0': '3.43.8',
'3.74.0': '3.43.7',
'3.73.0': '3.43.6',
'3.72.4': '3.43.5',
'3.72.3': '3.43.4',
'3.72.2': '3.43.3',
'3.72.1': '3.43.2',
'3.72.0': '3.43.1',
'3.71.8': '3.43.0',
'3.71.7': '3.42.5',
'3.71.6': '3.42.4',
'3.71.5': '3.42.3',
'3.71.4': '3.42.2',
'3.71.3': '3.42.1',
'3.71.2': '3.42.0',
'3.71.1': '3.41.10',
'3.71.0': '3.41.9',
'3.70.5': '3.41.8',
'3.70.4': '3.41.7',
'3.70.3': '3.41.6',
'3.70.2': '3.41.5',
'3.70.1': '3.41.4',
'3.70.0': '3.41.3',
'3.69.1': '3.41.2',
'3.69.0': '3.41.1',
'3.68.6': '3.41.0',
'3.68.5': '3.40.2',
'3.68.4': '3.40.1',
'3.68.3': '3.40.0',
'3.68.2': '3.39.12',
'3.68.1': '3.39.11',
'3.68.0': '3.39.10',
'3.67.4': '3.39.9',
'3.67.3': '3.39.8',
'3.67.2': '3.39.7',
'3.67.1': '3.39.6',
'3.67.0': '3.39.5',
'3.66.0': '3.39.4',
'3.65.0': '3.39.3',
'3.64.0': '3.39.2',
'3.63.3': '3.39.1',
'3.63.2': '3.39.0',
'3.63.1': '3.38.1',
'3.63.0': '3.38.0',
'3.62.4': '3.37.14',
'3.62.3': '3.37.13',
'3.62.2': '3.37.12',
'3.62.1': '3.37.11',
'3.62.0': '3.37.10',
'3.61.0': '3.37.9',
'3.60.0': '3.37.8',
'3.59.3': '3.37.7',
'3.59.2': '3.37.6',
'3.59.1': '3.37.5',
'3.59.0': '3.37.4',
'3.58.1': '3.37.3',
'3.58.0': '3.37.2',
'3.57.3': '3.37.1',
'3.57.2': '3.37.0',
'3.57.1': '3.36.18',
'3.57.0': '3.36.17',
'3.56.0': '3.36.16',
'3.55.2': '3.36.15',
'3.55.1': '3.36.14',
'3.55.0': '3.36.13',
'3.54.6': '3.36.12',
'3.54.5': '3.36.11',
'3.54.4': '3.36.10',
'3.54.3': '3.36.9',
'3.54.2': '3.36.8',
'3.54.1': '3.36.7',
'3.54.0': '3.36.6',
'3.53.0': '3.36.5',
'3.52.1': '3.36.4',
'3.52.0': '3.36.3',
'3.51.0': '3.36.2',
'3.50.6': '3.36.1',
'3.50.5': '3.36.0',
'3.50.4': '3.35.16',
'3.50.3': '3.35.15',
'3.50.2': '3.35.14',
'3.50.1': '3.35.13',
'3.50.0': '3.35.12',
'3.49.3': '3.35.11',
'3.49.2': '3.35.10',
'3.49.1': '3.35.9',
'3.49.0': '3.35.8',
'3.48.3': '3.35.7',
'3.48.2': '3.35.6',
'3.48.1': '3.35.5',
'3.48.0': '3.35.4',
'3.47.3': '3.35.3',
'3.47.2': '3.35.2',
'3.47.1': '3.35.1',
'3.47.0': '3.35.0',
'3.46.3': '3.34.3',
'3.46.2': '3.34.2',
'3.46.1': '3.34.1',
'3.46.0': '3.34.0',
'3.45.18': '3.33.7',
'3.45.17': '3.33.6',
'3.45.16': '3.33.5',
'3.45.15': '3.33.4',
'3.45.14': '3.33.3',
'3.45.13': '3.33.2',
'3.45.12': '3.33.1',
'3.45.11': '3.33.0',
'3.45.10': '3.32.5',
'3.45.9': '3.32.4',
'3.45.8': '3.32.3',
'3.45.7': '3.32.2',
'3.45.6': '3.32.1',
'3.45.5': '3.32.0',
'3.45.4': '3.31.29',
'3.45.3': '3.31.28',
'3.45.2': '3.31.27',
'3.45.1': '3.31.26',
'3.45.0': '3.31.25',
'3.44.7': '3.31.24',
'3.44.6': '3.31.23',
'3.44.5': '3.31.22',
'3.44.4': '3.31.21',
'3.44.3': '3.31.20',
'3.44.2': '3.31.19',
'3.44.1': '3.31.18',
'3.44.0': '3.31.17',
'3.43.1': '3.31.16',
'3.43.0': '3.31.15',
'3.42.1': '3.31.14',
'3.42.0': '3.31.13',
'3.41.3': '3.31.12',
'3.41.2': '3.31.11',
'3.41.1': '3.31.10',
'3.41.0': '3.31.9',
'3.40.7': '3.31.8',
'3.40.6': '3.31.7',
'3.40.5': '3.31.6',
'3.40.4': '3.31.5',
'3.40.3': '3.31.4',
'3.40.2': '3.31.3',
'3.40.1': '3.31.2',
'3.40.0': '3.31.1',
'3.39.1': '3.31.0',
'3.39.0': '3.30.38',
'3.38.6': '3.30.37',
'3.38.5': '3.30.36',
'3.38.4': '3.30.35',
'3.38.3': '3.30.34',
'3.38.2': '3.30.33',
'3.38.1': '3.30.32',
'3.38.0': '3.30.31',
'3.37.7': '3.30.30',
'3.37.6': '3.30.29',
'3.37.5': '3.30.28',
'3.37.4': '3.30.27',
'3.37.3': '3.30.26',
'3.37.2': '3.30.25',
'3.37.1': '3.30.24',
'3.37.0': '3.30.23',
'3.36.0': '3.30.22',
'3.35.2': '3.30.21',
'3.35.1': '3.30.20',
'3.35.0': '3.30.19',
'3.34.0': '3.30.18',
'3.33.8': '3.30.17',
'3.33.7': '3.30.16',
'3.33.6': '3.30.15',
'3.33.5': '3.30.14',
'3.33.4': '3.30.13',
'3.33.3': '3.30.12',
'3.33.2': '3.30.11',
'3.33.1': '3.30.10',
'3.33.0': '3.30.9',
'3.32.0': '3.30.8',
'3.31.1': '3.30.7',
'3.31.0': '3.30.6',
'3.30.0': '3.30.5',
'3.29.2': '3.30.4',
'3.29.1': '3.30.3',
'3.29.0': '3.30.2',
'3.28.1': '3.30.1',
'3.28.0': '3.30.0',
'3.27.7': '3.29.0',
'3.27.6': '3.28.3',
'3.27.5': '3.28.2',
'3.27.4': '3.28.1',
'3.27.3': '3.28.0',
'3.27.2': '3.27.13',
'3.27.1': '3.27.12',
'3.27.0': '3.27.11',
'3.26.4': '3.27.10',
'3.26.3': '3.27.9',
'3.26.2': '3.27.8',
'3.26.1': '3.27.7',
'3.26.0': '3.27.6',
'3.25.0': '3.27.5',
'3.24.6': '3.27.4',
'3.24.5': '3.27.3',
'3.24.4': '3.27.2',
'3.24.3': '3.27.1',
'3.24.3-alpha.1': '3.27.0',
'3.24.3-alpha.0': '3.26.14',
'3.24.2': '3.26.13',
'3.24.2-alpha.1': '3.26.12',
'3.24.2-alpha.0': '3.26.11',
'3.24.1': '3.26.10',
'3.24.1-alpha.0': '3.26.9',
'3.24.0': '3.26.8',
'3.24.0-alpha.7': '3.26.7',
'3.24.0-alpha.6': '3.26.6',
'3.24.0-alpha.5': '3.26.5',
'3.24.0-alpha.4': '3.26.4',
'3.24.0-alpha.3': '3.26.3',
'3.24.0-alpha.2': '3.26.2',
'3.24.0-alpha.1': '3.26.1',
'3.24.0-alpha.0': '3.26.0',
'3.23.0': '3.25.15',
'3.23.0-alpha.1': '3.25.14',
'3.23.0-alpha.0': '3.25.13',
'3.22.7-alpha.0': '3.25.12',
'3.22.6': '3.25.11',
'3.22.6-alpha.0': '3.25.10',
'3.22.5': '3.25.9',
'3.22.4': '3.24.4',
'3.22.3': '3.24.3',
'3.22.3-alpha.9': '3.25.6',
'3.22.3-alpha.8': '3.25.5',
'3.22.3-alpha.7': '3.25.4',
'3.22.3-alpha.6': '3.25.3',
'3.22.3-alpha.5': '3.25.2',
'3.22.3-alpha.4': '3.25.1',
'3.22.3-alpha.3': '3.25.0',
'3.22.3-alpha.2': '3.24.8',
'3.22.3-alpha.1': '3.24.7',
'3.22.3-alpha.0': '3.24.6',
'3.22.2': '3.24.5',
'3.22.1': '3.24.2',
'3.22.0': '3.24.2-alpha.2',
'3.21.0': '3.24.2-alpha.1',
}

View file

@ -9,6 +9,7 @@ const PREFERENCE_IDS = [
'accessibility',
'get-free-month',
'help-feedback',
'whats-new',
] as const
export type PreferenceId = typeof PREFERENCE_IDS[number]

View file

@ -1,6 +1,9 @@
export * from './Alert/Functions'
export * from './Alert/WebAlertService'
export * from './Archive/ArchiveManager'
export * from './Changelog/Changelog'
export * from './Changelog/ChangelogService'
export * from './Changelog/ChangelogServiceInterface'
export * from './Keyboard/KeyboardService'
export * from './Keyboard/KeyboardShortcut'
export * from './Keyboard/KeyboardCommands'

View file

@ -29,6 +29,7 @@ import { DesktopManager } from './Device/DesktopManager'
import {
ArchiveManager,
AutolockService,
ChangelogService,
KeyboardService,
PreferenceId,
RouteService,
@ -97,6 +98,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
? new DesktopManager(this, deviceInterface)
: undefined
this.webServices.viewControllerManager = new ViewControllerManager(this, deviceInterface)
this.webServices.changelogService = new ChangelogService(this.environment, this.storage)
if (this.isNativeMobileWeb()) {
this.mobileWebReceiver = new MobileWebReceiver(this)
@ -193,6 +195,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return this.webServices.viewControllerManager.paneController
}
public get changelogService() {
return this.webServices.changelogService
}
public get desktopDevice(): DesktopDeviceInterface | undefined {
if (isDesktopDevice(this.deviceInterface)) {
return this.deviceInterface

View file

@ -1,6 +1,12 @@
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
import { DesktopManager } from './Device/DesktopManager'
import { ArchiveManager, AutolockService, KeyboardService, ThemeManager } from '@standardnotes/ui-services'
import {
ArchiveManager,
AutolockService,
ChangelogServiceInterface,
KeyboardService,
ThemeManager,
} from '@standardnotes/ui-services'
export type WebServices = {
viewControllerManager: ViewControllerManager
@ -9,4 +15,5 @@ export type WebServices = {
archiveService: ArchiveManager
themeService: ThemeManager
keyboardService: KeyboardService
changelogService: ChangelogServiceInterface
}

View file

@ -9,6 +9,7 @@ import Security from './Panes/Security/Security'
import Listed from './Panes/Listed/Listed'
import HelpAndFeedback from './Panes/HelpFeedback'
import { PreferencesProps } from './PreferencesProps'
import WhatsNew from './Panes/WhatsNew/WhatsNew'
const PaneSelector: FunctionComponent<PreferencesProps & { menu: PreferencesMenu }> = ({
menu,
@ -51,6 +52,8 @@ const PaneSelector: FunctionComponent<PreferencesProps & { menu: PreferencesMenu
return null
case 'help-feedback':
return <HelpAndFeedback application={application} />
case 'whats-new':
return <WhatsNew application={application} />
default:
return (
<General

View file

@ -0,0 +1 @@
export const IgnoreScopes = ['mobile:', 'dev:']

View file

@ -0,0 +1 @@
export type SectionKey = 'Bug Fixes' | 'Features'

View file

@ -0,0 +1,109 @@
import PreferencesPane from '../../PreferencesComponents/PreferencesPane'
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import { WebApplication } from '@/Application/Application'
import { useEffect, useMemo, useState } from 'react'
import { Changelog } from '@standardnotes/ui-services'
import { LinkButton, Subtitle, Title } from '@/Components/Preferences/PreferencesComponents/Content'
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
import { getSectionItems } from './getSectionItems'
import { isDesktopApplication } from '@/Utils'
import { compareSemVersions } from '@standardnotes/snjs'
const WhatsNewSection = ({ items, sectionName }: { items: string[] | undefined; sectionName: string }) => {
if (!items) {
return null
}
return (
<div>
<Subtitle>{sectionName}</Subtitle>
<ul className="list-inside">
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)
}
const WhatsNew = ({ application }: { application: WebApplication }) => {
const [changelog, setChangelog] = useState<Changelog | null>(null)
const appVersion = application.version
const lastReadVersion = useMemo(() => application.changelogService.getLastReadVersion(), [application])
useEffect(() => {
void application.changelogService.getChangelog().then(setChangelog)
}, [application])
useEffect(() => {
if (changelog) {
application.changelogService.markAsRead()
}
}, [changelog, application])
if (!changelog) {
return <div>Loading...</div>
}
return (
<PreferencesPane>
{changelog.versions.map((version, index) => {
const bugFixes = getSectionItems(version, 'Bug Fixes')
const features = getSectionItems(version, 'Features')
if (!bugFixes && !features) {
return null
}
if (!version.version) {
return null
}
const isUnreadVersion = lastReadVersion && compareSemVersions(version.version, lastReadVersion) > 0
const isLatest = index === 0
const isDesktopEnvironment = isDesktopApplication()
const showDownloadLink = isDesktopEnvironment && isLatest
return (
<PreferencesGroup>
<div key={version.version}>
<div className="flex justify-between">
<div className="flex items-start">
<Title className="mb-3 flex">{version.version}</Title>
{version.version === appVersion && (
<div className="ml-2 rounded bg-info px-2 py-1 text-[10px] font-bold text-info-contrast">
Your Version
</div>
)}
{isLatest && (
<div className="ml-2 rounded bg-success px-2 py-1 text-[10px] font-bold text-success-contrast">
Latest Version
</div>
)}
{isUnreadVersion && (
<div className="ml-2 rounded bg-success px-2 py-1 text-[10px] font-bold text-success-contrast">
New
</div>
)}
</div>
{showDownloadLink && (
<LinkButton
label={'Open Downloads'}
link={application.changelogService.getDesktopDownloadsUrl(version.version)}
className="mb-3"
/>
)}
</div>
<WhatsNewSection sectionName="Features" items={features} />
{features && bugFixes && <HorizontalSeparator classes="my-4" />}
<WhatsNewSection sectionName="Bug Fixes" items={bugFixes} />
</div>
</PreferencesGroup>
)
})}
</PreferencesPane>
)
}
export default WhatsNew

View file

@ -0,0 +1,44 @@
import { ChangelogVersion } from '@standardnotes/ui-services'
import { SectionKey } from './SectionKey'
import { IgnoreScopes } from './IgnoreScopes'
function removeAnythingInParentheses(line: string): string {
return line.replace(/\(.*\)/g, '')
}
function capitalizeFirstLetter(line: string): string {
return line.charAt(0).toUpperCase() + line.slice(1)
}
export function formatChangelogLine(line: string): string {
let result = capitalizeFirstLetter(line)
result = removeAnythingInParentheses(result)
return result
}
function lineHasIgnoredScope(line: string): boolean {
return IgnoreScopes.some((scope) => line.toLowerCase().includes(scope.toLowerCase()))
}
function lineHasOnlyOneWord(line: string): boolean {
return line.trim().split(' ').length === 1
}
export function getSectionItems(version: ChangelogVersion, sectionKey: SectionKey): string[] | undefined {
const section = version.parsed[sectionKey]
if (!section) {
return undefined
}
const filtered = section.map(formatChangelogLine).filter((item) => {
return !lineHasIgnoredScope(item) && !lineHasOnlyOneWord(item)
})
if (filtered.length === 0) {
return undefined
}
return filtered
}

View file

@ -20,6 +20,7 @@ interface SelectableMenuItem extends PreferencesMenuItem {
* Items are in order of appearance
*/
const PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'whats-new', label: "What's New", icon: 'asterisk' },
{ id: 'account', label: 'Account', icon: 'user' },
{ id: 'general', label: 'General', icon: 'settings' },
{ id: 'security', label: 'Security', icon: 'security' },
@ -33,6 +34,7 @@ const PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
]
const READY_PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'whats-new', label: "What's New", icon: 'asterisk' },
{ id: 'account', label: 'Account', icon: 'user' },
{ id: 'general', label: 'General', icon: 'settings' },
{ id: 'security', label: 'Security', icon: 'security' },

View file

@ -0,0 +1,16 @@
const parseChangelog = require('changelog-parser')
const path = require('path')
const fs = require('fs')
const FILES = ['packages/web/CHANGELOG.md']
async function saveJsonChangelogs() {
for (const file of FILES) {
const parsed = await parseChangelog(path.join(__dirname, `../${file}`))
const json = JSON.stringify(parsed, null, 2)
const jsonPath = path.join(__dirname, `../${file}.json`)
fs.writeFileSync(jsonPath, json)
}
}
saveJsonChangelogs()

View file

@ -1,7 +1,7 @@
const parseChangelog = require('changelog-parser')
const path = require('path')
const scopes = ['mobile', 'web', 'desktop', 'components']
const scopes = ['mobile', 'web', 'desktop']
async function parsePackages(packageNames) {
let result = ''