Compare commits

...
Author SHA1 Message Date
StandardNotes CI f5b69691c0 chore(release): publish
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-08-26 12:13:01 +00:00
Antonella Sgarlatta 54af28aa04 chore: Add serverPassword param to endpoints (#2919) [skip e2e]
* chore: send server password param to delete account endpoint

* chore: send server password param to disable mfa endpoint

* chore: modify tests

* chore: force challenge prompt for mfa disable

* chore: fix eslint errors

* chore: add server passsword to get recovery codes

* chore: fix tests

* chore: pass server password as header
2025-08-26 09:04:03 -03:00
StandardNotes CI cf4d2196de chore(release): publish
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-08-25 11:33:29 +00:00
Antonella Sgarlatta abf14d1e50 chore: trigger release [skip e2e] 2025-08-25 08:24:22 -03:00
Antonella Sgarlatta 86c00056bc chore: trigger release [skip e2e] 2025-08-25 07:59:48 -03:00
Antonella Sgarlatta f77a6a4480 chore: trigger release
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
2025-08-21 15:15:51 -03:00
StandardNotes CI 98038da99f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-08-21 14:53:47 +00:00
Antonella Sgarlatta b9d7f36884 chore: trigger release [skip e2e] 2025-08-21 11:44:33 -03:00
Antonella Sgarlatta 19831e834e chore: use next storage implementation for asyncstorage (#2931)
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
* chore: use next storage implementation for asyncstorage

* chore: trigger release
2025-08-20 14:52:30 -03:00
StandardNotes CI 7e50b864ef chore(release): publish
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-08-11 17:47:42 +00:00
Antonella Sgarlatta 687668c8e2 chore: trigger release [skip e2e] 2025-08-11 14:38:25 -03:00
Antonella Sgarlatta d135e09bda fix: Fixes Share note option not working on Android for note titles with special characters (#2928) 2025-08-11 14:24:28 -03:00
Antonella Sgarlatta f284111a0c chore: remove folders migration prompt (#2927)
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
* chore: remove folders migration prompt

* chore: remove unused imports
2025-08-08 10:13:37 -03:00
StandardNotes CI be109a0f6b chore(release): publish
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-08-05 14:27:15 +00:00
Antonella Sgarlatta c08d9034e1 chore: trigger release [skip e2e] 2025-08-05 11:17:58 -03:00
Antonella Sgarlatta 010864881d fix: Fixes Back and Navigation buttons not working on mobile view when panels explicitly collapsed (#2926)
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
2025-08-05 10:31:48 -03:00
Antonella Sgarlatta 2b41c8337b fix: Fixes portions of window not being draggable on desktop (#2925) 2025-08-05 10:31:25 -03:00
Antonella Sgarlatta 45bcc0e0b1 fix: Fixes issue where plaintext backups would use invalid special characters for filenames (#2924) 2025-08-05 10:31:15 -03:00
StandardNotes CI d928477d82 chore(release): publish
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-08-04 13:53:41 +00:00
Antonella Sgarlatta b86aebb7ab chore: trigger release [skip e2e] 2025-08-04 10:36:32 -03:00
Antonella Sgarlatta 3cce44f5ab Merge pull request #2923 from standardnotes/fix/prevent-quick-type-suggestions
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
fix: Prevents quick type suggestions on iOS for password input
2025-08-01 11:09:06 -03:00
Antonella Sgarlatta ff80775cd6 fix: Prevents quick type suggestions on iOS for password input 2025-07-31 14:24:27 -03:00
StandardNotes CI c75f655ffe chore(release): publish
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-07-29 16:00:49 +00:00
Antonella Sgarlatta 87cf17d02f Merge pull request #2921 from standardnotes/fix/disable-quick-type-suggestions-password-field
fix: disable quick type suggestions for password field
2025-07-29 12:31:28 -03:00
Antonella Sgarlatta 0d17961570 fix: Fixed font size not updating on plain editor when changing it from Preferences (#2920) 2025-07-29 12:30:29 -03:00
Antonella Sgarlatta 63d84725d2 fix: Fixes extra bottom padding in account modal 2025-07-28 00:33:22 -03:00
Antonella Sgarlatta 9583d0db44 fix: Fixes quick type suggestions showing up in iOS for password field 2025-07-28 00:32:51 -03:00
StandardNotes CI fa4182ba9f chore(release): publish
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-07-23 00:23:53 +00:00
Antonella Sgarlatta 259017bd39 chore: revert some release workflow steps 2025-07-22 20:53:51 -03:00
StandardNotes CI f1f9070dcc chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2025-07-22 22:30:34 +00:00
Antonella Sgarlatta 6e6b5a017b chore: revert some release workflow steps
CodeQL / Analyze (javascript) (push) Has been cancelled
Sync Repo / git-sync (push) Has been cancelled
Publish Packages / Build & Test (push) Has been cancelled
Publish Packages / Build Docker Image (push) Has been cancelled
Publish Packages / E2E Base Test Suite (push) Has been cancelled
Publish Packages / E2E Vaults Test Suite (push) Has been cancelled
Publish Packages / Build and publish Docker Image for Web App (push) Has been cancelled
Publish Packages / Publish to NPM (push) Has been cancelled
Publish Packages / Publish to Docker Hub (push) Has been cancelled
2025-07-22 19:01:40 -03:00
61 changed files with 663 additions and 146 deletions
+13 -7
View File
@@ -78,22 +78,25 @@ jobs:
- run: yarn install --immutable
- name: Rebuild Electron Native Modules
run: yarn workspace @standardnotes/desktop rebuild:home-server
- run: yarn build:desktop
- 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
- name: AppImage
run: |
yarn workspace @standardnotes/desktop rebuild:home-server
yarn run webpack --config desktop.webpack.prod.js
yarn run electron-builder --linux --x64 -c.linux.target=AppImage --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: AppImageX64
run: |
yarn workspace @standardnotes/desktop rebuild:home-server
yarn run webpack --config desktop.webpack.prod.js
yarn run electron-builder --linux --x64 -c.linux.target=AppImage --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: AppImageArm64
run: |
yarn workspace @standardnotes/desktop rebuild:home-server --arch arm64
yarn run webpack --config desktop.webpack.prod.js
yarn run electron-builder --linux --arm64 -c.linux.target=AppImage --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
env:
npm_config_target_arch: 'arm64'
@@ -138,7 +141,6 @@ jobs:
- name: Dir
run: |
yarn workspace @standardnotes/desktop rebuild:home-server
yarn run webpack --config desktop.webpack.prod.js
yarn run electron-builder --linux --x64 -c.linux.target=dir --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: DirArm64
@@ -171,11 +173,15 @@ jobs:
- run: yarn install --immutable
- name: Rebuild Electron Native Modules
run: yarn workspace @standardnotes/desktop rebuild:home-server
- run: yarn build:desktop
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- name: Deb
run: |
yarn workspace @standardnotes/desktop rebuild:home-server
yarn run webpack --config desktop.webpack.prod.js --env deb
yarn run electron-builder --linux --x64 -c.linux.target=deb --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: DebArm64
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "@standardnotes/app-monorepo",
"version": "0.0.1",
"private": true,
"author": "Standard Notes.",
"author": "Standard Notes",
"repository": {
"type": "git",
"url": "git://github.com/standardnotes/app"
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.94](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/api
## [1.26.93](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-15)
**Note:** Version bump only for package @standardnotes/api
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.26.93",
"version": "1.26.94",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -22,7 +22,9 @@ export class AuthApiService implements AuthApiServiceInterface {
this.operationsInProgress = new Map()
}
async generateRecoveryCodes(): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>> {
async generateRecoveryCodes(dto: {
serverPassword: string
}): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>> {
if (this.operationsInProgress.get(AuthApiOperations.GenerateRecoveryCodes)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
@@ -30,7 +32,9 @@ export class AuthApiService implements AuthApiServiceInterface {
this.operationsInProgress.set(AuthApiOperations.GenerateRecoveryCodes, true)
try {
const response = await this.authServer.generateRecoveryCodes()
const response = await this.authServer.generateRecoveryCodes({
headers: [{ key: 'x-server-password', value: dto.serverPassword }],
})
return response
} catch (error) {
@@ -6,7 +6,7 @@ import {
} from '../../Response'
export interface AuthApiServiceInterface {
generateRecoveryCodes(): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>>
generateRecoveryCodes(dto: { serverPassword: string }): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>>
recoveryKeyParams(dto: {
username: string
codeChallenge: string
@@ -27,13 +27,19 @@ export class UserApiService implements UserApiServiceInterface {
this.operationsInProgress = new Map()
}
async deleteAccount(userUuid: string): Promise<HttpResponse<UserDeletionResponseBody>> {
async deleteAccount(dto: {
userUuid: string
serverPassword: string
}): Promise<HttpResponse<UserDeletionResponseBody>> {
this.lockOperation(UserApiOperations.DeletingAccount)
try {
const response = await this.userServer.deleteAccount({
userUuid: userUuid,
})
const response = await this.userServer.deleteAccount(
{
userUuid: dto.userUuid,
},
{ headers: [{ key: 'x-server-password', value: dto.serverPassword }] },
)
this.unlockOperation(UserApiOperations.DeletingAccount)
@@ -22,5 +22,8 @@ export interface UserApiServiceInterface {
requestType: UserRequestType
}): Promise<HttpResponse<UserRequestResponseBody>>
deleteAccount(userUuid: string): Promise<HttpResponse<UserDeletionResponseBody>>
deleteAccount(dto: {
userUuid: string
serverPassword: string | undefined
}): Promise<HttpResponse<UserDeletionResponseBody>>
}
@@ -91,6 +91,7 @@ export class HttpService implements HttpServiceInterface {
params,
verb: HttpVerb.Get,
authentication: options?.authentication ?? this.getSessionAccessToken(),
customHeaders: options?.headers,
})
}
@@ -123,6 +124,7 @@ export class HttpService implements HttpServiceInterface {
params,
verb: HttpVerb.Put,
authentication: options?.authentication ?? this.getSessionAccessToken(),
customHeaders: options?.headers,
})
}
@@ -141,6 +143,7 @@ export class HttpService implements HttpServiceInterface {
params,
verb: HttpVerb.Delete,
authentication: options?.authentication ?? this.getSessionAccessToken(),
customHeaders: options?.headers,
})
}
@@ -0,0 +1,3 @@
export interface GenerateRecoveryCodesRequestParams {
serverPassword: string
}
+1
View File
@@ -2,6 +2,7 @@ export * from './Authenticator/DeleteAuthenticatorRequestParams'
export * from './Authenticator/GenerateAuthenticatorAuthenticationOptionsRequestParams'
export * from './Authenticator/ListAuthenticatorsRequestParams'
export * from './Authenticator/VerifyAuthenticatorRegistrationResponseRequestParams'
export * from './Recovery/GenerateRecoveryCodesRequestParams'
export * from './Recovery/RecoveryKeyParamsRequestParams'
export * from './Recovery/SignInWithRecoveryCodesRequestParams'
export * from './Revision/DeleteRevisionRequestParams'
@@ -8,12 +8,13 @@ import {
} from '../../Response'
import { AuthServerInterface } from './AuthServerInterface'
import { Paths } from './Paths'
import { HttpRequestOptions } from '../../Http/HttpRequestOptions'
export class AuthServer implements AuthServerInterface {
constructor(private httpService: HttpServiceInterface) {}
async generateRecoveryCodes(): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>> {
return this.httpService.post(Paths.v1.generateRecoveryCodes)
async generateRecoveryCodes(options?: HttpRequestOptions): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>> {
return this.httpService.post(Paths.v1.generateRecoveryCodes, undefined, options)
}
async recoveryKeyParams(
@@ -5,9 +5,10 @@ import {
RecoveryKeyParamsResponseBody,
SignInWithRecoveryCodesResponseBody,
} from '../../Response'
import { HttpRequestOptions } from '../../Http/HttpRequestOptions'
export interface AuthServerInterface {
generateRecoveryCodes(): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>>
generateRecoveryCodes(options?: HttpRequestOptions): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>>
recoveryKeyParams(params: RecoveryKeyParamsRequestParams): Promise<HttpResponse<RecoveryKeyParamsResponseBody>>
signInWithRecoveryCodes(
params: SignInWithRecoveryCodesRequestParams,
@@ -8,12 +8,16 @@ import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrati
import { Paths } from './Paths'
import { UserServerInterface } from './UserServerInterface'
import { UserUpdateRequestParams } from '../../Request/User/UserUpdateRequestParams'
import { HttpRequestOptions } from '../../Http/HttpRequestOptions'
export class UserServer implements UserServerInterface {
constructor(private httpService: HttpServiceInterface) {}
async deleteAccount(params: UserDeletionRequestParams): Promise<HttpResponse<UserDeletionResponseBody>> {
return this.httpService.delete(Paths.v1.deleteAccount(params.userUuid), params)
async deleteAccount(
params: UserDeletionRequestParams,
options?: HttpRequestOptions,
): Promise<HttpResponse<UserDeletionResponseBody>> {
return this.httpService.delete(Paths.v1.deleteAccount(params.userUuid), params, options)
}
async register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>> {
@@ -5,9 +5,13 @@ import { UserDeletionResponseBody } from '../../Response/User/UserDeletionRespon
import { UserRegistrationResponseBody } from '../../Response/User/UserRegistrationResponseBody'
import { UserUpdateResponse } from '../../Response/User/UserUpdateResponse'
import { UserUpdateRequestParams } from '../../Request/User/UserUpdateRequestParams'
import { HttpRequestOptions } from '../../Http/HttpRequestOptions'
export interface UserServerInterface {
register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>>
deleteAccount(params: UserDeletionRequestParams): Promise<HttpResponse<UserDeletionResponseBody>>
deleteAccount(
params: UserDeletionRequestParams,
options?: HttpRequestOptions,
): Promise<HttpResponse<UserDeletionResponseBody>>
update(params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>>
}
+32
View File
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.544](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.543](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-25)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.542](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-11)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.541](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-05)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.540](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-04)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.539](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-29)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.538](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-23)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.537](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.536](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/clipper
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/clipper",
"description": "Web clipper browser extension for Standard Notes",
"version": "1.1.536",
"version": "1.1.544",
"private": true,
"scripts": {
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
+35
View File
@@ -3,6 +3,41 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.110.149](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/desktop
## [3.110.148](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-25)
**Note:** Version bump only for package @standardnotes/desktop
## [3.110.147](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.110.146](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-05)
### Bug Fixes
* Fixes issue where plaintext backups would use invalid special characters for filenames ([#2924](https://github.com/standardnotes/app/issues/2924)) ([45bcc0e](https://github.com/standardnotes/app/commit/45bcc0e0b1541558ecf410e9a92bc2db192cfe93))
* Fixes portions of window not being draggable on desktop ([#2925](https://github.com/standardnotes/app/issues/2925)) ([2b41c83](https://github.com/standardnotes/app/commit/2b41c8337b1408209dc4903cacbc701b73c590b0))
## [3.110.145](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-04)
**Note:** Version bump only for package @standardnotes/desktop
## [3.110.144](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.110.143](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-23)
**Note:** Version bump only for package @standardnotes/desktop
## [3.110.142](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/desktop
## [3.110.141](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/desktop
@@ -17,6 +17,7 @@ import { FileReadOperation } from './FileReadOperation'
import { Paths } from '../Types/Paths'
import { MessageToWebApp } from '../../Shared/IpcMessages'
import { FilesManagerInterface } from '../File/FilesManagerInterface'
import { sanitizeFileName } from '@standardnotes/utils'
const TextBackupFileExtension = '.txt'
@@ -316,7 +317,8 @@ export class FilesBackupManager implements FileBackupsDevice {
const relativePath = forTag ?? ''
const filenameWithSlashesEscaped = filename.replace(/\//g, '\u2215')
const fileAbsolutePath = path.join(absolutePath, relativePath, filenameWithSlashesEscaped)
const sanitizedFilename = sanitizeFileName(filenameWithSlashesEscaped)
const fileAbsolutePath = path.join(absolutePath, relativePath, sanitizedFilename)
await this.filesManager.writeFile(fileAbsolutePath, data)
const existingRecord = findMappingRecord(forTag)
@@ -70,12 +70,19 @@
/* https://github.com/electron/electron/issues/37789 */
.mac-desktop #navigation,
.mac-desktop #items-column,
.mac-desktop #editor-column,
.mac-desktop #items-title-bar,
.mac-desktop #editor-title-bar,
.mac-desktop [data-preferences-header] {
-webkit-app-region: drag;
}
.mac-desktop #items-column > *:not(#items-title-bar),
.mac-desktop #editor-column > *:not(#editor-title-bar) {
-webkit-app-region: no-drag;
}
.mac-desktop.focus-mode #editor-title-bar {
-webkit-app-region: no-drag;
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.110.141",
"version": "3.110.149",
"license": "AGPL-3.0",
"author": "Standard Notes.",
"private": true,
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.58.207](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.206](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-25)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.205](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-21)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.204](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-11)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.203](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-05)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.202](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-04)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.201](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.200](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-23)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.199](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/mobile
## [3.58.198](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/mobile
+4 -2
View File
@@ -38,8 +38,10 @@ newArchEnabled=true
# If set to false, you will be using JSC instead.
hermesEnabled=true
# Set AsyncStorage limit
AsyncStorage_db_size_in_MB=50
# Use Next storage implementation for AsyncStorage
AsyncStorage_useNextStorage=true
AsyncStorage_kotlinVersion=2.0.21
AsyncStorage_next_kspVersion=2.0.21-1.0.28
# The URL of the server
host=https://app.standardnotes.com
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.58.198",
"version": "3.58.207",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0",
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.847](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.846](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-25)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.845](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-21)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.844](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.843](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-05)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.842](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-04)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.841](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.840](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-23)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.839](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.838](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.838",
"version": "1.4.847",
"license": "AGPL-3.0",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.71.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/services
## [1.71.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-15)
**Note:** Version bump only for package @standardnotes/services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.71.9",
"version": "1.71.10",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -2,7 +2,7 @@ import { AnyKeyParamsContent } from '@standardnotes/common'
import { SessionBody } from '@standardnotes/responses'
export interface AuthClientInterface {
generateRecoveryCodes(): Promise<string | false>
generateRecoveryCodes(dto: { serverPassword: string }): Promise<string | false>
recoveryKeyParams(dto: {
username: string
codeChallenge: string
@@ -14,9 +14,9 @@ export class AuthManager extends AbstractService implements AuthClientInterface
super(internalEventBus)
}
async generateRecoveryCodes(): Promise<string | false> {
async generateRecoveryCodes(dto: { serverPassword: string }): Promise<string | false> {
try {
const result = await this.authApiService.generateRecoveryCodes()
const result = await this.authApiService.generateRecoveryCodes(dto)
if (isErrorResponse(result)) {
return false
@@ -1,6 +1,6 @@
import { ApplicationServiceInterface } from './../Service/ApplicationServiceInterface'
import { DecryptedItem, DecryptedItemInterface, FileItem, SNNote } from '@standardnotes/models'
import { ChallengeInterface, ChallengeReason } from '../Challenge'
import { ChallengeInterface, ChallengeReason, ChallengeResponseInterface } from '../Challenge'
import { MobileUnlockTiming } from './MobileUnlockTiming'
import { TimingDisplayOption } from './TimingDisplayOption'
import { ProtectionEvent } from './ProtectionEvent'
@@ -28,6 +28,10 @@ export interface ProtectionsClientInterface extends ApplicationServiceInterface<
reason: ChallengeReason,
dto: { fallBackToAccountPassword: boolean; requireAccountPassword: boolean; forcePrompt: boolean },
): Promise<boolean>
authorizeActionWithChallengeResponse(
reason: ChallengeReason,
dto: { fallBackToAccountPassword: boolean; requireAccountPassword: boolean; forcePrompt: boolean },
): Promise<{ success: boolean; challengeResponse?: ChallengeResponseInterface }>
authorizeAddingPasscode(): Promise<boolean>
authorizeRemovingPasscode(): Promise<boolean>
authorizeChangingPasscode(): Promise<boolean>
@@ -36,7 +40,8 @@ export interface ProtectionsClientInterface extends ApplicationServiceInterface<
authorizeAutolockIntervalChange(): Promise<boolean>
authorizeSearchingProtectedNotesText(): Promise<boolean>
authorizeBackupCreation(): Promise<boolean>
authorizeMfaDisable(): Promise<boolean>
authorizeMfaDisable(): Promise<{ success: boolean; challengeResponse?: ChallengeResponseInterface }>
authorizeAccountDeletion(): Promise<{ success: boolean; challengeResponse?: ChallengeResponseInterface }>
protectItems<I extends DecryptedItemInterface>(items: I[]): Promise<I[]>
unprotectItems<I extends DecryptedItemInterface>(items: I[], reason: ChallengeReason): Promise<I[] | undefined>
@@ -237,13 +237,9 @@ export class UserService
error: boolean
message?: string
}> {
if (
!(await this.protections.authorizeAction(ChallengeReason.DeleteAccount, {
fallBackToAccountPassword: true,
requireAccountPassword: true,
forcePrompt: false,
}))
) {
const { success, challengeResponse } = await this.protections.authorizeAccountDeletion()
if (!success) {
return {
error: true,
message: Messages.INVALID_PASSWORD,
@@ -251,7 +247,13 @@ export class UserService
}
const uuid = this.sessions.getSureUser().uuid
const response = await this.userApi.deleteAccount(uuid)
const password = challengeResponse?.getValueForType(ChallengeValidation.AccountPassword).value as string
const currentRootKey = await this.encryption.computeRootKey(
password,
this.encryption.getRootKeyParams() as SNRootKeyParams,
)
const serverPassword = currentRootKey.serverPassword
const response = await this.userApi.deleteAccount({ userUuid: uuid, serverPassword: serverPassword })
if (isErrorResponse(response)) {
return {
error: true,
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.209.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/snjs
## [2.209.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-15)
**Note:** Version bump only for package @standardnotes/snjs
@@ -1026,6 +1026,7 @@ export class Dependencies {
return new GetRecoveryCodes(
this.get<AuthManager>(TYPES.AuthManager),
this.get<SettingsService>(TYPES.SettingsService),
this.get<EncryptionService>(TYPES.EncryptionService),
)
})
@@ -1231,6 +1232,7 @@ export class Dependencies {
this.get<PureCryptoInterface>(TYPES.Crypto),
this.get<FeaturesService>(TYPES.FeaturesService),
this.get<ProtectionsClientInterface>(TYPES.ProtectionService),
this.get<EncryptionService>(TYPES.EncryptionService),
this.get<InternalEventBus>(TYPES.InternalEventBus),
)
})
@@ -1,4 +1,4 @@
import { AuthClientInterface } from '@standardnotes/services'
import { AuthClientInterface, EncryptionService } from '@standardnotes/services'
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
import { GetRecoveryCodes } from './GetRecoveryCodes'
@@ -6,8 +6,9 @@ import { GetRecoveryCodes } from './GetRecoveryCodes'
describe('GetRecoveryCodes', () => {
let authClient: AuthClientInterface
let settingsClient: SettingsClientInterface
let encryption: EncryptionService
const createUseCase = () => new GetRecoveryCodes(authClient, settingsClient)
const createUseCase = () => new GetRecoveryCodes(authClient, settingsClient, encryption)
beforeEach(() => {
authClient = {} as jest.Mocked<AuthClientInterface>
@@ -15,12 +16,16 @@ describe('GetRecoveryCodes', () => {
settingsClient = {} as jest.Mocked<SettingsClientInterface>
settingsClient.getSetting = jest.fn().mockResolvedValue('existing-recovery-codes')
encryption = {} as jest.Mocked<EncryptionService>
encryption.computeRootKey = jest.fn().mockResolvedValue({ serverPassword: 'test-server-password' })
encryption.getRootKeyParams = jest.fn().mockReturnValue({ algorithm: 'test-algorithm' })
})
it('should return existing recovery code if they exist', async () => {
const useCase = createUseCase()
const result = await useCase.execute()
const result = await useCase.execute({ password: 'test-password' })
expect(result.getValue()).toBe('existing-recovery-codes')
})
@@ -30,7 +35,7 @@ describe('GetRecoveryCodes', () => {
const useCase = createUseCase()
const result = await useCase.execute()
const result = await useCase.execute({ password: 'test-password' })
expect(result.getValue()).toBe('recovery-codes')
})
@@ -41,7 +46,7 @@ describe('GetRecoveryCodes', () => {
const useCase = createUseCase()
const result = await useCase.execute()
const result = await useCase.execute({ password: 'test-password' })
expect(result.isFailed()).toBe(true)
})
@@ -1,23 +1,40 @@
import { AuthClientInterface } from '@standardnotes/services'
import { AuthClientInterface, EncryptionService } from '@standardnotes/services'
import { Result, SettingName, UseCaseInterface } from '@standardnotes/domain-core'
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
import { GetRecoveryCodesDTO } from './GetRecoveryCodesDTO'
import { SNRootKeyParams } from '@standardnotes/encryption'
export class GetRecoveryCodes implements UseCaseInterface<string> {
constructor(
private authClient: AuthClientInterface,
private settingsClient: SettingsClientInterface,
private encryption: EncryptionService,
) {}
async execute(): Promise<Result<string>> {
async execute(dto: GetRecoveryCodesDTO): Promise<Result<string>> {
if (!dto.password) {
return Result.fail('Password is required to get recovery code.')
}
const currentRootKey = await this.encryption.computeRootKey(
dto.password,
this.encryption.getRootKeyParams() as SNRootKeyParams,
)
const serverPassword = currentRootKey.serverPassword
if (!serverPassword) {
return Result.fail('Could not compute server password')
}
const existingRecoveryCodes = await this.settingsClient.getSetting(
SettingName.create(SettingName.NAMES.RecoveryCodes).getValue(),
serverPassword,
)
if (existingRecoveryCodes !== undefined) {
return Result.ok(existingRecoveryCodes)
}
const generatedRecoveryCodes = await this.authClient.generateRecoveryCodes()
const generatedRecoveryCodes = await this.authClient.generateRecoveryCodes({ serverPassword })
if (generatedRecoveryCodes === false) {
return Result.fail('Could not generate recovery code')
}
@@ -0,0 +1,3 @@
export interface GetRecoveryCodesDTO {
password?: string
}
+14 -2
View File
@@ -578,12 +578,18 @@ export class LegacyApiService
})
}
async getSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<GetSettingResponse>> {
async getSetting(
userUuid: UuidString,
settingName: string,
serverPassword?: string,
): Promise<HttpResponse<GetSettingResponse>> {
const customHeaders = serverPassword ? [{ key: 'x-server-password', value: serverPassword }] : undefined
return await this.tokenRefreshableRequest<GetSettingResponse>({
verb: HttpVerb.Get,
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName.toLowerCase())),
authentication: this.getSessionAccessToken(),
fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
customHeaders,
})
}
@@ -616,12 +622,18 @@ export class LegacyApiService
})
}
async deleteSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<DeleteSettingResponse>> {
async deleteSetting(
userUuid: UuidString,
settingName: string,
serverPassword?: string,
): Promise<HttpResponse<DeleteSettingResponse>> {
const customHeaders = serverPassword ? [{ key: 'x-server-password', value: serverPassword }] : undefined
return this.tokenRefreshableRequest<DeleteSettingResponse>({
verb: HttpVerb.Delete,
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName)),
authentication: this.getSessionAccessToken(),
fallbackErrorMessage: API_MESSAGE_FAILED_UPDATE_SETTINGS,
customHeaders,
})
}
+18 -2
View File
@@ -6,9 +6,12 @@ import {
InternalEventBusInterface,
MfaServiceInterface,
ProtectionsClientInterface,
EncryptionService,
SignInStrings,
ChallengeValidation,
} from '@standardnotes/services'
import { SettingName } from '@standardnotes/domain-core'
import { SNRootKeyParams } from '@standardnotes/encryption'
export class MfaService extends AbstractService implements MfaServiceInterface {
constructor(
@@ -16,6 +19,7 @@ export class MfaService extends AbstractService implements MfaServiceInterface {
private crypto: PureCryptoInterface,
private featuresService: FeaturesService,
private protections: ProtectionsClientInterface,
private encryption: EncryptionService,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
@@ -55,11 +59,23 @@ export class MfaService extends AbstractService implements MfaServiceInterface {
}
async disableMfa(): Promise<void> {
if (!(await this.protections.authorizeMfaDisable())) {
const { success, challengeResponse } = await this.protections.authorizeMfaDisable()
if (!success) {
return
}
return await this.settingsService.deleteSetting(SettingName.create(SettingName.NAMES.MfaSecret).getValue())
const password = challengeResponse?.getValueForType(ChallengeValidation.AccountPassword).value as string
const currentRootKey = await this.encryption.computeRootKey(
password,
this.encryption.getRootKeyParams() as SNRootKeyParams,
)
const serverPassword = currentRootKey.serverPassword
return await this.settingsService.deleteSetting(
SettingName.create(SettingName.NAMES.MfaSecret).getValue(),
serverPassword,
)
}
override deinit(): void {
@@ -34,6 +34,7 @@ import {
import { ContentType } from '@standardnotes/domain-core'
import { isValidProtectionSessionLength } from './isValidProtectionSessionLength'
import { UnprotectedAccessSecondsDuration } from './UnprotectedAccessSecondsDuration'
import { ChallengeResponse } from '../Challenge'
/**
* Enforces certain actions to require extra authentication,
@@ -246,11 +247,11 @@ export class ProtectionService
})
}
async authorizeMfaDisable(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.DisableMfa, {
async authorizeMfaDisable(): Promise<{ success: boolean; challengeResponse?: ChallengeResponse }> {
return this.authorizeActionWithChallengeResponse(ChallengeReason.DisableMfa, {
fallBackToAccountPassword: true,
requireAccountPassword: true,
forcePrompt: false,
forcePrompt: true,
})
}
@@ -278,6 +279,14 @@ export class ProtectionService
})
}
async authorizeAccountDeletion(): Promise<{ success: boolean; challengeResponse?: ChallengeResponse }> {
return this.authorizeActionWithChallengeResponse(ChallengeReason.DeleteAccount, {
fallBackToAccountPassword: true,
requireAccountPassword: true,
forcePrompt: true,
})
}
async authorizeAction(
reason: ChallengeReason,
dto: { fallBackToAccountPassword: boolean; requireAccountPassword: boolean; forcePrompt: boolean },
@@ -285,6 +294,13 @@ export class ProtectionService
return this.validateOrRenewSession(reason, dto)
}
async authorizeActionWithChallengeResponse(
reason: ChallengeReason,
dto: { fallBackToAccountPassword: boolean; requireAccountPassword: boolean; forcePrompt: boolean },
): Promise<{ success: boolean; challengeResponse?: ChallengeResponse }> {
return this.validateOrRenewSessionWithChallengeResponse(reason, dto)
}
getMobilePasscodeTimingOptions(): TimingDisplayOption[] {
return [
{
@@ -353,8 +369,20 @@ export class ProtectionService
reason: ChallengeReason,
{ fallBackToAccountPassword = true, requireAccountPassword = false, forcePrompt = false } = {},
): Promise<boolean> {
const response = await this.validateOrRenewSessionWithChallengeResponse(reason, {
fallBackToAccountPassword,
requireAccountPassword,
forcePrompt,
})
return response.success
}
private async validateOrRenewSessionWithChallengeResponse(
reason: ChallengeReason,
{ fallBackToAccountPassword = true, requireAccountPassword = false, forcePrompt = false } = {},
): Promise<{ success: boolean; challengeResponse?: ChallengeResponse }> {
if (this.getSessionExpiryDate() > new Date() && !forcePrompt) {
return true
return { success: true }
}
const prompts: ChallengePrompt[] = []
@@ -378,9 +406,10 @@ export class ProtectionService
if (fallBackToAccountPassword && this.encryption.hasAccount()) {
prompts.push(new ChallengePrompt(ChallengeValidation.AccountPassword))
} else {
return true
return { success: true }
}
}
const lastSessionLength = this.getLastSessionLength()
const chosenSessionLength = isValidProtectionSessionLength(lastSessionLength)
? lastSessionLength
@@ -407,9 +436,9 @@ export class ProtectionService
} else {
this.setSessionLength(length as UnprotectedAccessSecondsDuration)
}
return true
return { success: true, challengeResponse: response }
} else {
return false
return { success: false }
}
}
@@ -30,8 +30,8 @@ export class SettingsService extends AbstractService implements SettingsClientIn
return this.provider.listSettings()
}
async getSetting(name: SettingName) {
return this.provider.getSetting(name)
async getSetting(name: SettingName, serverPassword?: string) {
return this.provider.getSetting(name, serverPassword)
}
async getSubscriptionSetting(name: SettingName) {
@@ -50,8 +50,8 @@ export class SettingsService extends AbstractService implements SettingsClientIn
return this.provider.getDoesSensitiveSettingExist(name)
}
async deleteSetting(name: SettingName) {
return this.provider.deleteSetting(name)
async deleteSetting(name: SettingName, serverPassword?: string) {
return this.provider.deleteSetting(name, serverPassword)
}
getEmailBackupFrequencyOptionLabel(frequency: EmailBackupFrequency): string {
@@ -5,13 +5,13 @@ import { SettingName } from '@standardnotes/domain-core'
export interface SettingsClientInterface {
listSettings(): Promise<SettingsList>
getSetting(name: SettingName): Promise<string | undefined>
getSetting(name: SettingName, serverPassword?: string): Promise<string | undefined>
getDoesSensitiveSettingExist(name: SettingName): Promise<boolean>
updateSetting(name: SettingName, payload: string, sensitive?: boolean): Promise<void>
deleteSetting(name: SettingName): Promise<void>
deleteSetting(name: SettingName, serverPassword?: string): Promise<void>
getEmailBackupFrequencyOptionLabel(frequency: EmailBackupFrequency): string
}
@@ -45,8 +45,8 @@ export class SettingsGateway {
return settings
}
async getSetting(name: SettingName): Promise<string | undefined> {
const response = await this.settingsApi.getSetting(this.userUuid, name.value)
async getSetting(name: SettingName, serverPassword?: string): Promise<string | undefined> {
const response = await this.settingsApi.getSetting(this.userUuid, name.value, serverPassword)
if (response.status === HttpStatusCode.BadRequest) {
return undefined
@@ -109,8 +109,8 @@ export class SettingsGateway {
}
}
async deleteSetting(name: SettingName): Promise<void> {
const response = await this.settingsApi.deleteSetting(this.userUuid, name.value)
async deleteSetting(name: SettingName, serverPassword?: string): Promise<void> {
const response = await this.settingsApi.deleteSetting(this.userUuid, name.value, serverPassword)
if (isErrorResponse(response)) {
throw new Error(getErrorFromErrorResponse(response).message)
}
@@ -17,7 +17,11 @@ export interface SettingsServerInterface {
sensitive: boolean,
): Promise<HttpResponse<UpdateSettingResponse>>
getSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<GetSettingResponse>>
getSetting(
userUuid: UuidString,
settingName: string,
serverPassword?: string,
): Promise<HttpResponse<GetSettingResponse>>
getSubscriptionSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<GetSettingResponse>>
@@ -28,5 +32,9 @@ export interface SettingsServerInterface {
sensitive: boolean,
): Promise<HttpResponse<UpdateSettingResponse>>
deleteSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<DeleteSettingResponse>>
deleteSetting(
userUuid: UuidString,
settingName: string,
serverPassword?: string,
): Promise<HttpResponse<DeleteSettingResponse>>
}
+47 -1
View File
@@ -549,6 +549,24 @@ describe('basic auth', function () {
expect(sendChallengeSpy.callCount).to.equal(1)
}).timeout(Factory.TenSecondTimeout)
it('should send server password when deleting account', async function () {
Factory.handlePasswordChallenges(context.application, context.password)
const userApiService = context.application.dependencies.get(TYPES.UserApiService)
const deleteAccountSpy = sinon.spy(userApiService, 'deleteAccount')
await context.application.user.deleteAccount()
expect(deleteAccountSpy.callCount).to.equal(1)
const deleteAccountCall = deleteAccountSpy.getCall(0)
const callArgs = deleteAccountCall.args[0]
expect(callArgs).to.have.property('serverPassword')
expect(callArgs.serverPassword).to.not.be.undefined
expect(typeof callArgs.serverPassword).to.equal('string')
expect(callArgs.serverPassword.length).to.be.above(0)
}).timeout(Factory.TenSecondTimeout)
it('deleting account should sign out current user', async function () {
Factory.handlePasswordChallenges(context.application, context.password)
@@ -567,12 +585,40 @@ describe('basic auth', function () {
const response = await context.application.dependencies
.get(TYPES.UserApiService)
.deleteAccount(registerResponse.user.uuid)
.deleteAccount({
userUuid: registerResponse.user.uuid,
serverPassword: 'dummy-password'
})
expect(response.status).to.equal(401)
expect(response.data.error.message).to.equal('Operation not allowed.')
await secondContext.deinit()
})
it('should not allow deleting account if server password is not sent', async function () {
Factory.handlePasswordChallenges(context.application, context.password)
const response = await context.application.dependencies
.get(TYPES.UserApiService)
.deleteAccount({
userUuid: context.application.user.uuid,
})
expect(response.status).to.equal(400)
}).timeout(Factory.TenSecondTimeout)
it('should not allow deleting account if server password is incorrect', async function () {
Factory.handlePasswordChallenges(context.application, context.password)
const response = await context.application.dependencies
.get(TYPES.UserApiService)
.deleteAccount({
userUuid: context.application.user.uuid,
serverPassword: 'wrong-password'
})
expect(response.status).to.equal(400)
}).timeout(Factory.TenSecondTimeout)
})
})
+61
View File
@@ -65,6 +65,7 @@ describe('mfa service', () => {
const token = await application.mfa.getOtpToken(secret)
sinon.spy(application.challenges, 'sendChallenge')
await application.mfa.enableMfa(secret, token)
await application.mfa.disableMfa()
@@ -73,4 +74,64 @@ describe('mfa service', () => {
expect(challenge.prompts).to.have.lengthOf(2)
expect(challenge.prompts[0].validation).to.equal(ChallengeValidation.AccountPassword)
}).timeout(Factory.TenSecondTimeout)
it('sends server password when disabling mfa', async () => {
await registerApp(application)
Factory.handlePasswordChallenges(application, accountPassword)
const secret = await application.mfa.generateMfaSecret()
const token = await application.mfa.getOtpToken(secret)
await application.mfa.enableMfa(secret, token)
sinon.spy(application.settings.settingsApi, 'deleteSetting')
await application.mfa.disableMfa()
const deleteSettingCall = application.settings.settingsApi.deleteSetting.getCall(0)
const [serverPassword] = deleteSettingCall.args
expect(typeof serverPassword).to.equal('string')
expect(serverPassword.length).to.be.above(0)
}).timeout(Factory.TenSecondTimeout)
it('should not allow disabling mfa if server password is not sent', async function () {
await registerApp(application)
Factory.handlePasswordChallenges(application, accountPassword)
const secret = await application.mfa.generateMfaSecret()
const token = await application.mfa.getOtpToken(secret)
await application.mfa.enableMfa(secret, token)
const response = await application.dependencies
.get(TYPES.SettingsApiService)
.deleteSetting({
userUuid: application.user.uuid,
settingName: 'MFA_SECRET',
})
expect(response.status).to.equal(400)
}).timeout(Factory.TenSecondTimeout)
it('should not allow disabling mfa if server password is incorrect', async function () {
await registerApp(application)
Factory.handlePasswordChallenges(application, accountPassword)
const secret = await application.mfa.generateMfaSecret()
const token = await application.mfa.getOtpToken(secret)
await application.mfa.enableMfa(secret, token)
const response = await application.dependencies
.get(TYPES.SettingsApiService)
.deleteSetting({
userUuid: application.user.uuid,
settingName: 'MFA_SECRET',
serverPassword: 'wrong-password'
})
expect(response.status).to.equal(400)
}).timeout(Factory.TenSecondTimeout)
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.209.12",
"version": "2.209.13",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.38.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.38.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-15)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.38.11",
"version": "1.38.12",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+42
View File
@@ -3,6 +3,48 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.198.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)
**Note:** Version bump only for package @standardnotes/web
## [3.198.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-25)
**Note:** Version bump only for package @standardnotes/web
## [3.198.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-11)
### Bug Fixes
* Fixes Share note option not working on Android for note titles with special characters ([#2928](https://github.com/standardnotes/app/issues/2928)) ([d135e09](https://github.com/standardnotes/app/commit/d135e09bda39814585a3d76b0544ed8b5834b4d0))
## [3.198.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-05)
### Bug Fixes
* Fixes Back and Navigation buttons not working on mobile view when panels explicitly collapsed ([#2926](https://github.com/standardnotes/app/issues/2926)) ([0108648](https://github.com/standardnotes/app/commit/010864881d0c056e88dae2394fb398c0f1e822a5))
## [3.198.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-04)
### Bug Fixes
* Prevents quick type suggestions on iOS for password input ([ff80775](https://github.com/standardnotes/app/commit/ff80775cd6f09863429f053e590a054c6e45722c))
## [3.198.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-29)
### Bug Fixes
* Fixed font size not updating on plain editor when changing it from Preferences ([#2920](https://github.com/standardnotes/app/issues/2920)) ([0d17961](https://github.com/standardnotes/app/commit/0d17961570cdadbbb909709075bdef3b8d77434b))
* Fixes extra bottom padding in account modal ([63d8472](https://github.com/standardnotes/app/commit/63d84725d2084d014de6aa73e74ba77a7e343484))
* Fixes quick type suggestions showing up in iOS for password field ([9583d0d](https://github.com/standardnotes/app/commit/9583d0db44ba315e125fbc36b50a8da527ae3a89))
## [3.198.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-23)
**Note:** Version bump only for package @standardnotes/web
## [3.198.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/web
## [3.198.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)
**Note:** Version bump only for package @standardnotes/web
+104
View File
@@ -1,5 +1,109 @@
{
"versions": [
{
"version": "3.198.17",
"title": "[3.198.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-26)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.198.16",
"title": "[3.198.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-25)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.198.15",
"title": "[3.198.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-11)",
"date": null,
"body": "### Bug Fixes\n\n* Fixes Share note option not working on Android for note titles with special characters ([#2928](https://github.com/standardnotes/app/issues/2928)) ([d135e09](https://github.com/standardnotes/app/commit/d135e09bda39814585a3d76b0544ed8b5834b4d0))",
"parsed": {
"_": [
"Fixes Share note option not working on Android for note titles with special characters (#2928) (d135e09)"
],
"Bug Fixes": [
"Fixes Share note option not working on Android for note titles with special characters (#2928) (d135e09)"
]
}
},
{
"version": "3.198.14",
"title": "[3.198.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-05)",
"date": null,
"body": "### Bug Fixes\n\n* Fixes Back and Navigation buttons not working on mobile view when panels explicitly collapsed ([#2926](https://github.com/standardnotes/app/issues/2926)) ([0108648](https://github.com/standardnotes/app/commit/010864881d0c056e88dae2394fb398c0f1e822a5))",
"parsed": {
"_": [
"Fixes Back and Navigation buttons not working on mobile view when panels explicitly collapsed (#2926) (0108648)"
],
"Bug Fixes": [
"Fixes Back and Navigation buttons not working on mobile view when panels explicitly collapsed (#2926) (0108648)"
]
}
},
{
"version": "3.198.13",
"title": "[3.198.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-08-04)",
"date": null,
"body": "### Bug Fixes\n\n* Prevents quick type suggestions on iOS for password input ([ff80775](https://github.com/standardnotes/app/commit/ff80775cd6f09863429f053e590a054c6e45722c))",
"parsed": {
"_": [
"Prevents quick type suggestions on iOS for password input (ff80775)"
],
"Bug Fixes": [
"Prevents quick type suggestions on iOS for password input (ff80775)"
]
}
},
{
"version": "3.198.12",
"title": "[3.198.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-29)",
"date": null,
"body": "### Bug Fixes\n\n* Fixed font size not updating on plain editor when changing it from Preferences ([#2920](https://github.com/standardnotes/app/issues/2920)) ([0d17961](https://github.com/standardnotes/app/commit/0d17961570cdadbbb909709075bdef3b8d77434b))\n* Fixes extra bottom padding in account modal ([63d8472](https://github.com/standardnotes/app/commit/63d84725d2084d014de6aa73e74ba77a7e343484))\n* Fixes quick type suggestions showing up in iOS for password field ([9583d0d](https://github.com/standardnotes/app/commit/9583d0db44ba315e125fbc36b50a8da527ae3a89))",
"parsed": {
"_": [
"Fixed font size not updating on plain editor when changing it from Preferences (#2920) (0d17961)",
"Fixes extra bottom padding in account modal (63d8472)",
"Fixes quick type suggestions showing up in iOS for password field (9583d0d)"
],
"Bug Fixes": [
"Fixed font size not updating on plain editor when changing it from Preferences (#2920) (0d17961)",
"Fixes extra bottom padding in account modal (63d8472)",
"Fixes quick type suggestions showing up in iOS for password field (9583d0d)"
]
}
},
{
"version": "3.198.11",
"title": "[3.198.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-23)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.198.10",
"title": "[3.198.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.198.9",
"title": "[3.198.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2025-07-22)",
+2 -2
View File
@@ -1,9 +1,9 @@
{
"name": "@standardnotes/web",
"version": "3.198.9",
"version": "3.198.17",
"license": "AGPL-3.0",
"main": "dist/app.js",
"author": "Standard Notes",
"author": "Standard Notes.",
"private": true,
"files": [
"dist"
@@ -75,6 +75,8 @@ const DecoratedInput = forwardRef(
<input
autoComplete={autocomplete ? 'on' : 'off'}
autoCorrect={autocomplete ? 'on' : 'off'}
autoCapitalize={autocomplete ? 'on' : 'off'}
autoFocus={autofocus}
className={`${computedClassNames.input} ${disabled ? computedClassNames.disabled : ''} ${className?.input}`}
data-lpignore={type !== 'password' ? true : false}
@@ -30,6 +30,8 @@ const DecoratedPasswordInput = forwardRef((props: DecoratedInputProps, ref: Ref<
{...props}
ref={ref}
type={isToggled ? 'text' : 'password'}
autocomplete={false}
spellcheck={false}
right={[...rightSideDecorations, <Toggle isToggled={isToggled} setIsToggled={setIsToggled} />]}
/>
)
@@ -190,8 +190,11 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
useEffect(() => {
reloadPreferences()
return application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
reloadPreferences()
return application.addEventObserver(async (event) => {
const events = [ApplicationEvent.PreferencesChanged, ApplicationEvent.LocalPreferencesChanged]
if (events.includes(event)) {
reloadPreferences()
}
})
}, [reloadPreferences, application])
@@ -11,6 +11,7 @@ import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/u
import { SupportsPassiveListeners } from '@/Constants/Constants'
import { useLifecycleAnimation } from '@/Hooks/useLifecycleAnimation'
import { getScrollParent } from '@/Utils'
import { useAvailableSafeAreaPadding } from '@/Hooks/useSafeAreaPadding'
const DisableScroll = () => {
useDisableBodyScrollOnMobile()
@@ -63,6 +64,8 @@ const MobilePopoverContent = ({
},
})
const { hasBottomInset } = useAvailableSafeAreaPadding()
useEffect(() => {
if (!element) {
return
@@ -181,7 +184,8 @@ const MobilePopoverContent = ({
<div
ref={mergeRefs([setPopoverElement, addCloseMethod])}
className={classNames(
'z-1 absolute bottom-0 flex max-h-[calc(100%_-_max(var(--safe-area-inset-top),2rem))] min-h-[40%] w-full flex-col rounded-t-xl bg-passive-5 pb-safe-bottom',
'z-1 absolute bottom-0 flex max-h-[calc(100%_-_max(var(--safe-area-inset-top),2rem))] min-h-[40%] w-full flex-col rounded-t-xl bg-passive-5',
hasBottomInset && 'pb-safe-bottom',
forceFullHeightOnMobile && 'h-full',
)}
style={{
@@ -10,13 +10,13 @@ const RecoveryCodeBanner = ({ application }: { application: WebApplication }) =>
const [errorMessage, setErrorMessage] = useState<string>()
const onClickShow = async () => {
const authorized = await application.challenges.promptForAccountPassword()
const password = await application.challenges.promptForAccountPassword()
if (!authorized) {
if (!password) {
return
}
const recoveryCodeOrError = await application.getRecoveryCodes.execute()
const recoveryCodeOrError = await application.getRecoveryCodes.execute({ password })
if (recoveryCodeOrError.isFailed()) {
setErrorMessage(recoveryCodeOrError.getError())
return
@@ -1,7 +1,6 @@
import TagsList from '@/Components/Tags/TagsList'
import { ApplicationEvent } from '@/__mocks__/@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
import { FunctionComponent } from 'react'
import TagsSectionAddButton from './TagsSectionAddButton'
import TagsSectionTitle from './TagsSectionTitle'
import { useApplication } from '../ApplicationProvider'
@@ -9,47 +8,6 @@ import { useApplication } from '../ApplicationProvider'
const TagsSection: FunctionComponent = () => {
const application = useApplication()
const [hasMigration, setHasMigration] = useState<boolean>(false)
const checkIfMigrationNeeded = useCallback(() => {
setHasMigration(application.items.hasTagsNeedingFoldersMigration())
}, [application])
useEffect(() => {
const removeObserver = application.addEventObserver(async (event) => {
const events = [ApplicationEvent.CompletedInitialSync, ApplicationEvent.SignedIn]
if (events.includes(event)) {
checkIfMigrationNeeded()
}
})
return () => {
removeObserver()
}
}, [application, checkIfMigrationNeeded])
const runMigration = useCallback(async () => {
if (
await application.alerts.confirm(
'<i>Introducing native, built-in nested tags without requiring the legacy Folders extension.</i><br/></br> ' +
" To get started, we'll need to migrate any tags containing a dot character to the new system.<br/></br> " +
' This migration will convert any tags with dots appearing in their name into a natural' +
' hierarchy that is compatible with the new nested tags feature.' +
' Running this migration will remove any "." characters appearing in tag names.',
'New: Folders to Nested Tags',
'Run Migration',
)
) {
application.mutator
.migrateTagsToFolders()
.then(() => {
void application.sync.sync()
checkIfMigrationNeeded()
})
.catch(console.error)
}
}, [application, checkIfMigrationNeeded])
return (
<>
{application.navigationController.starredTags.length > 0 && (
@@ -68,11 +26,7 @@ const TagsSection: FunctionComponent = () => {
<section>
<div className={'section-title-bar'}>
<div className="section-title-bar-header">
<TagsSectionTitle
features={application.featuresController}
hasMigration={hasMigration}
onClickMigration={runMigration}
/>
<TagsSectionTitle features={application.featuresController} />
{!application.navigationController.isSearching && (
<TagsSectionAddButton tags={application.navigationController} features={application.featuresController} />
)}
@@ -7,11 +7,9 @@ import StyledTooltip from '../StyledTooltip/StyledTooltip'
type Props = {
features: FeaturesController
hasMigration: boolean
onClickMigration: () => void
}
const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, onClickMigration }) => {
const TagsSectionTitle: FunctionComponent<Props> = ({ features }) => {
const entitledToFolders = features.hasFolders
const modal = usePremiumModal()
@@ -24,11 +22,6 @@ const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, on
<>
<div className="title text-base md:text-sm">
<span className="font-bold">Folders</span>
{hasMigration && (
<label className="ml-1 cursor-pointer font-bold text-info" onClick={onClickMigration}>
Migration Available
</label>
)}
</div>
</>
)
@@ -213,11 +213,15 @@ export class PaneController extends AbstractViewController implements InternalEv
const panes = this._panesForLayout.execute(layout).getValue()
if (panes.includes(AppPaneId.Items) && this.listPaneExplicitelyCollapsed) {
if (panes.includes(AppPaneId.Items) && this.listPaneExplicitelyCollapsed && layout !== PaneLayout.ItemSelection) {
removeFromArray(panes, AppPaneId.Items)
}
if (panes.includes(AppPaneId.Navigation) && this.navigationPaneExplicitelyCollapsed) {
if (
panes.includes(AppPaneId.Navigation) &&
this.navigationPaneExplicitelyCollapsed &&
layout !== PaneLayout.TagSelection
) {
removeFromArray(panes, AppPaneId.Navigation)
}
@@ -10,7 +10,7 @@ import snColorsCSS from '!css-loader?{"sourceMap":false}!sass-loader!@standardno
// @ts-expect-error Using inline loaders to load CSS as string
import exportOverridesCSS from '!css-loader?{"sourceMap":false}!sass-loader!../Components/SuperEditor/Lexical/Theme/export-overrides.scss'
import { getBase64FromBlob } from './Utils'
import { parseFileName, parseAndCreateZippableFileName } from '@standardnotes/utils'
import { parseFileName, parseAndCreateZippableFileName, sanitizeFileName } from '@standardnotes/utils'
export const getNoteFormat = (application: WebApplicationInterface, note: SNNote) => {
if (note.noteType === NoteType.Super) {
@@ -28,7 +28,8 @@ export const getNoteFormat = (application: WebApplicationInterface, note: SNNote
export const getNoteFileName = (application: WebApplicationInterface, note: SNNote): string => {
const format = getNoteFormat(application, note)
return `${note.title}.${format}`
const filename = sanitizeFileName(note.title)
return `${filename}.${format}`
}
const headlessSuperConverter = new HeadlessSuperConverter()