mirror of
https://github.com/standardnotes/app
synced 2026-09-13 09:46:02 -04:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ff7fb605a | ||
|
|
3c332a35f6 | ||
|
|
a447fa1ad7 | ||
|
|
76d2d2112b | ||
|
|
63cd1f9b07 | ||
|
|
fbc93a4f28 | ||
|
|
a896b4d9cf | ||
|
|
c0a0ea185c | ||
|
|
a3d8f1f3ff | ||
|
|
6e4bf2417a | ||
|
|
f1ae7a53ef | ||
|
|
cd5bd1640c | ||
|
|
7508a23979 | ||
|
|
59e8b5c8b5 | ||
|
|
5a8a37413e | ||
|
|
113d3bebb4 | ||
|
|
874f2e90bb | ||
|
|
df4acbb1bc | ||
|
|
b75be1d0f8 | ||
|
|
015dd1e806 | ||
|
|
8e3ae80aa0 | ||
|
|
5a0d87845c | ||
|
|
b764296c8f | ||
|
|
b0c11b1b4d | ||
|
|
e81ecfc024 | ||
|
|
b408d0a4c5 | ||
|
|
9ec68c9bf6 | ||
|
|
2611c690b0 |
@@ -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
|
||||
|
||||
@@ -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.
@@ -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,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>
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export interface GenerateAuthenticatorAuthenticationOptionsRequestParams {
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export interface GenerateAuthenticatorRegistrationOptionsRequestParams {
|
||||
userUuid: string
|
||||
username: string
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface ListAuthenticatorsRequestParams {
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
export interface VerifyAuthenticatorAuthenticationResponseRequestParams {
|
||||
userUuid: string
|
||||
authenticationCredential: Record<string, unknown>
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
export interface VerifyAuthenticatorRegistrationResponseRequestParams {
|
||||
userUuid: string
|
||||
name: string
|
||||
registrationCredential: Record<string, unknown>
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+10
@@ -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>
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export interface GenerateAuthenticatorAuthenticationOptionsResponseBody {
|
||||
options: Record<string, unknown>
|
||||
}
|
||||
+10
@@ -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>
|
||||
}
|
||||
+3
@@ -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
|
||||
}>
|
||||
}
|
||||
+10
@@ -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>
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export interface VerifyAuthenticatorAuthenticationResponseResponseBody {
|
||||
success: boolean
|
||||
}
|
||||
+10
@@ -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>
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export interface VerifyAuthenticatorRegistrationResponseResponseBody {
|
||||
success: boolean
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from './Authenticator/AuthenticatorServer'
|
||||
export * from './Authenticator/AuthenticatorServerInterface'
|
||||
export * from './Subscription/SubscriptionServer'
|
||||
export * from './Subscription/SubscriptionServerInterface'
|
||||
export * from './User/UserServer'
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/blocks-editor",
|
||||
"version": "1.12.1",
|
||||
"version": "1.12.2",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.19.29",
|
||||
"version": "1.19.33",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -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,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',
|
||||
}
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.27.7",
|
||||
"version": "1.28.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.14.6",
|
||||
"version": "1.14.10",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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,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())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/responses",
|
||||
"version": "1.12.10",
|
||||
"version": "1.12.12",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
+16
-12
@@ -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)
|
||||
+24
-25
@@ -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
|
||||
/**
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user