Compare commits

...
Author SHA1 Message Date
StandardNotes CI 8ff7fb605a chore(release): publish
- @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].0
2023-01-03 20:43:29 +00:00
Mo 3c332a35f6 feat: improve initial load performance on mobile (#2126) 2023-01-03 14:15:45 -06:00
Mo a447fa1ad7 test: fix e2e testing setup 2023-01-03 11:00:12 -06:00
Mo 76d2d2112b ci: remove linux ia32 builds
Electron no longer offers builds for this architecture
2023-01-03 10:42:04 -06:00
StandardNotes CI 63cd1f9b07 chore(release): publish
- @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
2023-01-03 07:12:09 +00:00
Aman Harwara fbc93a4f28 refactor: only show open filesend menu option on mobile 2023-01-03 11:53:44 +05:30
Mo a896b4d9cf chore: remove redunant electronVersion from desktop build params 2023-01-02 23:26:15 -06:00
Mo c0a0ea185c chore(ci): fix mobile version script 2023-01-01 15:28:43 -06:00
StandardNotes CI a3d8f1f3ff chore(release): publish
- @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-12-29 20:21:46 +00:00
Aman Harwara 6e4bf2417a feat: Added "Import" option in the account menu that allows you to import from plaintext/markdown files, Evernote exports, Simplenote exports, Google Keep exports and also convert Aegis exports to TokenVault notes 2022-12-30 01:22:59 +05:30
StandardNotes CI f1ae7a53ef chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
2022-12-29 16:11:00 +00:00
Aman Harwara cd5bd1640c fix: Use regular file list on mobile instead of table view 2022-12-29 21:11:00 +05:30
StandardNotes CI 7508a23979 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].4
2022-12-29 15:30:13 +00:00
Karol Sójko 59e8b5c8b5 feat(api): add authenticators api (#2124)
* feat(api): add authenticators api

* fix(services): responses interpreting in authenticator manager
2022-12-29 16:04:53 +01:00
StandardNotes CI 5a8a37413e chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].3
2022-12-29 07:50:08 +00:00
Aman Harwara 113d3bebb4 fix: Fixed issue where Super note font size would shift on first load 2022-12-29 12:51:32 +05:30
Aman Harwara 874f2e90bb Merge branch 'main' of github.com:standardnotes/app 2022-12-29 12:31:59 +05:30
Aman Harwara df4acbb1bc fix: Fixed issue where converting a note to Super from the note options menu would not work 2022-12-29 12:31:50 +05:30
StandardNotes CI b75be1d0f8 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-12-29 05:02:30 +00:00
Mo 015dd1e806 chore: bump version (skip e2e) 2022-12-28 22:47:56 -06:00
Mo 8e3ae80aa0 fix: Fixed issue where Moments could incorrectly get engaged during biometrics unlocking 2022-12-28 22:20:44 -06:00
StandardNotes CI 5a0d87845c chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].0
2022-12-28 21:47:36 +00:00
Aman Harwara b764296c8f feat: Added "Files Table View" to Labs 2022-12-29 02:44:04 +05:30
StandardNotes CI b0c11b1b4d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-12-28 17:21:41 +00:00
Aman Harwara e81ecfc024 Update CHANGELOG.md 2022-12-28 22:20:28 +05:30
Aman Harwara b408d0a4c5 Update CHANGELOG.md.json 2022-12-28 22:20:08 +05:30
StandardNotes CI 9ec68c9bf6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-12-28 16:44:26 +00:00
Aman Harwara 2611c690b0 fix: Fixed issue where Moments would capture even after disabling (#2122) 2022-12-28 21:44:01 +05:30
173 changed files with 2866 additions and 3319 deletions
+2 -8
View File
@@ -74,7 +74,7 @@ jobs:
- name: Compile for AppImage
run: yarn run webpack --config desktop.webpack.prod.js
- name: AppImage
run: yarn run electron-builder --linux --x64 --ia32 -c.linux.target=AppImage --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
run: yarn run electron-builder --linux --x64 -c.linux.target=AppImage --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: AppImageX64
run: yarn run electron-builder --linux --x64 -c.linux.target=AppImage --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: AppImageArm64
@@ -83,7 +83,7 @@ jobs:
- name: Deb
run: |
yarn run webpack --config desktop.webpack.prod.js --env deb
yarn run electron-builder --linux --x64 --ia32 -c.linux.target=deb --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
yarn run electron-builder --linux --x64 -c.linux.target=deb --publish=never --c.extraMetadata.version=${{ env.APP_VERSION }}
- name: DebArm64
env:
npm_config_target_arch: 'arm64'
@@ -180,12 +180,6 @@ jobs:
- name: Generate Checksums
run: node scripts/sums.mjs
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@main
with:
path: packages/desktop
- name: Generate Release Notes
uses: ./actions/release-notes
with:
@@ -53,8 +53,3 @@ jobs:
with:
name: assets
path: packages/desktop/dist
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@main
with:
path: packages/desktop
+2 -8
View File
@@ -43,12 +43,6 @@ jobs:
lane: 'android prod'
subdirectory: 'packages/mobile'
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@main
with:
path: packages/mobile
- name: Generate Release Notes
uses: ./actions/release-notes
with:
@@ -60,8 +54,8 @@ jobs:
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.CI_PAT_TOKEN }}
tag_name: "@standardnotes/mobile@${{ steps.package-version.outputs.current-version}}"
name: "Mobile ${{ steps.package-version.outputs.current-version }}"
tag_name: "@standardnotes/mobile@${{ env.PACKAGE_VERSION }}"
name: "Mobile ${{ env.PACKAGE_VERSION }}"
body: ${{ steps.release-notes.outputs.result }}
prerelease: true
draft: false
@@ -42,12 +42,6 @@ jobs:
with:
lane: 'android dev'
subdirectory: 'packages/mobile'
- name: get-npm-version
id: package-version
uses: martinbeentjes/npm-get-version-action@main
with:
path: packages/mobile
ios:
env:
ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD: true
Binary file not shown.
+22
View File
@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/api
## [1.21.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/api
# [1.21.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
### Features
* **api:** add authenticators api ([#2124](https://github.com/standardnotes/app/issues/2124)) ([59e8b5c](https://github.com/standardnotes/app/commit/59e8b5c8b5b61ef1cd8f291798c45860736a1296))
## [1.20.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/api
## [1.20.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/api
## [1.20.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/api
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.20.24",
"version": "1.21.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -0,0 +1,8 @@
export enum AuthenticatorApiOperations {
List,
Delete,
GenerateRegistrationOptions,
GenerateAuthenticationOptions,
VerifyRegistrationResponse,
VerifyAuthenticationResponse,
}
@@ -0,0 +1,152 @@
import { ErrorMessage } from '../../Error/ErrorMessage'
import { ApiCallError } from '../../Error/ApiCallError'
import { AuthenticatorApiServiceInterface } from './AuthenticatorApiServiceInterface'
import { AuthenticatorApiOperations } from './AuthenticatorApiOperations'
import {
ListAuthenticatorsResponse,
DeleteAuthenticatorResponse,
GenerateAuthenticatorRegistrationOptionsResponse,
VerifyAuthenticatorRegistrationResponseResponse,
GenerateAuthenticatorAuthenticationOptionsResponse,
VerifyAuthenticatorAuthenticationResponseResponse,
} from '../../Response'
import { AuthenticatorServerInterface } from '../../Server/Authenticator/AuthenticatorServerInterface'
export class AuthenticatorApiService implements AuthenticatorApiServiceInterface {
private operationsInProgress: Map<AuthenticatorApiOperations, boolean>
constructor(private authenticatorServer: AuthenticatorServerInterface) {
this.operationsInProgress = new Map()
}
async list(): Promise<ListAuthenticatorsResponse> {
if (this.operationsInProgress.get(AuthenticatorApiOperations.List)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(AuthenticatorApiOperations.List, true)
try {
const response = await this.authenticatorServer.list({})
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(AuthenticatorApiOperations.List, false)
}
}
async delete(authenticatorId: string): Promise<DeleteAuthenticatorResponse> {
if (this.operationsInProgress.get(AuthenticatorApiOperations.Delete)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(AuthenticatorApiOperations.Delete, true)
try {
const response = await this.authenticatorServer.delete({
authenticatorId,
})
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(AuthenticatorApiOperations.Delete, false)
}
}
async generateRegistrationOptions(
userUuid: string,
username: string,
): Promise<GenerateAuthenticatorRegistrationOptionsResponse> {
if (this.operationsInProgress.get(AuthenticatorApiOperations.GenerateRegistrationOptions)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(AuthenticatorApiOperations.GenerateRegistrationOptions, true)
try {
const response = await this.authenticatorServer.generateRegistrationOptions({
username,
userUuid,
})
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(AuthenticatorApiOperations.GenerateRegistrationOptions, false)
}
}
async verifyRegistrationResponse(
userUuid: string,
name: string,
registrationCredential: Record<string, unknown>,
): Promise<VerifyAuthenticatorRegistrationResponseResponse> {
if (this.operationsInProgress.get(AuthenticatorApiOperations.VerifyRegistrationResponse)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(AuthenticatorApiOperations.VerifyRegistrationResponse, true)
try {
const response = await this.authenticatorServer.verifyRegistrationResponse({
userUuid,
name,
registrationCredential,
})
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(AuthenticatorApiOperations.VerifyRegistrationResponse, false)
}
}
async generateAuthenticationOptions(): Promise<GenerateAuthenticatorAuthenticationOptionsResponse> {
if (this.operationsInProgress.get(AuthenticatorApiOperations.GenerateAuthenticationOptions)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(AuthenticatorApiOperations.GenerateAuthenticationOptions, true)
try {
const response = await this.authenticatorServer.generateAuthenticationOptions({})
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(AuthenticatorApiOperations.GenerateAuthenticationOptions, false)
}
}
async verifyAuthenticationResponse(
userUuid: string,
authenticationCredential: Record<string, unknown>,
): Promise<VerifyAuthenticatorAuthenticationResponseResponse> {
if (this.operationsInProgress.get(AuthenticatorApiOperations.VerifyAuthenticationResponse)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(AuthenticatorApiOperations.VerifyAuthenticationResponse, true)
try {
const response = await this.authenticatorServer.verifyAuthenticationResponse({
authenticationCredential,
userUuid,
})
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(AuthenticatorApiOperations.VerifyAuthenticationResponse, false)
}
}
}
@@ -0,0 +1,27 @@
import {
ListAuthenticatorsResponse,
DeleteAuthenticatorResponse,
GenerateAuthenticatorRegistrationOptionsResponse,
VerifyAuthenticatorRegistrationResponseResponse,
GenerateAuthenticatorAuthenticationOptionsResponse,
VerifyAuthenticatorAuthenticationResponseResponse,
} from '../../Response'
export interface AuthenticatorApiServiceInterface {
list(): Promise<ListAuthenticatorsResponse>
delete(authenticatorId: string): Promise<DeleteAuthenticatorResponse>
generateRegistrationOptions(
userUuid: string,
username: string,
): Promise<GenerateAuthenticatorRegistrationOptionsResponse>
verifyRegistrationResponse(
userUuid: string,
name: string,
registrationCredential: Record<string, unknown>,
): Promise<VerifyAuthenticatorRegistrationResponseResponse>
generateAuthenticationOptions(): Promise<GenerateAuthenticatorAuthenticationOptionsResponse>
verifyAuthenticationResponse(
userUuid: string,
authenticationCredential: Record<string, unknown>,
): Promise<VerifyAuthenticatorAuthenticationResponseResponse>
}
+3
View File
@@ -1,3 +1,6 @@
export * from './Authenticator/AuthenticatorApiOperations'
export * from './Authenticator/AuthenticatorApiService'
export * from './Authenticator/AuthenticatorApiServiceInterface'
export * from './Subscription/SubscriptionApiOperations'
export * from './Subscription/SubscriptionApiService'
export * from './Subscription/SubscriptionApiServiceInterface'
@@ -0,0 +1,4 @@
export interface DeleteAuthenticatorRequestParams {
authenticatorId: string
[additionalParam: string]: unknown
}
@@ -0,0 +1,3 @@
export interface GenerateAuthenticatorAuthenticationOptionsRequestParams {
[additionalParam: string]: unknown
}
@@ -0,0 +1,5 @@
export interface GenerateAuthenticatorRegistrationOptionsRequestParams {
userUuid: string
username: string
[additionalParam: string]: unknown
}
@@ -0,0 +1,3 @@
export interface ListAuthenticatorsRequestParams {
[additionalParam: string]: unknown
}
@@ -0,0 +1,5 @@
export interface VerifyAuthenticatorAuthenticationResponseRequestParams {
userUuid: string
authenticationCredential: Record<string, unknown>
[additionalParam: string]: unknown
}
@@ -0,0 +1,6 @@
export interface VerifyAuthenticatorRegistrationResponseRequestParams {
userUuid: string
name: string
registrationCredential: Record<string, unknown>
[additionalParam: string]: unknown
}
+6
View File
@@ -1,4 +1,10 @@
export * from './ApiEndpointParam'
export * from './Authenticator/DeleteAuthenticatorRequestParams'
export * from './Authenticator/GenerateAuthenticatorAuthenticationOptionsRequestParams'
export * from './Authenticator/GenerateAuthenticatorRegistrationOptionsRequestParams'
export * from './Authenticator/ListAuthenticatorsRequestParams'
export * from './Authenticator/VerifyAuthenticatorAuthenticationResponseRequestParams'
export * from './Authenticator/VerifyAuthenticatorRegistrationResponseRequestParams'
export * from './Subscription/AppleIAPConfirmRequestParams'
export * from './Subscription/SubscriptionInviteAcceptRequestParams'
export * from './Subscription/SubscriptionInviteCancelRequestParams'
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { DeleteAuthenticatorResponseBody } from './DeleteAuthenticatorResponseBody'
export interface DeleteAuthenticatorResponse extends HttpResponse {
data: Either<DeleteAuthenticatorResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export interface DeleteAuthenticatorResponseBody {
message: string
}
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { GenerateAuthenticatorAuthenticationOptionsResponseBody } from './GenerateAuthenticatorAuthenticationOptionsResponseBody'
export interface GenerateAuthenticatorAuthenticationOptionsResponse extends HttpResponse {
data: Either<GenerateAuthenticatorAuthenticationOptionsResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export interface GenerateAuthenticatorAuthenticationOptionsResponseBody {
options: Record<string, unknown>
}
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { GenerateAuthenticatorRegistrationOptionsResponseBody } from './GenerateAuthenticatorRegistrationOptionsResponseBody'
export interface GenerateAuthenticatorRegistrationOptionsResponse extends HttpResponse {
data: Either<GenerateAuthenticatorRegistrationOptionsResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export interface GenerateAuthenticatorRegistrationOptionsResponseBody {
options: Record<string, unknown>
}
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { ListAuthenticatorsResponseBody } from './ListAuthenticatorsResponseBody'
export interface ListAuthenticatorsResponse extends HttpResponse {
data: Either<ListAuthenticatorsResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,6 @@
export interface ListAuthenticatorsResponseBody {
authenticators: Array<{
id: string
name: string
}>
}
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { VerifyAuthenticatorAuthenticationResponseResponseBody } from './VerifyAuthenticatorAuthenticationResponseResponseBody'
export interface VerifyAuthenticatorAuthenticationResponseResponse extends HttpResponse {
data: Either<VerifyAuthenticatorAuthenticationResponseResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export interface VerifyAuthenticatorAuthenticationResponseResponseBody {
success: boolean
}
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { VerifyAuthenticatorRegistrationResponseResponseBody } from './VerifyAuthenticatorRegistrationResponseResponseBody'
export interface VerifyAuthenticatorRegistrationResponseResponse extends HttpResponse {
data: Either<VerifyAuthenticatorRegistrationResponseResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export interface VerifyAuthenticatorRegistrationResponseResponseBody {
success: boolean
}
+14
View File
@@ -1,3 +1,17 @@
export * from './Auth/SessionRefreshResponse'
export * from './Auth/SessionRefreshResponseBody'
export * from './Authenticator/DeleteAuthenticatorResponse'
export * from './Authenticator/DeleteAuthenticatorResponseBody'
export * from './Authenticator/GenerateAuthenticatorAuthenticationOptionsResponse'
export * from './Authenticator/GenerateAuthenticatorAuthenticationOptionsResponseBody'
export * from './Authenticator/GenerateAuthenticatorRegistrationOptionsResponse'
export * from './Authenticator/GenerateAuthenticatorRegistrationOptionsResponseBody'
export * from './Authenticator/ListAuthenticatorsResponse'
export * from './Authenticator/ListAuthenticatorsResponseBody'
export * from './Authenticator/VerifyAuthenticatorAuthenticationResponseResponse'
export * from './Authenticator/VerifyAuthenticatorAuthenticationResponseResponseBody'
export * from './Authenticator/VerifyAuthenticatorRegistrationResponseResponse'
export * from './Authenticator/VerifyAuthenticatorRegistrationResponseResponseBody'
export * from './Subscription/AppleIAPConfirmResponse'
export * from './Subscription/AppleIAPConfirmResponseBody'
export * from './Subscription/SubscriptionInviteAcceptResponse'
@@ -0,0 +1,67 @@
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
import {
ListAuthenticatorsRequestParams,
DeleteAuthenticatorRequestParams,
GenerateAuthenticatorRegistrationOptionsRequestParams,
VerifyAuthenticatorRegistrationResponseRequestParams,
GenerateAuthenticatorAuthenticationOptionsRequestParams,
VerifyAuthenticatorAuthenticationResponseRequestParams,
} from '../../Request'
import {
ListAuthenticatorsResponse,
DeleteAuthenticatorResponse,
GenerateAuthenticatorRegistrationOptionsResponse,
VerifyAuthenticatorRegistrationResponseResponse,
GenerateAuthenticatorAuthenticationOptionsResponse,
VerifyAuthenticatorAuthenticationResponseResponse,
} from '../../Response'
import { AuthenticatorServerInterface } from './AuthenticatorServerInterface'
import { Paths } from './Paths'
export class AuthenticatorServer implements AuthenticatorServerInterface {
constructor(private httpService: HttpServiceInterface) {}
async list(params: ListAuthenticatorsRequestParams): Promise<ListAuthenticatorsResponse> {
const response = await this.httpService.get(Paths.v1.listAuthenticators, params)
return response as ListAuthenticatorsResponse
}
async delete(params: DeleteAuthenticatorRequestParams): Promise<DeleteAuthenticatorResponse> {
const response = await this.httpService.delete(Paths.v1.deleteAuthenticator(params.authenticatorId), params)
return response as DeleteAuthenticatorResponse
}
async generateRegistrationOptions(
params: GenerateAuthenticatorRegistrationOptionsRequestParams,
): Promise<GenerateAuthenticatorRegistrationOptionsResponse> {
const response = await this.httpService.get(Paths.v1.generateRegistrationOptions, params)
return response as GenerateAuthenticatorRegistrationOptionsResponse
}
async verifyRegistrationResponse(
params: VerifyAuthenticatorRegistrationResponseRequestParams,
): Promise<VerifyAuthenticatorRegistrationResponseResponse> {
const response = await this.httpService.post(Paths.v1.verifyRegistrationResponse, params)
return response as VerifyAuthenticatorRegistrationResponseResponse
}
async generateAuthenticationOptions(
params: GenerateAuthenticatorAuthenticationOptionsRequestParams,
): Promise<GenerateAuthenticatorAuthenticationOptionsResponse> {
const response = await this.httpService.get(Paths.v1.generateAuthenticationOptions, params)
return response as GenerateAuthenticatorAuthenticationOptionsResponse
}
async verifyAuthenticationResponse(
params: VerifyAuthenticatorAuthenticationResponseRequestParams,
): Promise<VerifyAuthenticatorAuthenticationResponseResponse> {
const response = await this.httpService.post(Paths.v1.verifyAuthenticationResponse, params)
return response as VerifyAuthenticatorAuthenticationResponseResponse
}
}
@@ -0,0 +1,33 @@
import {
ListAuthenticatorsRequestParams,
DeleteAuthenticatorRequestParams,
GenerateAuthenticatorRegistrationOptionsRequestParams,
VerifyAuthenticatorRegistrationResponseRequestParams,
GenerateAuthenticatorAuthenticationOptionsRequestParams,
VerifyAuthenticatorAuthenticationResponseRequestParams,
} from '../../Request'
import {
ListAuthenticatorsResponse,
DeleteAuthenticatorResponse,
GenerateAuthenticatorRegistrationOptionsResponse,
VerifyAuthenticatorRegistrationResponseResponse,
GenerateAuthenticatorAuthenticationOptionsResponse,
VerifyAuthenticatorAuthenticationResponseResponse,
} from '../../Response'
export interface AuthenticatorServerInterface {
list(params: ListAuthenticatorsRequestParams): Promise<ListAuthenticatorsResponse>
delete(params: DeleteAuthenticatorRequestParams): Promise<DeleteAuthenticatorResponse>
generateRegistrationOptions(
params: GenerateAuthenticatorRegistrationOptionsRequestParams,
): Promise<GenerateAuthenticatorRegistrationOptionsResponse>
verifyRegistrationResponse(
params: VerifyAuthenticatorRegistrationResponseRequestParams,
): Promise<VerifyAuthenticatorRegistrationResponseResponse>
generateAuthenticationOptions(
params: GenerateAuthenticatorAuthenticationOptionsRequestParams,
): Promise<GenerateAuthenticatorAuthenticationOptionsResponse>
verifyAuthenticationResponse(
params: VerifyAuthenticatorAuthenticationResponseRequestParams,
): Promise<VerifyAuthenticatorAuthenticationResponseResponse>
}
@@ -0,0 +1,14 @@
const AuthenticatorPaths = {
listAuthenticators: '/v1/authenticators',
deleteAuthenticator: (authenticatorId: string) => `/v1/authenticators/${authenticatorId}`,
generateRegistrationOptions: '/v1/authenticators/generate-registration-options',
verifyRegistrationResponse: '/v1/authenticators/verify-registration',
generateAuthenticationOptions: '/v1/authenticators/generate-authentication-options',
verifyAuthenticationResponse: '/v1/authenticators/verify-authentication',
}
export const Paths = {
v1: {
...AuthenticatorPaths,
},
}
+2
View File
@@ -1,3 +1,5 @@
export * from './Authenticator/AuthenticatorServer'
export * from './Authenticator/AuthenticatorServerInterface'
export * from './Subscription/SubscriptionServer'
export * from './Subscription/SubscriptionServerInterface'
export * from './User/UserServer'
+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.12.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/blocks-editor
## [1.12.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-26)
**Note:** Version bump only for package @standardnotes/blocks-editor
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/blocks-editor",
"version": "1.12.1",
"version": "1.12.2",
"private": true,
"main": "./src/index.ts",
"scripts": {
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.104.65](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.63](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.62](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.61](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.60](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.59](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.58](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.57](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.56](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.55](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.54](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/desktop
+1 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.104.54",
"version": "3.104.65",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
@@ -80,7 +80,6 @@
"webpack-merge": "^5.8.0"
},
"build": {
"electronVersion": "17.4.2",
"appId": "org.standardnotes.standardnotes",
"artifactName": "standard-notes-${version}-${os}-${arch}.${ext}",
"afterSign": "scripts/notarizeMac.js",
+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.
## [0.2.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/docs
## [0.2.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
### Bug Fixes
+1 -1
View File
@@ -178,7 +178,7 @@ Due to mounted volumes, we recommend running the setup as a root user. If you wi
1. Once the server has finished rebooting, log back into the server and start the Standard Notes server process:
```shell
cd standalone
cd self-hosted
./server.sh start
```
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/docs",
"version": "0.2.5",
"version": "0.2.6",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.19.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.31](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/encryption
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.19.29",
"version": "1.19.33",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.57.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/features
# [1.57.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
### Features
* Added "Files Table View" to Labs ([b764296](https://github.com/standardnotes/app/commit/b764296c8f831d06f854dcf2982e644c27e14e7c))
# [1.56.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/features",
"version": "1.56.0",
"version": "1.57.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -38,6 +38,8 @@ export enum FeatureIdentifier {
TaskEditor = 'org.standardnotes.simple-task-editor',
TokenVaultEditor = 'org.standardnotes.token-vault',
FilesTableView = 'org.standardnotes.files-table-view',
DeprecatedBoldEditor = 'org.standardnotes.bold-editor',
DeprecatedMarkdownBasicEditor = 'org.standardnotes.simple-markdown-editor',
DeprecatedMarkdownMathEditor = 'org.standardnotes.fancy-markdown-editor',
@@ -51,4 +53,4 @@ export enum FeatureIdentifier {
*/
export const LegacyFileSafeIdentifier = 'org.standardnotes.legacy.file-safe'
export const ExperimentalFeatures = []
export const ExperimentalFeatures = [FeatureIdentifier.FilesTableView]
@@ -1,5 +1,18 @@
import { RoleName, SubscriptionName } from '@standardnotes/common'
import { FeatureDescription } from '../Feature/FeatureDescription'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { PermissionName } from '../Permission/PermissionName'
export function experimentalFeatures(): FeatureDescription[] {
return []
const filesTableView: FeatureDescription = {
identifier: FeatureIdentifier.FilesTableView,
name: 'Files Table View',
description:
'Replaces the current Files view with a table view, with name, size, and date sort options. Requires reload to take effect.',
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
availableInRoles: [RoleName.PlusUser, RoleName.ProUser],
permission_name: PermissionName.FilesTableView,
}
return [filesTableView]
}
@@ -40,4 +40,5 @@ export enum PermissionName {
TwoFactorAuth = 'server:two-factor-auth',
SubscriptionSharing = 'server:subscription-sharing',
SuperEditor = 'editor:super-editor',
FilesTableView = 'app:files-table-view',
}
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.28.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
### Features
* improve initial load performance on mobile ([#2126](https://github.com/standardnotes/app/issues/2126)) ([3c332a3](https://github.com/standardnotes/app/commit/3c332a35f6024c46020a1d6ac68a5df989804142))
## [1.27.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.27.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.27.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.27.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/filepicker
@@ -18,17 +18,6 @@ export default class WebDeviceInterface {
}
}
async getAllRawStorageKeyValues() {
const results = []
for (const key of Object.keys(localStorage)) {
results.push({
key: key,
value: localStorage[key],
})
}
return results
}
async setRawStorageValue(key, value) {
localStorage.setItem(key, value)
}
@@ -57,7 +46,7 @@ export default class WebDeviceInterface {
return `${this._getDatabaseKeyPrefix(identifier)}${id}`
}
async getAllRawDatabasePayloads(identifier) {
async getAllDatabaseEntries(identifier) {
const models = []
for (const key in localStorage) {
if (key.startsWith(this._getDatabaseKeyPrefix(identifier))) {
@@ -67,21 +56,21 @@ export default class WebDeviceInterface {
return models
}
async saveRawDatabasePayload(payload, identifier) {
async saveDatabaseEntry(payload, identifier) {
localStorage.setItem(this._keyForPayloadId(payload.uuid, identifier), JSON.stringify(payload))
}
async saveRawDatabasePayloads(payloads, identifier) {
async saveDatabaseEntries(payloads, identifier) {
for (const payload of payloads) {
await this.saveRawDatabasePayload(payload, identifier)
await this.saveDatabaseEntry(payload, identifier)
}
}
async removeRawDatabasePayloadWithId(id, identifier) {
async removeDatabaseEntry(id, identifier) {
localStorage.removeItem(this._keyForPayloadId(id, identifier))
}
async removeAllRawDatabasePayloads(identifier) {
async removeAllDatabaseEntries(identifier) {
for (const key in localStorage) {
if (key.startsWith(this._getDatabaseKeyPrefix(identifier))) {
delete localStorage[key]
@@ -121,12 +110,6 @@ export default class WebDeviceInterface {
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(keychain))
}
/** Allows unit tests to set legacy keychain structure as it was <= 003 */
// eslint-disable-next-line camelcase
async setLegacyRawKeychainValue(value) {
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value))
}
async getRawKeychainValue() {
const keychain = localStorage.getItem(KEYCHAIN_STORAGE_KEY)
return JSON.parse(keychain)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.27.7",
"version": "1.28.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.14.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/files
## [1.14.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/files
## [1.14.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/files
## [1.14.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/files
## [1.14.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.14.6",
"version": "1.14.10",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.8.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
### Features
* Added "Import" option in the account menu that allows you to import from plaintext/markdown files, Evernote exports, Simplenote exports, Google Keep exports and also convert Aegis exports to TokenVault notes ([6e4bf24](https://github.com/standardnotes/app/commit/6e4bf2417abbd88cbac1fd34ae8306041fa2279f))
# [1.7.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-07)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/icons",
"version": "1.7.0",
"version": "1.8.0",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 48 48">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
d="M19.59 40a2.4 2.4 0 0 1-3.28-.88l-.48-.82A2.4 2.4 0 0 1 20 35.91l.48.82a2.42 2.42 0 0 1-.89 3.27Zm10.85 0a2.4 2.4 0 0 1-3.28-.88l-5.64-9.76A2.4 2.4 0 1 1 25.68 27l5.64 9.77a2.39 2.39 0 0 1-.88 3.23Zm10.86 0a2.42 2.42 0 0 1-3.3-.86L25.67 17.75a1.92 1.92 0 0 0-3.33 0L10 39.14a2.42 2.42 0 0 1-2.09 1.2A2.37 2.37 0 0 1 6.7 40a2.4 2.4 0 0 1-.88-3.28l16.1-27.86a2.4 2.4 0 0 1 4.16 0l16.1 27.88A2.4 2.4 0 0 1 41.3 40Z" />
</svg>

After

Width:  |  Height:  |  Size: 612 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<path fill="currentColor"
d="M15.09 11.63s.19-1.28.91-1.28c.76 0 1.78 1.71 1.78 1.71s-2.32-.43-2.69-.43M19 4.69c-.36-.6-2.17-1.28-3.11-1.28H13.5S12.7 2 10.88 2c-1.83 0-1.71.81-1.71 1.5v2.82l-.83.87H4.5s-1.06.72-1.06 2.25c0 1.56.48 6.91 3.69 7.41c3.8.58 4.45-1.18 4.45-1.39c0-.9.02-2.25.02-2.25s1.11 2.12 2.79 2.12s2.65.97 2.65 1.96v1.84S17 20.28 16 20.28h-2.11s-.69-.54-.69-1.28c0-.75.33-.95.73-.95c.39 0 .72.04.72.04v-1.56s-3.18-.03-3.18 2.41c0 2.43 1.66 3.06 2.99 3.06h2.17s3.93-.5 3.93-8.25S19.33 5.28 19 4.69M7.5 6.31H4.26l4.06-4.09V5.5l-.82.81Z" />
</svg>

After

Width:  |  Height:  |  Size: 660 B

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<path fill="currentColor"
d="M4 2h16a2 2 0 0 1 2 2v13.33L17.33 22H4a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2m13 15v3.25L20.25 17H17m-7 2h4v-1h1v-5a5.002 5.002 0 0 0-3-9a5.002 5.002 0 0 0-3 9v5h1v1m4-2h-4v-2h4v2M12 5c2.21 0 4 1.79 4 4c0 1.5-.8 2.77-2 3.46V14h-4v-1.54C8.8 11.77 8 10.5 8 9c0-2.21 1.79-4 4-4Z" />
</svg>

After

Width:  |  Height:  |  Size: 406 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<path fill="currentColor"
d="M3.466 3.62c-.004.052-.014.104-.018.158c-.406 4.626 2.747 8.548 8.03 9.994c2.024.553 5.374 2.018 5.06 5.599a5.063 5.063 0 0 1-1.803 3.46c-1.022.857-2.308 1.21-3.64 1.166C5.147 23.794 0 18.367 0 12.05a11.95 11.95 0 0 1 3.467-8.428zM9.82 1.032C10.727.27 11.876-.046 13.055.005C18.996.27 24 5.67 24 11.936a11.94 11.94 0 0 1-2.667 7.536c.332-4.908-2.94-8.897-8.59-10.441c-2.337-.64-4.749-2.274-4.514-4.948A4.467 4.467 0 0 1 9.82 1.03z" />
</svg>

After

Width:  |  Height:  |  Size: 570 B

+8
View File
@@ -201,6 +201,10 @@ import UserSwitch from './ic-user-switch.svg'
import ViewIcon from './ic-view.svg'
import WarningIcon from './ic-warning.svg'
import WindowIcon from './ic-window.svg'
import EvernoteIcon from './ic-evernote.svg'
import GoogleKeepIcon from './ic-gkeep.svg'
import SimplenoteIcon from './ic-simplenote.svg'
import AegisIcon from './ic-aegis.svg'
export {
AccessibilityIcon,
@@ -406,4 +410,8 @@ export {
ViewIcon,
WarningIcon,
WindowIcon,
EvernoteIcon,
GoogleKeepIcon,
SimplenoteIcon,
AegisIcon,
}
+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.50.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
### Features
* improve initial load performance on mobile ([#2126](https://github.com/standardnotes/app/issues/2126)) ([3c332a3](https://github.com/standardnotes/app/commit/3c332a35f6024c46020a1d6ac68a5df989804142))
## [3.49.60](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.59](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.58](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.57](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.56](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.55](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.54](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.53](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.52](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.51](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/mobile
+16 -2
View File
@@ -75,6 +75,9 @@ PODS:
- glog (0.3.5)
- hermes-engine (0.70.6)
- libevent (2.1.12)
- MMKV (1.2.14):
- MMKVCore (~> 1.2.14)
- MMKVCore (1.2.14)
- OpenSSL-Universal (1.1.1100)
- RCT-Folly (2021.07.22.00):
- boost
@@ -304,6 +307,9 @@ PODS:
- glog
- react-native-fingerprint-scanner (5.0.0):
- React-Core
- react-native-mmkv (2.5.1):
- MMKV (>= 1.2.13)
- React-Core
- react-native-version-info (1.1.1):
- React-Core
- react-native-webview (11.23.1):
@@ -444,6 +450,7 @@ DEPENDENCIES:
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- react-native-fingerprint-scanner (from `../node_modules/react-native-fingerprint-scanner`)
- react-native-mmkv (from `../node_modules/react-native-mmkv`)
- react-native-version-info (from `../node_modules/react-native-version-info`)
- react-native-webview (from `../node_modules/react-native-webview`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -483,6 +490,8 @@ SPEC REPOS:
- FlipperKit
- fmt
- libevent
- MMKV
- MMKVCore
- OpenSSL-Universal
- SocketRocket
- TrustKit
@@ -533,6 +542,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/logger"
react-native-fingerprint-scanner:
:path: "../node_modules/react-native-fingerprint-scanner"
react-native-mmkv:
:path: "../node_modules/react-native-mmkv"
react-native-version-info:
:path: "../node_modules/react-native-version-info"
react-native-webview:
@@ -583,7 +594,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: 48289402952f4f7a4e235de70a9a590aa0b79ef4
FBReactNativeSpec: dd1186fd05255e3457baa2f4ca65e94c2cd1e3ac
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
@@ -596,9 +607,11 @@ SPEC CHECKSUMS:
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: 2af7b7a59128f250adfd86f15aa1d5a2ecd39995
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
MMKV: 9c4663aa7ca255d478ff10f2f5cb7d17c1651ccd
MMKVCore: 89f5c8a66bba2dcd551779dea4d412eeec8ff5bb
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
RCTRequired: e1866f61af7049eb3d8e08e8b133abd38bc1ca7a
@@ -616,6 +629,7 @@ SPEC CHECKSUMS:
React-jsinspector: 60769e5a0a6d4b32294a2456077f59d0266f9a8b
React-logger: 1623c216abaa88974afce404dc8f479406bbc3a0
react-native-fingerprint-scanner: be63e626b31fb951780a5fac5328b065a61a3d6e
react-native-mmkv: 69b9c003f10afdd01addf7c6ee784ce42ee2eff3
react-native-version-info: a106f23009ac0db4ee00de39574eb546682579b9
react-native-webview: d33e2db8925d090871ffeb232dfa50cb3a727581
React-perflogger: 8c79399b0500a30ee8152d0f9f11beae7fc36595
+3 -1
View File
@@ -100,7 +100,7 @@
<key>NSPhotoLibraryUsageDescription</key>
<string>Photo library is optionally used to select files to upload or QR code images from your photo library.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone is optionally used to capture videos.</string>
<string>Microphone is optionally used to capture videos.</string>
<key>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>
@@ -147,5 +147,7 @@
<false/>
<key>supportsAlternateIcons</key>
<true/>
<key>RCTAsyncStorageExcludeFromBackup</key>
<false/>
</dict>
</plist>
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.49.51",
"version": "3.50.0",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
@@ -59,6 +59,7 @@
"react-native-fs": "^2.20.0",
"react-native-iap": "^12.4.4",
"react-native-keychain": "standardnotes/react-native-keychain#d277d360494cbd02be4accb4a360772a8e0e97b6",
"react-native-mmkv": "^2.5.1",
"react-native-privacy-snapshot": "standardnotes/react-native-privacy-snapshot#653e904c90fc6f2b578da59138f2bfe5d7f942fe",
"react-native-share": "^8.0.0",
"react-native-version-info": "^1.1.1",
@@ -0,0 +1,158 @@
import AsyncStorage from '@react-native-community/async-storage'
import {
DatabaseKeysLoadChunk,
DatabaseKeysLoadChunkResponse,
DatabaseLoadOptions,
GetSortedPayloadsByPriority,
TransferPayload,
} from '@standardnotes/snjs'
import { Platform } from 'react-native'
import { DatabaseInterface } from './DatabaseInterface'
import { DatabaseMetadata } from './DatabaseMetadata'
import { FlashKeyValueStore } from './FlashKeyValueStore'
import { isLegacyIdentifier } from './LegacyIdentifier'
import { showLoadFailForItemIds } from './showLoadFailForItemIds'
export class Database implements DatabaseInterface {
private metadataStore: DatabaseMetadata
constructor(private identifier: string) {
const flashStorage = new FlashKeyValueStore(identifier)
this.metadataStore = new DatabaseMetadata(identifier, flashStorage)
}
private databaseKeyForPayloadId(id: string) {
return `${this.getDatabaseKeyPrefix()}${id}`
}
private getDatabaseKeyPrefix() {
if (this.identifier && !isLegacyIdentifier(this.identifier)) {
return `${this.identifier}-Item-`
} else {
return 'Item-'
}
}
async getAllEntries<T extends TransferPayload = TransferPayload>(): Promise<T[]> {
const keys = await this.getAllKeys()
return this.multiGet(keys)
}
async getAllKeys(): Promise<string[]> {
const keys = await AsyncStorage.getAllKeys()
const filtered = keys.filter((key) => {
return key.startsWith(this.getDatabaseKeyPrefix())
})
return filtered
}
async multiDelete(keys: string[]): Promise<void> {
return AsyncStorage.multiRemove(keys)
}
async deleteItem(itemUuid: string): Promise<void> {
const key = this.databaseKeyForPayloadId(itemUuid)
this.metadataStore.deleteMetadataItem(itemUuid)
return this.multiDelete([key])
}
async deleteAll(): Promise<void> {
const keys = await this.getAllKeys()
return this.multiDelete(keys)
}
async setItems(items: TransferPayload[]): Promise<void> {
if (items.length === 0) {
return
}
await Promise.all(
items.map((item) => {
return Promise.all([
AsyncStorage.setItem(this.databaseKeyForPayloadId(item.uuid), JSON.stringify(item)),
this.metadataStore.setMetadataForPayloads([item]),
])
}),
)
}
async getLoadChunks(options: DatabaseLoadOptions): Promise<DatabaseKeysLoadChunkResponse> {
let metadataItems = this.metadataStore.getAllMetadataItems()
if (metadataItems.length === 0) {
const allEntries = await this.getAllEntries()
metadataItems = this.metadataStore.runMigration(allEntries)
}
const sorted = GetSortedPayloadsByPriority(metadataItems, options)
const itemsKeysChunk: DatabaseKeysLoadChunk = {
keys: sorted.itemsKeyPayloads.map((item) => this.databaseKeyForPayloadId(item.uuid)),
}
const contentTypePriorityChunk: DatabaseKeysLoadChunk = {
keys: sorted.contentTypePriorityPayloads.map((item) => this.databaseKeyForPayloadId(item.uuid)),
}
const remainingKeys = sorted.remainingPayloads.map((item) => this.databaseKeyForPayloadId(item.uuid))
const remainingKeysChunks: DatabaseKeysLoadChunk[] = []
for (let i = 0; i < remainingKeys.length; i += options.batchSize) {
remainingKeysChunks.push({
keys: remainingKeys.slice(i, i + options.batchSize),
})
}
const result: DatabaseKeysLoadChunkResponse = {
keys: {
itemsKeys: itemsKeysChunk,
remainingChunks: [contentTypePriorityChunk, ...remainingKeysChunks],
},
remainingChunksItemCount: sorted.contentTypePriorityPayloads.length + sorted.remainingPayloads.length,
}
return result
}
async multiGet<T>(keys: string[]): Promise<T[]> {
const results: T[] = []
if (Platform.OS === 'android') {
const failedItemIds: string[] = []
for (const key of keys) {
try {
const item = await AsyncStorage.getItem(key)
if (item) {
try {
results.push(JSON.parse(item) as T)
} catch (e) {
results.push(item as T)
}
}
} catch (e) {
console.error('Error getting item', key, e)
failedItemIds.push(key)
}
}
if (failedItemIds.length > 0) {
showLoadFailForItemIds(failedItemIds)
}
} else {
try {
for (const item of await AsyncStorage.multiGet(keys)) {
if (item[1]) {
try {
results.push(JSON.parse(item[1]))
} catch (e) {
results.push(item[1] as T)
}
}
}
} catch (e) {
console.error('Error getting items', e)
}
}
return results
}
}
@@ -0,0 +1,10 @@
import { TransferPayload } from '@standardnotes/snjs'
export interface DatabaseInterface {
getAllKeys(): Promise<string[]>
multiDelete(keys: string[]): Promise<void>
deleteItem(itemUuid: string): Promise<void>
deleteAll(): Promise<void>
setItems(items: TransferPayload[]): Promise<void>
multiGet<T>(keys: string[]): Promise<T[]>
}
@@ -0,0 +1,39 @@
import { DatabaseItemMetadata, isNotUndefined, TransferPayload } from '@standardnotes/snjs'
import { FlashKeyValueStore } from './FlashKeyValueStore'
export class DatabaseMetadata {
constructor(private identifier: string, private flashStorage: FlashKeyValueStore) {}
runMigration(payloads: TransferPayload[]) {
const metadataItems = this.setMetadataForPayloads(payloads)
return metadataItems
}
setMetadataForPayloads(payloads: TransferPayload[]) {
const metadataItems = []
for (const payload of payloads) {
const { uuid, content_type, updated_at } = payload
const key = this.keyForUuid(uuid)
const metadata: DatabaseItemMetadata = { uuid, content_type, updated_at }
this.flashStorage.set(key, metadata)
metadataItems.push(metadata)
}
return metadataItems
}
deleteMetadataItem(itemUuid: string) {
const key = this.keyForUuid(itemUuid)
this.flashStorage.delete(key)
}
getAllMetadataItems(): DatabaseItemMetadata[] {
const keys = this.flashStorage.getAllKeys()
const metadataKeys = keys.filter((key) => key.endsWith('-Metadata'))
const metadataItems = this.flashStorage.multiGet<DatabaseItemMetadata>(metadataKeys).filter(isNotUndefined)
return metadataItems
}
private keyForUuid(uuid: string) {
return `${this.identifier}-Item-${uuid}-Metadata`
}
}
@@ -0,0 +1,40 @@
import { MMKV } from 'react-native-mmkv'
export class FlashKeyValueStore {
private storage: MMKV
constructor(identifier: string) {
this.storage = new MMKV({ id: identifier })
}
set(key: string, value: unknown): void {
this.storage.set(key, JSON.stringify(value))
}
delete(key: string): void {
this.storage.delete(key)
}
deleteAll(): void {
this.storage.clearAll()
}
getAllKeys(): string[] {
return this.storage.getAllKeys()
}
get<T>(key: string): T | undefined {
const item = this.storage.getString(key)
if (item) {
try {
return JSON.parse(item)
} catch (e) {
return item as T
}
}
}
multiGet<T>(keys: string[]): (T | undefined)[] {
return keys.map((key) => this.get<T>(key))
}
}
@@ -0,0 +1,15 @@
import { ApplicationIdentifier } from '@standardnotes/snjs'
/**
* This identifier was the database name used in Standard Notes web/desktop.
*/
const LEGACY_IDENTIFIER = 'standardnotes'
/**
* We use this function to decide if we need to prefix the identifier in getDatabaseKeyPrefix or not.
* It is also used to decide if the raw or the namespaced keychain is used.
* @param identifier The ApplicationIdentifier
*/
export const isLegacyIdentifier = function (identifier: ApplicationIdentifier) {
return identifier && identifier === LEGACY_IDENTIFIER
}
@@ -0,0 +1,26 @@
import AsyncStorage from '@react-native-community/async-storage'
export class LegacyKeyValueStore {
set(key: string, value: string): Promise<void> {
return AsyncStorage.setItem(key, JSON.stringify(value))
}
delete(key: string): Promise<void> {
return AsyncStorage.removeItem(key)
}
deleteAll(): Promise<void> {
return AsyncStorage.clear()
}
async getValue<T>(key: string): Promise<T | undefined> {
const item = await AsyncStorage.getItem(key)
if (item) {
try {
return JSON.parse(item)
} catch (e) {
return item as T
}
}
}
}
@@ -0,0 +1,16 @@
import { Alert } from 'react-native'
export const showLoadFailForItemIds = (failedItemIds: string[]) => {
let text =
'The following items could not be loaded. This may happen if you are in low-memory conditions, or if the note is very large in size. We recommend breaking up large notes into smaller chunks using the desktop or web app.\n\nItems:\n'
let index = 0
text += failedItemIds.map((id) => {
let result = id
if (index !== failedItemIds.length - 1) {
result += '\n'
}
index++
return result
})
Alert.alert('Unable to load item(s)', text)
}
@@ -1,12 +1,11 @@
import AsyncStorage from '@react-native-community/async-storage'
import SNReactNative from '@standardnotes/react-native-utils'
import { AppleIAPReceipt } from '@standardnotes/services'
import {
AppleIAPProductId,
AppleIAPReceipt,
ApplicationIdentifier,
DatabaseKeysLoadChunkResponse,
DatabaseLoadOptions,
Environment,
LegacyMobileKeychainStructure,
LegacyRawKeychainValue,
MobileDeviceInterface,
NamespacedRootKeyInKeychain,
Platform as SNPlatform,
@@ -41,8 +40,11 @@ import {
import { hide, show } from 'react-native-privacy-snapshot'
import Share from 'react-native-share'
import { AndroidBackHandlerService } from '../AndroidBackHandlerService'
import { AppStateObserverService } from '../AppStateObserverService'
import { PurchaseManager } from '../PurchaseManager'
import { AppStateObserverService } from './../AppStateObserverService'
import { Database } from './Database/Database'
import { isLegacyIdentifier } from './Database/LegacyIdentifier'
import { LegacyKeyValueStore } from './Database/LegacyKeyValueStore'
import Keychain from './Keychain'
export type BiometricsType = 'Fingerprint' | 'Face ID' | 'Biometrics' | 'Touch ID'
@@ -53,41 +55,6 @@ export enum MobileDeviceEvent {
type MobileDeviceEventHandler = (event: MobileDeviceEvent) => void
/**
* This identifier was the database name used in Standard Notes web/desktop.
*/
const LEGACY_IDENTIFIER = 'standardnotes'
/**
* We use this function to decide if we need to prefix the identifier in getDatabaseKeyPrefix or not.
* It is also used to decide if the raw or the namespaced keychain is used.
* @param identifier The ApplicationIdentifier
*/
const isLegacyIdentifier = function (identifier: ApplicationIdentifier) {
return identifier && identifier === LEGACY_IDENTIFIER
}
function isLegacyMobileKeychain(
x: LegacyMobileKeychainStructure | RawKeychainValue,
): x is LegacyMobileKeychainStructure {
return x.ak != undefined
}
const showLoadFailForItemIds = (failedItemIds: string[]) => {
let text =
'The following items could not be loaded. This may happen if you are in low-memory conditions, or if the note is very large in size. We recommend breaking up large notes into smaller chunks using the desktop or web app.\n\nItems:\n'
let index = 0
text += failedItemIds.map((id) => {
let result = id
if (index !== failedItemIds.length - 1) {
result += '\n'
}
index++
return result
})
Alert.alert('Unable to load item(s)', text)
}
export class MobileDevice implements MobileDeviceInterface {
environment: Environment.Mobile = Environment.Mobile
platform: SNPlatform.Ios | SNPlatform.Android = Platform.OS === 'ios' ? SNPlatform.Ios : SNPlatform.Android
@@ -95,6 +62,8 @@ export class MobileDevice implements MobileDeviceInterface {
public isDarkMode = false
public statusBarBgColor: string | undefined
private componentUrls: Map<UuidString, string> = new Map()
private keyValueStore = new LegacyKeyValueStore()
private databases = new Map<string, Database>()
constructor(
private stateObserverService?: AppStateObserverService,
@@ -106,6 +75,17 @@ export class MobileDevice implements MobileDeviceInterface {
return PurchaseManager.getInstance().purchase(plan)
}
private findOrCreateDatabase(identifier: ApplicationIdentifier): Database {
const existing = this.databases.get(identifier)
if (existing) {
return existing
}
const newDb = new Database(identifier)
this.databases.set(identifier, newDb)
return newDb
}
deinit() {
this.stateObserverService?.deinit()
;(this.stateObserverService as unknown) = undefined
@@ -120,10 +100,6 @@ export class MobileDevice implements MobileDeviceInterface {
console.log(args)
}
async setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void> {
await Keychain.setKeys(value)
}
public async getJsonParsedRawStorageValue(key: string): Promise<unknown | undefined> {
const value = await this.getRawStorageValue(key)
if (value == undefined) {
@@ -136,219 +112,57 @@ export class MobileDevice implements MobileDeviceInterface {
}
}
private getDatabaseKeyPrefix(identifier: ApplicationIdentifier) {
if (identifier && !isLegacyIdentifier(identifier)) {
return `${identifier}-Item-`
} else {
return 'Item-'
}
}
private keyForPayloadId(id: string, identifier: ApplicationIdentifier) {
return `${this.getDatabaseKeyPrefix(identifier)}${id}`
}
private async getAllDatabaseKeys(identifier: ApplicationIdentifier) {
const keys = await AsyncStorage.getAllKeys()
const filtered = keys.filter((key) => {
return key.startsWith(this.getDatabaseKeyPrefix(identifier))
})
return filtered
}
getDatabaseKeys(): Promise<string[]> {
return AsyncStorage.getAllKeys()
}
private async getRawStorageKeyValues(keys: string[]) {
const results: { key: string; value: unknown }[] = []
if (Platform.OS === 'android') {
for (const key of keys) {
try {
const item = await AsyncStorage.getItem(key)
if (item) {
results.push({ key, value: item })
}
} catch (e) {
console.error('Error getting item', key, e)
}
}
} else {
try {
for (const item of await AsyncStorage.multiGet(keys)) {
if (item[1]) {
results.push({ key: item[0], value: item[1] })
}
}
} catch (e) {
console.error('Error getting items', e)
}
}
return results
}
private async getDatabaseKeyValues(keys: string[]) {
const results: (TransferPayload | unknown)[] = []
if (Platform.OS === 'android') {
const failedItemIds: string[] = []
for (const key of keys) {
try {
const item = await AsyncStorage.getItem(key)
if (item) {
try {
results.push(JSON.parse(item) as TransferPayload)
} catch (e) {
results.push(item)
}
}
} catch (e) {
console.error('Error getting item', key, e)
failedItemIds.push(key)
}
}
if (failedItemIds.length > 0) {
showLoadFailForItemIds(failedItemIds)
}
} else {
try {
for (const item of await AsyncStorage.multiGet(keys)) {
if (item[1]) {
try {
results.push(JSON.parse(item[1]))
} catch (e) {
results.push(item[1])
}
}
}
} catch (e) {
console.error('Error getting items', e)
}
}
return results
}
async getRawStorageValue(key: string) {
const item = await AsyncStorage.getItem(key)
if (item) {
try {
return JSON.parse(item)
} catch (e) {
return item
}
}
}
hideMobileInterfaceFromScreenshots(): void {
hide()
this.setAndroidScreenshotPrivacy(true)
}
stopHidingMobileInterfaceFromScreenshots(): void {
show()
this.setAndroidScreenshotPrivacy(false)
}
async getAllRawStorageKeyValues() {
const keys = await AsyncStorage.getAllKeys()
return this.getRawStorageKeyValues(keys)
getRawStorageValue(key: string): Promise<string | undefined> {
return this.keyValueStore.getValue(key)
}
setRawStorageValue(key: string, value: string): Promise<void> {
return AsyncStorage.setItem(key, JSON.stringify(value))
return this.keyValueStore.set(key, value)
}
removeRawStorageValue(key: string): Promise<void> {
return AsyncStorage.removeItem(key)
return this.keyValueStore.delete(key)
}
removeAllRawStorageValues(): Promise<void> {
return AsyncStorage.clear()
return this.keyValueStore.deleteAll()
}
openDatabase(): Promise<{ isNewDatabase?: boolean | undefined } | undefined> {
return Promise.resolve({ isNewDatabase: false })
}
async getAllRawDatabasePayloads<T extends TransferPayload = TransferPayload>(
getDatabaseLoadChunks(options: DatabaseLoadOptions, identifier: string): Promise<DatabaseKeysLoadChunkResponse> {
return this.findOrCreateDatabase(identifier).getLoadChunks(options)
}
async getAllDatabaseEntries<T extends TransferPayload = TransferPayload>(
identifier: ApplicationIdentifier,
): Promise<T[]> {
const keys = await this.getAllDatabaseKeys(identifier)
return this.getDatabaseKeyValues(keys) as Promise<T[]>
return this.findOrCreateDatabase(identifier).getAllEntries()
}
saveRawDatabasePayload(payload: TransferPayload, identifier: ApplicationIdentifier): Promise<void> {
return this.saveRawDatabasePayloads([payload], identifier)
}
async saveRawDatabasePayloads(payloads: TransferPayload[], identifier: ApplicationIdentifier): Promise<void> {
if (payloads.length === 0) {
return
}
await Promise.all(
payloads.map((item) => {
return AsyncStorage.setItem(this.keyForPayloadId(item.uuid, identifier), JSON.stringify(item))
}),
)
}
removeRawDatabasePayloadWithId(id: string, identifier: ApplicationIdentifier): Promise<void> {
return this.removeRawStorageValue(this.keyForPayloadId(id, identifier))
}
async removeAllRawDatabasePayloads(identifier: ApplicationIdentifier): Promise<void> {
const keys = await this.getAllDatabaseKeys(identifier)
return AsyncStorage.multiRemove(keys)
}
async getNamespacedKeychainValue(
async getDatabaseEntries<T extends TransferPayload = TransferPayload>(
identifier: ApplicationIdentifier,
): Promise<NamespacedRootKeyInKeychain | undefined> {
const keychain = await this.getRawKeychainValue()
if (!keychain) {
return
}
const namespacedValue = keychain[identifier]
if (!namespacedValue && isLegacyIdentifier(identifier)) {
return keychain as unknown as NamespacedRootKeyInKeychain
}
return namespacedValue
keys: string[],
): Promise<T[]> {
return this.findOrCreateDatabase(identifier).multiGet<T>(keys)
}
async setNamespacedKeychainValue(
value: NamespacedRootKeyInKeychain,
identifier: ApplicationIdentifier,
): Promise<void> {
let keychain = await this.getRawKeychainValue()
if (!keychain) {
keychain = {}
}
await Keychain.setKeys({
...keychain,
[identifier]: value,
})
saveDatabaseEntry(payload: TransferPayload, identifier: ApplicationIdentifier): Promise<void> {
return this.saveDatabaseEntries([payload], identifier)
}
async clearNamespacedKeychainValue(identifier: ApplicationIdentifier): Promise<void> {
const keychain = await this.getRawKeychainValue()
async saveDatabaseEntries(payloads: TransferPayload[], identifier: ApplicationIdentifier): Promise<void> {
return this.findOrCreateDatabase(identifier).setItems(payloads)
}
if (!keychain) {
return
}
removeDatabaseEntry(id: string, identifier: ApplicationIdentifier): Promise<void> {
return this.findOrCreateDatabase(identifier).deleteItem(id)
}
if (!keychain[identifier] && isLegacyIdentifier(identifier) && isLegacyMobileKeychain(keychain)) {
await this.clearRawKeychainValue()
return
}
delete keychain[identifier]
await Keychain.setKeys(keychain)
async removeAllDatabaseEntries(identifier: ApplicationIdentifier): Promise<void> {
return this.findOrCreateDatabase(identifier).deleteAll()
}
async getDeviceBiometricsAvailability() {
@@ -413,6 +227,51 @@ export class MobileDevice implements MobileDeviceInterface {
return result
}
async getNamespacedKeychainValue(
identifier: ApplicationIdentifier,
): Promise<NamespacedRootKeyInKeychain | undefined> {
const keychain = await this.getRawKeychainValue()
if (!keychain) {
return
}
const namespacedValue = keychain[identifier]
if (!namespacedValue && isLegacyIdentifier(identifier)) {
return keychain as unknown as NamespacedRootKeyInKeychain
}
return namespacedValue
}
async setNamespacedKeychainValue(
value: NamespacedRootKeyInKeychain,
identifier: ApplicationIdentifier,
): Promise<void> {
let keychain = await this.getRawKeychainValue()
if (!keychain) {
keychain = {}
}
await Keychain.setKeys({
...keychain,
[identifier]: value,
})
}
async clearNamespacedKeychainValue(identifier: ApplicationIdentifier): Promise<void> {
const keychain = await this.getRawKeychainValue()
if (!keychain) {
return
}
delete keychain[identifier]
await Keychain.setKeys(keychain)
}
async getRawKeychainValue(): Promise<RawKeychainValue | undefined> {
const result = await Keychain.getKeys()
@@ -641,4 +500,14 @@ export class MobileDevice implements MobileDeviceInterface {
async getColorScheme(): Promise<ColorSchemeName> {
return Appearance.getColorScheme()
}
hideMobileInterfaceFromScreenshots(): void {
hide()
this.setAndroidScreenshotPrivacy(true)
}
stopHidingMobileInterfaceFromScreenshots(): void {
show()
this.setAndroidScreenshotPrivacy(false)
}
}
@@ -7,7 +7,7 @@ import { OnShouldStartLoadWithRequest } from 'react-native-webview/lib/WebViewTy
import { AndroidBackHandlerService } from './AndroidBackHandlerService'
import { AppStateObserverService } from './AppStateObserverService'
import { ColorSchemeObserverService } from './ColorSchemeObserverService'
import { MobileDevice, MobileDeviceEvent } from './Lib/Interface'
import { MobileDevice, MobileDeviceEvent } from './Lib/MobileDevice'
import { IsDev } from './Lib/Utils'
const LoggingEnabled = IsDev
@@ -177,6 +177,10 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
window.ReactNativeWebView.postMessage('[web log] ' + args.join(' '));
}
console.error = (...args) => {
window.ReactNativeWebView.postMessage('[web log] ' + args.join(' '));
}
${WebProcessDeviceInterface}
${WebProcessMessageSender}
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.41.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
### Features
* improve initial load performance on mobile ([#2126](https://github.com/standardnotes/app/issues/2126)) ([3c332a3](https://github.com/standardnotes/app/commit/3c332a35f6024c46020a1d6ac68a5df989804142))
## [1.40.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/models
## [1.40.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/models
## [1.40.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/models
## [1.40.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/models
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.40.1",
"version": "1.41.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -10,22 +10,3 @@ export interface NamespacedRootKeyInKeychain {
}
export type RootKeyContentInStorage = RootKeyContentSpecialized
export interface LegacyRawKeychainValue {
mk: string
ak: string
version: ProtocolVersion
}
export type LegacyMobileKeychainStructure = {
offline?: {
timing?: unknown
pw?: string
}
encryptedAccountKeys?: unknown
mk: string
pw: string
ak: string
version?: string
jwt?: string
}
@@ -1,5 +1,5 @@
import { dateFromDSLDateString } from './Utils'
import { addDaysToDate, addHoursToDate } from '@standardnotes/utils'
import { addDaysToDate, addHoursToDate, addMonthsToDate, addYearsToDate } from '@standardnotes/utils'
describe('Predicate Utils', () => {
describe('dateFromDSLDateString', () => {
@@ -15,12 +15,12 @@ describe('Predicate Utils', () => {
it('should return a date object with the correct month', () => {
const date = dateFromDSLDateString('1.months.ago')
expect(date.getMonth()).toEqual(new Date().getMonth() - 1)
expect(date.getMonth()).toEqual(addMonthsToDate(new Date(), -1).getMonth())
})
it('should return a date object with the correct year', () => {
const date = dateFromDSLDateString('1.years.ago')
expect(date.getFullYear()).toEqual(new Date().getFullYear() - 1)
expect(date.getFullYear()).toEqual(addYearsToDate(new Date(), -1).getFullYear())
})
})
})
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.87](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.86](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.85](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.84](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.83](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.82](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.81](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.80](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.79](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.78](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.77",
"version": "1.4.87",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/responses
## [1.12.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/responses
## [1.12.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/responses
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.12.10",
"version": "1.12.12",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.52.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
### Features
* improve initial load performance on mobile ([#2126](https://github.com/standardnotes/app/issues/2126)) ([3c332a3](https://github.com/standardnotes/app/commit/3c332a35f6024c46020a1d6ac68a5df989804142))
## [1.51.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/services
# [1.51.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
### Features
* **api:** add authenticators api ([#2124](https://github.com/standardnotes/app/issues/2124)) ([59e8b5c](https://github.com/standardnotes/app/commit/59e8b5c8b5b61ef1cd8f291798c45860736a1296))
## [1.50.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/services
## [1.50.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/services
## [1.50.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.50.12",
"version": "1.52.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,7 +1,6 @@
import { ApplicationIdentifier, ContentType } from '@standardnotes/common'
import { BackupFile, DecryptedItemInterface, ItemStream, Platform, PrefKey, PrefValue } from '@standardnotes/models'
import { FilesClientInterface } from '@standardnotes/files'
import { AlertService } from '../Alert/AlertService'
import { ComponentManagerInterface } from '../Component/ComponentManagerInterface'
import { ApplicationEvent } from '../Event/ApplicationEvent'
@@ -0,0 +1,12 @@
export interface AuthenticatorClientInterface {
list(): Promise<Array<{ id: string; name: string }>>
delete(authenticatorId: string): Promise<boolean>
generateRegistrationOptions(userUuid: string, username: string): Promise<Record<string, unknown> | null>
verifyRegistrationResponse(
userUuid: string,
name: string,
registrationCredential: Record<string, unknown>,
): Promise<boolean>
generateAuthenticationOptions(): Promise<Record<string, unknown> | null>
verifyAuthenticationResponse(userUuid: string, authenticationCredential: Record<string, unknown>): Promise<boolean>
}
@@ -0,0 +1,111 @@
/* istanbul ignore file */
import { AuthenticatorApiServiceInterface } from '@standardnotes/api'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { AbstractService } from '../Service/AbstractService'
import { AuthenticatorClientInterface } from './AuthenticatorClientInterface'
export class AuthenticatorManager extends AbstractService implements AuthenticatorClientInterface {
constructor(
private authenticatorApiService: AuthenticatorApiServiceInterface,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
}
async list(): Promise<{ id: string; name: string }[]> {
try {
const result = await this.authenticatorApiService.list()
if (result.data.error) {
return []
}
return result.data.authenticators
} catch (error) {
return []
}
}
async delete(authenticatorId: string): Promise<boolean> {
try {
const result = await this.authenticatorApiService.delete(authenticatorId)
if (result.data.error) {
return false
}
return true
} catch (error) {
return false
}
}
async generateRegistrationOptions(userUuid: string, username: string): Promise<Record<string, unknown> | null> {
try {
const result = await this.authenticatorApiService.generateRegistrationOptions(userUuid, username)
if (result.data.error) {
return null
}
return result.data.options
} catch (error) {
return null
}
}
async verifyRegistrationResponse(
userUuid: string,
name: string,
registrationCredential: Record<string, unknown>,
): Promise<boolean> {
try {
const result = await this.authenticatorApiService.verifyRegistrationResponse(
userUuid,
name,
registrationCredential,
)
if (result.data.error) {
return false
}
return result.data.success
} catch (error) {
return false
}
}
async generateAuthenticationOptions(): Promise<Record<string, unknown> | null> {
try {
const result = await this.authenticatorApiService.generateAuthenticationOptions()
if (result.data.error) {
return null
}
return result.data.options
} catch (error) {
return null
}
}
async verifyAuthenticationResponse(
userUuid: string,
authenticationCredential: Record<string, unknown>,
): Promise<boolean> {
try {
const result = await this.authenticatorApiService.verifyAuthenticationResponse(userUuid, authenticationCredential)
if (result.data.error) {
return false
}
return result.data.success
} catch (error) {
return false
}
}
}
@@ -0,0 +1,3 @@
import { TransferPayload } from '@standardnotes/models'
export type DatabaseItemMetadata = Pick<TransferPayload, 'uuid' | 'updated_at' | 'content_type'>
@@ -0,0 +1,44 @@
import { ContentType } from '@standardnotes/common'
import { FullyFormedTransferPayload } from '@standardnotes/models'
export type DatabaseKeysLoadChunk = {
keys: string[]
}
export type DatabaseFullEntryLoadChunk = {
entries: FullyFormedTransferPayload[]
}
export function isChunkFullEntry(
x: DatabaseKeysLoadChunk | DatabaseFullEntryLoadChunk,
): x is DatabaseFullEntryLoadChunk {
return (x as DatabaseFullEntryLoadChunk).entries !== undefined
}
export type DatabaseKeysLoadChunkResponse = {
keys: {
itemsKeys: DatabaseKeysLoadChunk
remainingChunks: DatabaseKeysLoadChunk[]
}
remainingChunksItemCount: number
}
export type DatabaseFullEntryLoadChunkResponse = {
fullEntries: {
itemsKeys: DatabaseFullEntryLoadChunk
remainingChunks: DatabaseFullEntryLoadChunk[]
}
remainingChunksItemCount: number
}
export function isFullEntryLoadChunkResponse(
x: DatabaseKeysLoadChunkResponse | DatabaseFullEntryLoadChunkResponse,
): x is DatabaseFullEntryLoadChunkResponse {
return (x as DatabaseFullEntryLoadChunkResponse).fullEntries !== undefined
}
export type DatabaseLoadOptions = {
contentTypePriority: ContentType[]
uuidPriority: string[]
batchSize: number
}
@@ -1,6 +1,6 @@
import { ContentType } from '@standardnotes/common'
import { FullyFormedPayloadInterface } from '@standardnotes/models'
import { GetSortedPayloadsByPriority } from './Utils'
import { GetSortedPayloadsByPriority } from './DatabaseLoadSorter'
describe('GetSortedPayloadsByPriority', () => {
let payloads: FullyFormedPayloadInterface[] = []
@@ -26,11 +26,11 @@ describe('GetSortedPayloadsByPriority', () => {
} as FullyFormedPayloadInterface,
]
const { itemsKeyPayloads, contentTypePriorityPayloads, remainingPayloads } = GetSortedPayloadsByPriority(
payloads,
const { itemsKeyPayloads, contentTypePriorityPayloads, remainingPayloads } = GetSortedPayloadsByPriority(payloads, {
contentTypePriority,
launchPriorityUuids,
)
uuidPriority: launchPriorityUuids,
batchSize: 1000,
})
expect(itemsKeyPayloads.length).toBe(1)
expect(itemsKeyPayloads[0].content_type).toBe(ContentType.ItemsKey)
@@ -84,11 +84,11 @@ describe('GetSortedPayloadsByPriority', () => {
launchPriorityUuids = [prioritizedNoteUuid, prioritizedTagUuid]
const { itemsKeyPayloads, contentTypePriorityPayloads, remainingPayloads } = GetSortedPayloadsByPriority(
payloads,
const { itemsKeyPayloads, contentTypePriorityPayloads, remainingPayloads } = GetSortedPayloadsByPriority(payloads, {
contentTypePriority,
launchPriorityUuids,
)
uuidPriority: launchPriorityUuids,
batchSize: 1000,
})
expect(itemsKeyPayloads.length).toBe(1)
expect(itemsKeyPayloads[0].content_type).toBe(ContentType.ItemsKey)
@@ -116,12 +116,12 @@ describe('GetSortedPayloadsByPriority', () => {
{
content_type: ContentType.Note,
uuid: unprioritizedNoteUuid,
serverUpdatedAt: new Date(1),
updated_at: new Date(1),
} as FullyFormedPayloadInterface,
{
content_type: ContentType.Tag,
uuid: unprioritizedTagUuid,
serverUpdatedAt: new Date(2),
updated_at: new Date(2),
} as FullyFormedPayloadInterface,
{
content_type: ContentType.Note,
@@ -135,7 +135,11 @@ describe('GetSortedPayloadsByPriority', () => {
launchPriorityUuids = [prioritizedNoteUuid, prioritizedTagUuid]
const { remainingPayloads } = GetSortedPayloadsByPriority(payloads, contentTypePriority, launchPriorityUuids)
const { remainingPayloads } = GetSortedPayloadsByPriority(payloads, {
contentTypePriority,
uuidPriority: launchPriorityUuids,
batchSize: 1000,
})
expect(remainingPayloads.length).toBe(4)
expect(remainingPayloads[0].uuid).toBe(prioritizedNoteUuid)
@@ -1,18 +1,18 @@
import { UuidString } from '@Lib/Types'
import { ContentType } from '@standardnotes/common'
import { FullyFormedPayloadInterface } from '@standardnotes/models'
import { DatabaseItemMetadata } from './DatabaseItemMetadata'
import { DatabaseLoadOptions } from './DatabaseLoadOptions'
import { ContentType, Uuid } from '@standardnotes/common'
/**
* Sorts payloads according by most recently modified first, according to the priority,
* whereby the earlier a content_type appears in the priorityList,
* the earlier it will appear in the resulting sorted array.
*/
function SortPayloadsByRecentAndContentPriority(
payloads: FullyFormedPayloadInterface[],
function SortPayloadsByRecentAndContentPriority<T extends DatabaseItemMetadata = DatabaseItemMetadata>(
payloads: T[],
contentTypePriorityList: ContentType[],
): FullyFormedPayloadInterface[] {
): T[] {
return payloads.sort((a, b) => {
const dateResult = new Date(b.serverUpdatedAt).getTime() - new Date(a.serverUpdatedAt).getTime()
const dateResult = new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
let aPriority = 0
let bPriority = 0
@@ -45,12 +45,12 @@ function SortPayloadsByRecentAndContentPriority(
* whereby the earlier a uuid appears in the priorityList,
* the earlier it will appear in the resulting sorted array.
*/
function SortPayloadsByRecentAndUuidPriority(
payloads: FullyFormedPayloadInterface[],
uuidPriorityList: UuidString[],
): FullyFormedPayloadInterface[] {
function SortPayloadsByRecentAndUuidPriority<T extends DatabaseItemMetadata = DatabaseItemMetadata>(
payloads: T[],
uuidPriorityList: Uuid[],
): T[] {
return payloads.sort((a, b) => {
const dateResult = new Date(b.serverUpdatedAt).getTime() - new Date(a.serverUpdatedAt).getTime()
const dateResult = new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
let aPriority = 0
let bPriority = 0
@@ -78,25 +78,24 @@ function SortPayloadsByRecentAndUuidPriority(
})
}
export function GetSortedPayloadsByPriority(
payloads: FullyFormedPayloadInterface[],
contentTypePriorityList: ContentType[],
uuidPriorityList: UuidString[],
export function GetSortedPayloadsByPriority<T extends DatabaseItemMetadata = DatabaseItemMetadata>(
payloads: T[],
options: DatabaseLoadOptions,
): {
itemsKeyPayloads: FullyFormedPayloadInterface[]
contentTypePriorityPayloads: FullyFormedPayloadInterface[]
remainingPayloads: FullyFormedPayloadInterface[]
itemsKeyPayloads: T[]
contentTypePriorityPayloads: T[]
remainingPayloads: T[]
} {
const itemsKeyPayloads: FullyFormedPayloadInterface[] = []
const contentTypePriorityPayloads: FullyFormedPayloadInterface[] = []
const remainingPayloads: FullyFormedPayloadInterface[] = []
const itemsKeyPayloads: T[] = []
const contentTypePriorityPayloads: T[] = []
const remainingPayloads: T[] = []
for (let index = 0; index < payloads.length; index++) {
const payload = payloads[index]
if (payload.content_type === ContentType.ItemsKey) {
itemsKeyPayloads.push(payload)
} else if (contentTypePriorityList.includes(payload.content_type)) {
} else if (options.contentTypePriority.includes(payload.content_type)) {
contentTypePriorityPayloads.push(payload)
} else {
remainingPayloads.push(payload)
@@ -107,8 +106,8 @@ export function GetSortedPayloadsByPriority(
itemsKeyPayloads,
contentTypePriorityPayloads: SortPayloadsByRecentAndContentPriority(
contentTypePriorityPayloads,
contentTypePriorityList,
options.contentTypePriority,
),
remainingPayloads: SortPayloadsByRecentAndUuidPriority(remainingPayloads, uuidPriorityList),
remainingPayloads: SortPayloadsByRecentAndUuidPriority(remainingPayloads, options.uuidPriority),
}
}
@@ -2,10 +2,14 @@ import { ApplicationIdentifier } from '@standardnotes/common'
import {
FullyFormedTransferPayload,
TransferPayload,
LegacyRawKeychainValue,
NamespacedRootKeyInKeychain,
Environment,
} from '@standardnotes/models'
import {
DatabaseLoadOptions,
DatabaseKeysLoadChunkResponse,
DatabaseFullEntryLoadChunkResponse,
} from './DatabaseLoadOptions'
/**
* Platforms must override this class to provide platform specific utilities
@@ -21,8 +25,6 @@ export interface DeviceInterface {
getJsonParsedRawStorageValue(key: string): Promise<unknown | undefined>
getAllRawStorageKeyValues(): Promise<{ key: string; value: unknown }[]>
setRawStorageValue(key: string, value: string): Promise<void>
removeRawStorageValue(key: string): Promise<void>
@@ -38,10 +40,10 @@ export interface DeviceInterface {
*/
openDatabase(identifier: ApplicationIdentifier): Promise<{ isNewDatabase?: boolean } | undefined>
/**
* In a key/value database, this function returns just the keys.
*/
getDatabaseKeys(): Promise<string[]>
getDatabaseLoadChunks(
options: DatabaseLoadOptions,
identifier: ApplicationIdentifier,
): Promise<DatabaseKeysLoadChunkResponse | DatabaseFullEntryLoadChunkResponse>
/**
* Remove all keychain and database data from device.
@@ -52,17 +54,22 @@ export interface DeviceInterface {
*/
clearAllDataFromDevice(workspaceIdentifiers: ApplicationIdentifier[]): Promise<{ killsApplication: boolean }>
getAllRawDatabasePayloads<T extends FullyFormedTransferPayload = FullyFormedTransferPayload>(
getAllDatabaseEntries<T extends FullyFormedTransferPayload = FullyFormedTransferPayload>(
identifier: ApplicationIdentifier,
): Promise<T[]>
saveRawDatabasePayload(payload: TransferPayload, identifier: ApplicationIdentifier): Promise<void>
getDatabaseEntries<T extends FullyFormedTransferPayload = FullyFormedTransferPayload>(
identifier: ApplicationIdentifier,
keys: string[],
): Promise<T[]>
saveRawDatabasePayloads(payloads: TransferPayload[], identifier: ApplicationIdentifier): Promise<void>
saveDatabaseEntry(payload: TransferPayload, identifier: ApplicationIdentifier): Promise<void>
removeRawDatabasePayloadWithId(id: string, identifier: ApplicationIdentifier): Promise<void>
saveDatabaseEntries(payloads: TransferPayload[], identifier: ApplicationIdentifier): Promise<void>
removeAllRawDatabasePayloads(identifier: ApplicationIdentifier): Promise<void>
removeDatabaseEntry(id: string, identifier: ApplicationIdentifier): Promise<void>
removeAllDatabaseEntries(identifier: ApplicationIdentifier): Promise<void>
getNamespacedKeychainValue(identifier: ApplicationIdentifier): Promise<NamespacedRootKeyInKeychain | undefined>
@@ -70,8 +77,6 @@ export interface DeviceInterface {
clearNamespacedKeychainValue(identifier: ApplicationIdentifier): Promise<void>
setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void>
clearRawKeychainValue(): Promise<void>
openUrl(url: string): void
@@ -1,7 +1,13 @@
import { FullyFormedPayloadInterface, PayloadInterface, RootKeyInterface } from '@standardnotes/models'
import {
FullyFormedPayloadInterface,
PayloadInterface,
RootKeyInterface,
FullyFormedTransferPayload,
} from '@standardnotes/models'
import { StoragePersistencePolicies, StorageValueModes } from './StorageTypes'
export interface StorageServiceInterface {
getAllRawPayloads(): Promise<FullyFormedTransferPayload[]>
getValue<T>(key: string, mode?: StorageValueModes, defaultValue?: T): T
canDecryptWithKey(key: RootKeyInterface): Promise<boolean>
savePayload(payload: PayloadInterface): Promise<void>
@@ -11,6 +11,7 @@ export type SyncOptions = {
checkIntegrity?: boolean
/** Internally used to keep track of how sync requests were spawned. */
source: SyncSource
sourceDescription?: string
/** Whether to await any sync requests that may be queued from this call. */
awaitAll?: boolean
/**
+5
View File
@@ -6,6 +6,8 @@ export * from './Application/ApplicationStage'
export * from './Application/DeinitCallback'
export * from './Application/DeinitSource'
export * from './Application/DeinitMode'
export * from './Authenticator/AuthenticatorClientInterface'
export * from './Authenticator/AuthenticatorManager'
export * from './User/UserClientInterface'
export * from './Application/WebApplicationInterface'
export * from './Backups/BackupService'
@@ -20,6 +22,9 @@ export * from './Device/DeviceInterface'
export * from './Device/MobileDeviceInterface'
export * from './Device/TypeCheck'
export * from './Device/WebOrDesktopDeviceInterface'
export * from './Device/DatabaseLoadOptions'
export * from './Device/DatabaseItemMetadata'
export * from './Device/DatabaseLoadSorter'
export * from './Diagnostics/ServiceDiagnostics'
export * from './Encryption/BackupFileDecryptor'
export * from './Encryption/EncryptionService'
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.158.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
### Features
* improve initial load performance on mobile ([#2126](https://github.com/standardnotes/app/issues/2126)) ([3c332a3](https://github.com/standardnotes/app/commit/3c332a35f6024c46020a1d6ac68a5df989804142))
## [2.157.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-03)
**Note:** Version bump only for package @standardnotes/snjs
# [2.157.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
### Features
* **api:** add authenticators api ([#2124](https://github.com/standardnotes/app/issues/2124)) ([59e8b5c](https://github.com/standardnotes/app/commit/59e8b5c8b5b61ef1cd8f291798c45860736a1296))
## [2.156.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-29)
**Note:** Version bump only for package @standardnotes/snjs
## [2.156.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-28)
**Note:** Version bump only for package @standardnotes/snjs
## [2.156.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-27)
**Note:** Version bump only for package @standardnotes/snjs
+5 -3
View File
@@ -40,16 +40,18 @@ Object.assign(window, SNLibrary);
### E2E Tests
To run a stable server environment for E2E tests that is up to date with production, clone the [e2e repository](https://github.com/standardnotes/e2e), then run:
To run a stable server environment for E2E tests that is up to date with production, clone the [self-hosted repository](https://github.com/standardnotes/self-hosted), then run:
In self-hosted repo:
```
yarn install --immutable
yarn start:local-server
yarn && yarn start
```
Wait for the `All services are up!` message.
Once the server infrastructure is ready, and you've built all packages, you can run the test suite in the browser via:
In app repo:
```
yarn start:server:e2e
```
@@ -25,7 +25,7 @@ describe('application', () => {
device = {} as jest.Mocked<DeviceInterface>
device.openDatabase = jest.fn().mockResolvedValue(true)
device.getAllRawDatabasePayloads = jest.fn().mockReturnValue([])
device.getAllDatabaseEntries = jest.fn().mockReturnValue([])
device.setRawStorageValue = jest.fn()
device.getRawStorageValue = jest.fn().mockImplementation((key) => {
if (key === namespacedKey(identifier, RawStorageKey.SnjsVersion)) {
@@ -33,9 +33,6 @@ describe('application', () => {
}
return undefined
})
device.getDatabaseKeys = async () => {
return Promise.resolve(['1', '2', '3'])
}
application = new SNApplication({
environment: Environment.Mobile,
@@ -75,7 +72,6 @@ describe('application', () => {
currentPersistPromise: false,
isStorageWrapped: false,
allRawPayloadsCount: 0,
databaseKeys: ['1', '2', '3'],
},
encryption: expect.objectContaining({
getLatestVersion: '004',
+50 -17
View File
@@ -1,4 +1,8 @@
import {
AuthenticatorApiService,
AuthenticatorApiServiceInterface,
AuthenticatorServer,
AuthenticatorServerInterface,
HttpService,
HttpServiceInterface,
SubscriptionApiService,
@@ -63,6 +67,8 @@ import {
CredentialsChangeFunctionResponse,
SessionStrings,
AccountEvent,
AuthenticatorClientInterface,
AuthenticatorManager,
} from '@standardnotes/services'
import { BackupServiceInterface, FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateUsername } from '@standardnotes/encryption'
@@ -159,6 +165,9 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
private filesBackupService?: FilesBackupService
private declare sessionStorageMapper: MapperInterface<Session, Record<string, unknown>>
private declare legacySessionStorageMapper: MapperInterface<LegacySession, Record<string, unknown>>
private declare authenticatorApiService: AuthenticatorApiServiceInterface
private declare authenticatorServer: AuthenticatorServerInterface
private declare authenticatorManager: AuthenticatorClientInterface
private internalEventBus!: ExternalServices.InternalEventBusInterface
@@ -401,28 +410,32 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
await this.notifyEvent(ApplicationEvent.Launched)
await this.handleStage(ExternalServices.ApplicationStage.Launched_10)
const databasePayloads = await this.syncService.getDatabasePayloads()
await this.handleStage(ExternalServices.ApplicationStage.LoadingDatabase_11)
if (this.createdNewDatabase) {
await this.syncService.onNewDatabaseCreated()
}
/**
* We don't want to await this, as we want to begin allowing the app to function
* before local data has been loaded fully. We await only initial
* `getDatabasePayloads` to lock in on database state.
* before local data has been loaded fully.
*/
const loadPromise = this.syncService.loadDatabasePayloads(databasePayloads).then(async () => {
if (this.dealloced) {
throw 'Application has been destroyed.'
}
await this.handleStage(ExternalServices.ApplicationStage.LoadedDatabase_12)
this.beginAutoSyncTimer()
await this.syncService.sync({
mode: ExternalServices.SyncMode.DownloadFirst,
source: ExternalServices.SyncSource.External,
const loadPromise = this.syncService
.loadDatabasePayloads()
.then(async () => {
if (this.dealloced) {
throw 'Application has been destroyed.'
}
await this.handleStage(ExternalServices.ApplicationStage.LoadedDatabase_12)
this.beginAutoSyncTimer()
await this.syncService.sync({
mode: ExternalServices.SyncMode.DownloadFirst,
source: ExternalServices.SyncSource.External,
sourceDescription: 'Application Launch',
})
})
.catch((error) => {
void this.notifyEvent(ApplicationEvent.LocalDatabaseReadError, error)
throw error
})
})
if (awaitDatabaseLoad) {
await loadPromise
}
@@ -454,7 +467,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
private beginAutoSyncTimer() {
this.autoSyncInterval = setInterval(() => {
this.syncService.log('Syncing from autosync')
void this.sync.sync()
void this.sync.sync({ sourceDescription: 'Auto Sync' })
}, DEFAULT_AUTO_SYNC_INTERVAL)
}
@@ -1134,6 +1147,9 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.createMutatorService()
this.createListedService()
this.createActionsManager()
this.createAuthenticatorServer()
this.createAuthenticatorApiService()
this.createAuthenticatorManager()
}
private clearServices() {
@@ -1181,6 +1197,9 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
;(this.statusService as unknown) = undefined
;(this.sessionStorageMapper as unknown) = undefined
;(this.legacySessionStorageMapper as unknown) = undefined
;(this.authenticatorApiService as unknown) = undefined
;(this.authenticatorServer as unknown) = undefined
;(this.authenticatorManager as unknown) = undefined
this.services = []
}
@@ -1527,10 +1546,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
switch (event) {
case InternalServices.SessionEvent.Restored: {
void (async () => {
await this.sync.sync()
await this.sync.sync({ sourceDescription: 'Session restored pre key creation' })
if (this.protocolService.needsNewRootKeyBasedItemsKey()) {
void this.protocolService.createNewDefaultItemsKey().then(() => {
void this.sync.sync()
void this.sync.sync({ sourceDescription: 'Session restored post key creation' })
})
}
})()
@@ -1558,6 +1577,8 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.payloadManager,
this.apiService,
this.historyManager,
this.deviceInterface,
this.identifier,
{
loadBatchSize: this.options.loadBatchSize,
},
@@ -1705,4 +1726,16 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.statusService = new ExternalServices.StatusService(this.internalEventBus)
this.services.push(this.statusService)
}
private createAuthenticatorServer() {
this.authenticatorServer = new AuthenticatorServer(this.httpService)
}
private createAuthenticatorApiService() {
this.authenticatorApiService = new AuthenticatorApiService(this.authenticatorServer)
}
private createAuthenticatorManager() {
this.authenticatorManager = new AuthenticatorManager(this.authenticatorApiService, this.internalEventBus)
}
}
+1
View File
@@ -2,3 +2,4 @@ export * from './Application'
export * from './Event'
export * from './LiveItem'
export * from './Platforms'
export * from './Options/Defaults'

Some files were not shown because too many files have changed in this diff Show More