mirror of
https://github.com/standardnotes/app
synced 2026-09-15 06:45:59 -04:00
Compare commits
56
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2dc739686 | ||
|
|
fabce1f7ac | ||
|
|
3fbbacfc25 | ||
|
|
990939318f | ||
|
|
46e3cae804 | ||
|
|
e3f28421ff | ||
|
|
d22c164e5d | ||
|
|
5cf37037b7 | ||
|
|
9fc77d861e | ||
|
|
b80b04d374 | ||
|
|
b82eac09a8 | ||
|
|
7c9f0d3143 | ||
|
|
fb41f651ec | ||
|
|
7a43e2d368 | ||
|
|
49b3d2714f | ||
|
|
1540143dad | ||
|
|
8376381981 | ||
|
|
15e2c82e65 | ||
|
|
1c22c1ab0c | ||
|
|
27d2c95b5b | ||
|
|
dcb8024deb | ||
|
|
85d2415625 | ||
|
|
e2e7605414 | ||
|
|
158ca6ac6a | ||
|
|
a275a45753 | ||
|
|
8863ee82bb | ||
|
|
6a4b5c5cdc | ||
|
|
cd1669f56f | ||
|
|
037007f0ec | ||
|
|
e7ca12babe | ||
|
|
c4c33db1a4 | ||
|
|
734b986294 | ||
|
|
59db63f052 | ||
|
|
20226c3269 | ||
|
|
a1352d9f65 | ||
|
|
7d21046b51 | ||
|
|
a365c17c46 | ||
|
|
c1981334e1 | ||
|
|
1e3acd50e9 | ||
|
|
8de6023848 | ||
|
|
01ba715eba | ||
|
|
3733707bf1 | ||
|
|
cbbe913cd6 | ||
|
|
c13dd883a4 | ||
|
|
1377846f3f | ||
|
|
f9ee197f04 | ||
|
|
c944eb9365 | ||
|
|
4609053bd0 | ||
|
|
1c10214104 | ||
|
|
1602d8157f | ||
|
|
ed1d583476 | ||
|
|
05aff2776b | ||
|
|
4b6662d62d | ||
|
|
ac0ad49af0 | ||
|
|
09b994f8f9 | ||
|
|
6c26b96cdc |
@@ -48,16 +48,6 @@ jobs:
|
||||
uses: martinbeentjes/npm-get-version-action@main
|
||||
with:
|
||||
path: packages/mobile
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
tag_name: "@standardnotes/mobile@${{ steps.package-version.outputs.current-version}}"
|
||||
prerelease: true
|
||||
draft: true
|
||||
name: "Mobile Dev ${{ steps.package-version.outputs.current-version }}"
|
||||
files: |
|
||||
packages/mobile/android/app/build/outputs/apk/dev/release/app-dev-release.apk
|
||||
ios:
|
||||
defaults:
|
||||
run:
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,67 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add keyshare initiation for workspaces ([fabce1f](https://github.com/standardnotes/app/commit/fabce1f7aca5c9caaa5bb3908b2dc68063c690d0))
|
||||
|
||||
## [1.15.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
# [1.15.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
|
||||
|
||||
# [1.14.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add access level to invitation requests ([49b3d27](https://github.com/standardnotes/app/commit/49b3d2714fbe4ac9f9242097b8df6b4e3d604513))
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add accepting invites to workspace ([15e2c82](https://github.com/standardnotes/app/commit/15e2c82e654ce3f983cd42b01f14d18435b3e939))
|
||||
* **api:** add listing workspaces ([8376381](https://github.com/standardnotes/app/commit/837638198112df24c30e53803a7ddf5f841975b0))
|
||||
|
||||
## [1.12.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
# [1.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** specs ([85d2415](https://github.com/standardnotes/app/commit/85d24156254015914c684f900c77c080caa52eb7))
|
||||
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
|
||||
* linter issues ([e2e7605](https://github.com/standardnotes/app/commit/e2e76054142e8d67cac69280e2a3efcb15cf67f1))
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add inviting to workspace ([158ca6a](https://github.com/standardnotes/app/commit/158ca6ac6a428e082c8f984791f80fcaadcb3e34))
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add workspaces api ([#1765](https://github.com/standardnotes/app/issues/1765)) ([01ba715](https://github.com/standardnotes/app/commit/01ba715eba987a7da1ee062fec0b3593a7a453ed))
|
||||
|
||||
# [1.10.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [1.9.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.9.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
@@ -10,10 +10,10 @@ module.exports = {
|
||||
},
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 20,
|
||||
functions: 66,
|
||||
lines: 63,
|
||||
statements: 63
|
||||
branches: 22,
|
||||
functions: 69,
|
||||
lines: 67,
|
||||
statements: 67
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.9.1",
|
||||
"version": "1.16.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.32.0",
|
||||
"@standardnotes/common": "^1.39.0",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RootKeyParamsInterface } from '@standardnotes/models'
|
||||
|
||||
import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||
import { ApiCallError } from '../../Error/ApiCallError'
|
||||
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export enum WorkspaceApiOperations {
|
||||
Creating,
|
||||
Inviting,
|
||||
Accepting,
|
||||
ListingWorkspaces,
|
||||
ListingWorkspaceUsers,
|
||||
InitiatingKeyshare,
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
import { WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
|
||||
|
||||
import { HttpStatusCode } from '../../Http'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
|
||||
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
|
||||
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
|
||||
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
|
||||
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
|
||||
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
|
||||
|
||||
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
|
||||
import { WorkspaceApiService } from './WorkspaceApiService'
|
||||
|
||||
describe('WorkspaceApiService', () => {
|
||||
let workspaceServer: WorkspaceServerInterface
|
||||
|
||||
const createService = () => new WorkspaceApiService(workspaceServer)
|
||||
|
||||
beforeEach(() => {
|
||||
workspaceServer = {} as jest.Mocked<WorkspaceServerInterface>
|
||||
workspaceServer.createWorkspace = jest.fn().mockReturnValue({
|
||||
data: { uuid: '1-2-3' },
|
||||
} as jest.Mocked<WorkspaceCreationResponse>)
|
||||
workspaceServer.inviteToWorkspace = jest.fn().mockReturnValue({
|
||||
data: { uuid: 'i-1-2-3' },
|
||||
} as jest.Mocked<WorkspaceInvitationResponse>)
|
||||
workspaceServer.acceptInvite = jest.fn().mockReturnValue({
|
||||
data: { success: true },
|
||||
} as jest.Mocked<WorkspaceInvitationAcceptingResponse>)
|
||||
workspaceServer.listWorkspaces = jest.fn().mockReturnValue({
|
||||
status: HttpStatusCode.Success,
|
||||
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
|
||||
} as jest.Mocked<WorkspaceListResponse>)
|
||||
workspaceServer.listWorkspaceUsers = jest.fn().mockReturnValue({
|
||||
status: HttpStatusCode.Success,
|
||||
data: { users: [] },
|
||||
} as jest.Mocked<WorkspaceUserListResponse>)
|
||||
workspaceServer.initiateKeyshare = jest.fn().mockReturnValue({
|
||||
status: HttpStatusCode.Success,
|
||||
data: { success: true },
|
||||
} as jest.Mocked<WorkspaceKeyshareInitiatingResponse>)
|
||||
})
|
||||
|
||||
it('should create a workspace', async () => {
|
||||
const response = await createService().createWorkspace({
|
||||
workspaceType: WorkspaceType.Private,
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
})
|
||||
expect(workspaceServer.createWorkspace).toHaveBeenCalledWith({
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
workspaceType: 'private',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a workspace if it is already creating', async () => {
|
||||
const service = createService()
|
||||
Object.defineProperty(service, 'operationsInProgress', {
|
||||
get: () => new Map([[WorkspaceApiOperations.Creating, true]]),
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.createWorkspace({
|
||||
workspaceType: WorkspaceType.Private,
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not create a workspace if the server fails', async () => {
|
||||
workspaceServer.createWorkspace = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().createWorkspace({
|
||||
workspaceType: WorkspaceType.Private,
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should invite to a workspace', async () => {
|
||||
const response = await createService().inviteToWorkspace({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
inviteeEmail: '[email protected]',
|
||||
accessLevel: WorkspaceAccessLevel.WriteAndRead,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
uuid: 'i-1-2-3',
|
||||
},
|
||||
})
|
||||
expect(workspaceServer.inviteToWorkspace).toHaveBeenCalledWith({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
inviteeEmail: '[email protected]',
|
||||
accessLevel: 'write-and-read',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not invite to a workspace if it is already inviting', async () => {
|
||||
const service = createService()
|
||||
Object.defineProperty(service, 'operationsInProgress', {
|
||||
get: () => new Map([[WorkspaceApiOperations.Inviting, true]]),
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.inviteToWorkspace({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
inviteeEmail: '[email protected]',
|
||||
accessLevel: WorkspaceAccessLevel.WriteAndRead,
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not invite to a workspace if the server fails', async () => {
|
||||
workspaceServer.inviteToWorkspace = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().inviteToWorkspace({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
inviteeEmail: '[email protected]',
|
||||
accessLevel: WorkspaceAccessLevel.WriteAndRead,
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should accept invite to a workspace', async () => {
|
||||
const response = await createService().acceptInvite({
|
||||
userUuid: 'u-1-2-3',
|
||||
inviteUuid: 'i-1-2-3',
|
||||
publicKey: 'foo',
|
||||
encryptedPrivateKey: 'bar',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
})
|
||||
expect(workspaceServer.acceptInvite).toHaveBeenCalledWith({
|
||||
userUuid: 'u-1-2-3',
|
||||
inviteUuid: 'i-1-2-3',
|
||||
publicKey: 'foo',
|
||||
encryptedPrivateKey: 'bar',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not accept invite to a workspace if it is already accepting', async () => {
|
||||
const service = createService()
|
||||
Object.defineProperty(service, 'operationsInProgress', {
|
||||
get: () => new Map([[WorkspaceApiOperations.Accepting, true]]),
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.acceptInvite({
|
||||
userUuid: 'u-1-2-3',
|
||||
inviteUuid: 'i-1-2-3',
|
||||
publicKey: 'foo',
|
||||
encryptedPrivateKey: 'bar',
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not accept invite to a workspace if the server fails', async () => {
|
||||
workspaceServer.acceptInvite = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().acceptInvite({
|
||||
userUuid: 'u-1-2-3',
|
||||
inviteUuid: 'i-1-2-3',
|
||||
publicKey: 'foo',
|
||||
encryptedPrivateKey: 'bar',
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should list workspaces', async () => {
|
||||
const response = await createService().listWorkspaces()
|
||||
|
||||
expect(response).toEqual({
|
||||
status: 200,
|
||||
data: {
|
||||
ownedWorkspaces: [],
|
||||
joinedWorkspaces: [],
|
||||
},
|
||||
})
|
||||
expect(workspaceServer.listWorkspaces).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not list workspaces if it is already listing them', async () => {
|
||||
const service = createService()
|
||||
Object.defineProperty(service, 'operationsInProgress', {
|
||||
get: () => new Map([[WorkspaceApiOperations.ListingWorkspaces, true]]),
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.listWorkspaces()
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not list workspaces if the server fails', async () => {
|
||||
workspaceServer.listWorkspaces = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().listWorkspaces()
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should list workspace users', async () => {
|
||||
const response = await createService().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
|
||||
|
||||
expect(response).toEqual({
|
||||
status: 200,
|
||||
data: {
|
||||
users: [],
|
||||
},
|
||||
})
|
||||
expect(workspaceServer.listWorkspaceUsers).toHaveBeenCalledWith({ workspaceUuid: 'w-1-2-3' })
|
||||
})
|
||||
|
||||
it('should not list workspace users if it is already listing them', async () => {
|
||||
const service = createService()
|
||||
Object.defineProperty(service, 'operationsInProgress', {
|
||||
get: () => new Map([[WorkspaceApiOperations.ListingWorkspaceUsers, true]]),
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not list workspace users if the server fails', async () => {
|
||||
workspaceServer.listWorkspaceUsers = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should initiate keyshare in workspace for user', async () => {
|
||||
const response = await createService().initiateKeyshare({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
})
|
||||
expect(workspaceServer.initiateKeyshare).toHaveBeenCalledWith({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not initiate keyshare in workspace if it is already initiating', async () => {
|
||||
const service = createService()
|
||||
Object.defineProperty(service, 'operationsInProgress', {
|
||||
get: () => new Map([[WorkspaceApiOperations.InitiatingKeyshare, true]]),
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.initiateKeyshare({ workspaceUuid: 'w-1-2-3', userUuid: 'u-1-2-3', encryptedWorkspaceKey: 'foobar' })
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not initiate keyshare in workspace if the server fails', async () => {
|
||||
workspaceServer.initiateKeyshare = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().initiateKeyshare({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,156 @@
|
||||
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
|
||||
|
||||
import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||
import { ApiCallError } from '../../Error/ApiCallError'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
|
||||
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
|
||||
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
|
||||
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
|
||||
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
|
||||
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
|
||||
|
||||
import { WorkspaceApiServiceInterface } from './WorkspaceApiServiceInterface'
|
||||
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
|
||||
|
||||
export class WorkspaceApiService implements WorkspaceApiServiceInterface {
|
||||
private operationsInProgress: Map<WorkspaceApiOperations, boolean>
|
||||
|
||||
constructor(private workspaceServer: WorkspaceServerInterface) {
|
||||
this.operationsInProgress = new Map()
|
||||
}
|
||||
|
||||
async initiateKeyshare(dto: {
|
||||
workspaceUuid: string
|
||||
userUuid: string
|
||||
encryptedWorkspaceKey: string
|
||||
}): Promise<WorkspaceKeyshareInitiatingResponse> {
|
||||
this.lockOperation(WorkspaceApiOperations.InitiatingKeyshare)
|
||||
|
||||
try {
|
||||
const response = await this.workspaceServer.initiateKeyshare({
|
||||
workspaceUuid: dto.workspaceUuid,
|
||||
userUuid: dto.userUuid,
|
||||
encryptedWorkspaceKey: dto.encryptedWorkspaceKey,
|
||||
})
|
||||
|
||||
this.unlockOperation(WorkspaceApiOperations.InitiatingKeyshare)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
async listWorkspaceUsers(dto: { workspaceUuid: string }): Promise<WorkspaceUserListResponse> {
|
||||
this.lockOperation(WorkspaceApiOperations.ListingWorkspaceUsers)
|
||||
|
||||
try {
|
||||
const response = await this.workspaceServer.listWorkspaceUsers({ workspaceUuid: dto.workspaceUuid })
|
||||
|
||||
this.unlockOperation(WorkspaceApiOperations.ListingWorkspaceUsers)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
async listWorkspaces(): Promise<WorkspaceListResponse> {
|
||||
this.lockOperation(WorkspaceApiOperations.ListingWorkspaces)
|
||||
|
||||
try {
|
||||
const response = await this.workspaceServer.listWorkspaces({})
|
||||
|
||||
this.unlockOperation(WorkspaceApiOperations.ListingWorkspaces)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
async acceptInvite(dto: {
|
||||
inviteUuid: string
|
||||
userUuid: string
|
||||
publicKey: string
|
||||
encryptedPrivateKey: string
|
||||
}): Promise<WorkspaceInvitationAcceptingResponse> {
|
||||
this.lockOperation(WorkspaceApiOperations.Accepting)
|
||||
|
||||
try {
|
||||
const response = await this.workspaceServer.acceptInvite({
|
||||
encryptedPrivateKey: dto.encryptedPrivateKey,
|
||||
publicKey: dto.publicKey,
|
||||
inviteUuid: dto.inviteUuid,
|
||||
userUuid: dto.userUuid,
|
||||
})
|
||||
|
||||
this.unlockOperation(WorkspaceApiOperations.Accepting)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
async inviteToWorkspace(dto: {
|
||||
inviteeEmail: string
|
||||
workspaceUuid: Uuid
|
||||
accessLevel: WorkspaceAccessLevel
|
||||
}): Promise<WorkspaceInvitationResponse> {
|
||||
this.lockOperation(WorkspaceApiOperations.Inviting)
|
||||
|
||||
try {
|
||||
const response = await this.workspaceServer.inviteToWorkspace({
|
||||
inviteeEmail: dto.inviteeEmail,
|
||||
workspaceUuid: dto.workspaceUuid,
|
||||
accessLevel: dto.accessLevel,
|
||||
})
|
||||
|
||||
this.unlockOperation(WorkspaceApiOperations.Inviting)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
async createWorkspace(dto: {
|
||||
workspaceType: WorkspaceType
|
||||
encryptedWorkspaceKey?: string
|
||||
encryptedPrivateKey?: string
|
||||
publicKey?: string
|
||||
workspaceName?: string
|
||||
}): Promise<WorkspaceCreationResponse> {
|
||||
this.lockOperation(WorkspaceApiOperations.Creating)
|
||||
|
||||
try {
|
||||
const response = await this.workspaceServer.createWorkspace({
|
||||
workspaceType: dto.workspaceType,
|
||||
encryptedPrivateKey: dto.encryptedPrivateKey,
|
||||
encryptedWorkspaceKey: dto.encryptedWorkspaceKey,
|
||||
publicKey: dto.publicKey,
|
||||
workspaceName: dto.workspaceName,
|
||||
})
|
||||
|
||||
this.unlockOperation(WorkspaceApiOperations.Creating)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
|
||||
private lockOperation(operation: WorkspaceApiOperations): void {
|
||||
if (this.operationsInProgress.get(operation)) {
|
||||
throw new ApiCallError(ErrorMessage.GenericInProgress)
|
||||
}
|
||||
|
||||
this.operationsInProgress.set(operation, true)
|
||||
}
|
||||
|
||||
private unlockOperation(operation: WorkspaceApiOperations): void {
|
||||
this.operationsInProgress.set(operation, false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
|
||||
|
||||
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
|
||||
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
|
||||
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
|
||||
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
|
||||
|
||||
export interface WorkspaceApiServiceInterface {
|
||||
createWorkspace(dto: {
|
||||
workspaceType: WorkspaceType
|
||||
encryptedWorkspaceKey?: string
|
||||
encryptedPrivateKey?: string
|
||||
publicKey?: string
|
||||
workspaceName?: string
|
||||
}): Promise<WorkspaceCreationResponse>
|
||||
inviteToWorkspace(dto: {
|
||||
inviteeEmail: string
|
||||
workspaceUuid: Uuid
|
||||
accessLevel: WorkspaceAccessLevel
|
||||
}): Promise<WorkspaceInvitationResponse>
|
||||
acceptInvite(dto: {
|
||||
inviteUuid: Uuid
|
||||
userUuid: Uuid
|
||||
publicKey: string
|
||||
encryptedPrivateKey: string
|
||||
}): Promise<WorkspaceInvitationAcceptingResponse>
|
||||
listWorkspaces(): Promise<WorkspaceListResponse>
|
||||
listWorkspaceUsers(dto: { workspaceUuid: Uuid }): Promise<WorkspaceUserListResponse>
|
||||
initiateKeyshare(dto: {
|
||||
workspaceUuid: Uuid
|
||||
userUuid: Uuid
|
||||
encryptedWorkspaceKey: string
|
||||
}): Promise<WorkspaceKeyshareInitiatingResponse>
|
||||
}
|
||||
@@ -5,3 +5,5 @@ export * from './User/UserApiService'
|
||||
export * from './User/UserApiServiceInterface'
|
||||
export * from './WebSocket/WebSocketApiService'
|
||||
export * from './WebSocket/WebSocketApiServiceInterface'
|
||||
export * from './Workspace/WorkspaceApiService'
|
||||
export * from './Workspace/WorkspaceApiServiceInterface'
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { WorkspaceType } from '@standardnotes/common'
|
||||
|
||||
export type WorkspaceCreationRequestParams = {
|
||||
workspaceType: WorkspaceType
|
||||
encryptedWorkspaceKey?: string
|
||||
encryptedPrivateKey?: string
|
||||
publicKey?: string
|
||||
workspaceName?: string
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
export type WorkspaceInvitationAcceptingRequestParams = {
|
||||
inviteUuid: Uuid
|
||||
userUuid: Uuid
|
||||
publicKey: string
|
||||
encryptedPrivateKey: string
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Uuid, WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
|
||||
export type WorkspaceInvitationRequestParams = {
|
||||
workspaceUuid: Uuid
|
||||
inviteeEmail: string
|
||||
accessLevel: WorkspaceAccessLevel
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
export type WorkspaceKeyshareInitiatingRequestParams = {
|
||||
userUuid: Uuid
|
||||
workspaceUuid: Uuid
|
||||
encryptedWorkspaceKey: string
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type WorkspaceListRequestParams = {
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
export type WorkspaceUserListRequestParams = {
|
||||
workspaceUuid: Uuid
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -6,3 +6,9 @@ export * from './Subscription/SubscriptionInviteListRequestParams'
|
||||
export * from './Subscription/SubscriptionInviteRequestParams'
|
||||
export * from './User/UserRegistrationRequestParams'
|
||||
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
|
||||
export * from './Workspace/WorkspaceCreationRequestParams'
|
||||
export * from './Workspace/WorkspaceInvitationAcceptingRequestParams'
|
||||
export * from './Workspace/WorkspaceInvitationRequestParams'
|
||||
export * from './Workspace/WorkspaceKeyshareInitiatingRequestParams'
|
||||
export * from './Workspace/WorkspaceListRequestParams'
|
||||
export * from './Workspace/WorkspaceUserListRequestParams'
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Either } from '@standardnotes/common'
|
||||
|
||||
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||
import { HttpResponse } from '../../Http/HttpResponse'
|
||||
import { WorkspaceCreationResponseBody } from './WorkspaceCreationResponseBody'
|
||||
|
||||
export interface WorkspaceCreationResponse extends HttpResponse {
|
||||
data: Either<WorkspaceCreationResponseBody, HttpErrorResponseBody>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type WorkspaceCreationResponseBody = {
|
||||
uuid: string
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Either } from '@standardnotes/common'
|
||||
|
||||
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||
import { HttpResponse } from '../../Http/HttpResponse'
|
||||
import { WorkspaceInvitationAcceptingResponseBody } from './WorkspaceInvitationAcceptingResponseBody'
|
||||
|
||||
export interface WorkspaceInvitationAcceptingResponse extends HttpResponse {
|
||||
data: Either<WorkspaceInvitationAcceptingResponseBody, HttpErrorResponseBody>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type WorkspaceInvitationAcceptingResponseBody = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Either } from '@standardnotes/common'
|
||||
|
||||
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||
import { HttpResponse } from '../../Http/HttpResponse'
|
||||
import { WorkspaceInvitationResponseBody } from './WorkspaceInvitationResponseBody'
|
||||
|
||||
export interface WorkspaceInvitationResponse extends HttpResponse {
|
||||
data: Either<WorkspaceInvitationResponseBody, HttpErrorResponseBody>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type WorkspaceInvitationResponseBody = {
|
||||
uuid: string
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Either } from '@standardnotes/common'
|
||||
|
||||
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||
import { HttpResponse } from '../../Http/HttpResponse'
|
||||
import { WorkspaceKeyshareInitiatingResponseBody } from './WorkspaceKeyshareInitiatingResponseBody'
|
||||
|
||||
export interface WorkspaceKeyshareInitiatingResponse extends HttpResponse {
|
||||
data: Either<WorkspaceKeyshareInitiatingResponseBody, HttpErrorResponseBody>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type WorkspaceKeyshareInitiatingResponseBody = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Either } from '@standardnotes/common'
|
||||
|
||||
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||
import { HttpResponse } from '../../Http/HttpResponse'
|
||||
import { WorkspaceListResponseBody } from './WorkspaceListResponseBody'
|
||||
|
||||
export interface WorkspaceListResponse extends HttpResponse {
|
||||
data: Either<WorkspaceListResponseBody, HttpErrorResponseBody>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Workspace } from '@standardnotes/models'
|
||||
|
||||
export type WorkspaceListResponseBody = {
|
||||
ownedWorkspaces: Array<Workspace>
|
||||
joinedWorkspaces: Array<Workspace>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Either } from '@standardnotes/common'
|
||||
|
||||
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||
import { HttpResponse } from '../../Http/HttpResponse'
|
||||
import { WorkspaceUserListResponseBody } from './WorkspaceUserListResponseBody'
|
||||
|
||||
export interface WorkspaceUserListResponse extends HttpResponse {
|
||||
data: Either<WorkspaceUserListResponseBody, HttpErrorResponseBody>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { WorkspaceUser } from '@standardnotes/models'
|
||||
|
||||
export type WorkspaceUserListResponseBody = {
|
||||
users: Array<WorkspaceUser>
|
||||
}
|
||||
@@ -12,3 +12,15 @@ export * from './User/UserRegistrationResponse'
|
||||
export * from './User/UserRegistrationResponseBody'
|
||||
export * from './WebSocket/WebSocketConnectionTokenResponse'
|
||||
export * from './WebSocket/WebSocketConnectionTokenResponseBody'
|
||||
export * from './Workspace/WorkspaceCreationResponse'
|
||||
export * from './Workspace/WorkspaceCreationResponseBody'
|
||||
export * from './Workspace/WorkspaceInvitationAcceptingResponse'
|
||||
export * from './Workspace/WorkspaceInvitationAcceptingResponseBody'
|
||||
export * from './Workspace/WorkspaceKeyshareInitiatingResponse'
|
||||
export * from './Workspace/WorkspaceKeyshareInitiatingResponseBody'
|
||||
export * from './Workspace/WorkspaceInvitationResponse'
|
||||
export * from './Workspace/WorkspaceInvitationResponseBody'
|
||||
export * from './Workspace/WorkspaceListResponse'
|
||||
export * from './Workspace/WorkspaceListResponseBody'
|
||||
export * from './Workspace/WorkspaceUserListResponse'
|
||||
export * from './Workspace/WorkspaceUserListResponseBody'
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
const WorkspacePaths = {
|
||||
createWorkspace: '/v1/workspaces',
|
||||
listWorkspaces: '/v1/workspaces',
|
||||
listWorkspaceUsers: (uuid: Uuid) => `/v1/workspaces/${uuid}/users`,
|
||||
initiateKeyshare: (worksapceUuid: Uuid, userUuid: Uuid) =>
|
||||
`/v1/workspaces/${worksapceUuid}/users/${userUuid}/keyshare`,
|
||||
inviteToWorkspace: (uuid: Uuid) => `/v1/workspaces/${uuid}/invites`,
|
||||
acceptInvite: (uuid: Uuid) => `/v1/invites/${uuid}/accept`,
|
||||
}
|
||||
|
||||
export const Paths = {
|
||||
v1: {
|
||||
...WorkspacePaths,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
|
||||
|
||||
import { HttpServiceInterface, HttpStatusCode } from '../../Http'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
|
||||
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
|
||||
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
|
||||
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
|
||||
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
|
||||
|
||||
import { WorkspaceServer } from './WorkspaceServer'
|
||||
|
||||
describe('WorkspaceServer', () => {
|
||||
let httpService: HttpServiceInterface
|
||||
|
||||
const createServer = () => new WorkspaceServer(httpService)
|
||||
|
||||
beforeEach(() => {
|
||||
httpService = {} as jest.Mocked<HttpServiceInterface>
|
||||
httpService.post = jest.fn().mockReturnValue({
|
||||
data: { uuid: '1-2-3' },
|
||||
} as jest.Mocked<WorkspaceCreationResponse>)
|
||||
})
|
||||
|
||||
it('should create a workspace', async () => {
|
||||
const response = await createServer().createWorkspace({
|
||||
workspaceType: WorkspaceType.Private,
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should inivte to a workspace', async () => {
|
||||
httpService.post = jest.fn().mockReturnValue({
|
||||
data: { uuid: 'i-1-2-3' },
|
||||
} as jest.Mocked<WorkspaceInvitationResponse>)
|
||||
|
||||
const response = await createServer().inviteToWorkspace({
|
||||
inviteeEmail: '[email protected]',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
accessLevel: WorkspaceAccessLevel.WriteAndRead,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
uuid: 'i-1-2-3',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should accept invitation to a workspace', async () => {
|
||||
httpService.post = jest.fn().mockReturnValue({
|
||||
data: { success: true },
|
||||
} as jest.Mocked<WorkspaceInvitationAcceptingResponse>)
|
||||
|
||||
const response = await createServer().acceptInvite({
|
||||
encryptedPrivateKey: 'foo',
|
||||
inviteUuid: 'i-1-2-3',
|
||||
publicKey: 'bar',
|
||||
userUuid: 'u-1-2-3',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should list workspaces', async () => {
|
||||
httpService.get = jest.fn().mockReturnValue({
|
||||
status: HttpStatusCode.Success,
|
||||
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
|
||||
} as jest.Mocked<WorkspaceListResponse>)
|
||||
|
||||
const response = await createServer().listWorkspaces({})
|
||||
|
||||
expect(response).toEqual({
|
||||
status: 200,
|
||||
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
|
||||
})
|
||||
})
|
||||
|
||||
it('should list workspace users', async () => {
|
||||
httpService.get = jest.fn().mockReturnValue({
|
||||
status: HttpStatusCode.Success,
|
||||
data: { users: [] },
|
||||
} as jest.Mocked<WorkspaceUserListResponse>)
|
||||
|
||||
const response = await createServer().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
|
||||
|
||||
expect(response).toEqual({
|
||||
status: 200,
|
||||
data: { users: [] },
|
||||
})
|
||||
})
|
||||
|
||||
it('should initiate keyshare for user in a workspace', async () => {
|
||||
httpService.post = jest.fn().mockReturnValue({
|
||||
status: HttpStatusCode.Success,
|
||||
data: { success: true },
|
||||
} as jest.Mocked<WorkspaceKeyshareInitiatingResponse>)
|
||||
|
||||
const response = await createServer().initiateKeyshare({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
|
||||
import { WorkspaceInvitationRequestParams } from '../../Request/Workspace/WorkspaceInvitationRequestParams'
|
||||
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
|
||||
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspace/WorkspaceInvitationAcceptingRequestParams'
|
||||
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
|
||||
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
|
||||
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
|
||||
import { WorkspaceUserListRequestParams } from '../../Request/Workspace/WorkspaceUserListRequestParams'
|
||||
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
|
||||
import { WorkspaceKeyshareInitiatingRequestParams } from '../../Request/Workspace/WorkspaceKeyshareInitiatingRequestParams'
|
||||
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
|
||||
|
||||
import { Paths } from './Paths'
|
||||
import { WorkspaceServerInterface } from './WorkspaceServerInterface'
|
||||
|
||||
export class WorkspaceServer implements WorkspaceServerInterface {
|
||||
constructor(private httpService: HttpServiceInterface) {}
|
||||
|
||||
async initiateKeyshare(
|
||||
params: WorkspaceKeyshareInitiatingRequestParams,
|
||||
): Promise<WorkspaceKeyshareInitiatingResponse> {
|
||||
const response = await this.httpService.post(
|
||||
Paths.v1.initiateKeyshare(params.workspaceUuid, params.userUuid),
|
||||
params,
|
||||
)
|
||||
|
||||
return response as WorkspaceKeyshareInitiatingResponse
|
||||
}
|
||||
|
||||
async listWorkspaceUsers(params: WorkspaceUserListRequestParams): Promise<WorkspaceUserListResponse> {
|
||||
const response = await this.httpService.get(Paths.v1.listWorkspaceUsers(params.workspaceUuid), params)
|
||||
|
||||
return response as WorkspaceUserListResponse
|
||||
}
|
||||
|
||||
async listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse> {
|
||||
const response = await this.httpService.get(Paths.v1.listWorkspaces, params)
|
||||
|
||||
return response as WorkspaceListResponse
|
||||
}
|
||||
|
||||
async acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse> {
|
||||
const response = await this.httpService.post(Paths.v1.acceptInvite(params.inviteUuid), params)
|
||||
|
||||
return response as WorkspaceInvitationAcceptingResponse
|
||||
}
|
||||
|
||||
async inviteToWorkspace(params: WorkspaceInvitationRequestParams): Promise<WorkspaceInvitationResponse> {
|
||||
const response = await this.httpService.post(Paths.v1.inviteToWorkspace(params.workspaceUuid), params)
|
||||
|
||||
return response as WorkspaceInvitationResponse
|
||||
}
|
||||
|
||||
async createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse> {
|
||||
const response = await this.httpService.post(Paths.v1.createWorkspace, params)
|
||||
|
||||
return response as WorkspaceCreationResponse
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { WorkspaceInvitationRequestParams } from '../../Request/Workspace/WorkspaceInvitationRequestParams'
|
||||
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
|
||||
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspace/WorkspaceInvitationAcceptingRequestParams'
|
||||
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
|
||||
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
|
||||
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
|
||||
import { WorkspaceUserListRequestParams } from '../../Request/Workspace/WorkspaceUserListRequestParams'
|
||||
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
|
||||
import { WorkspaceKeyshareInitiatingRequestParams } from '../../Request/Workspace/WorkspaceKeyshareInitiatingRequestParams'
|
||||
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
|
||||
|
||||
export interface WorkspaceServerInterface {
|
||||
createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse>
|
||||
listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse>
|
||||
listWorkspaceUsers(params: WorkspaceUserListRequestParams): Promise<WorkspaceUserListResponse>
|
||||
inviteToWorkspace(params: WorkspaceInvitationRequestParams): Promise<WorkspaceInvitationResponse>
|
||||
acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse>
|
||||
initiateKeyshare(params: WorkspaceKeyshareInitiatingRequestParams): Promise<WorkspaceKeyshareInitiatingResponse>
|
||||
}
|
||||
@@ -4,3 +4,5 @@ export * from './User/UserServer'
|
||||
export * from './User/UserServerInterface'
|
||||
export * from './WebSocket/WebSocketServer'
|
||||
export * from './WebSocket/WebSocketServerInterface'
|
||||
export * from './Workspace/WorkspaceServer'
|
||||
export * from './Workspace/WorkspaceServerInterface'
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [2.7.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
## [2.7.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
## [2.7.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
## [2.7.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
## [2.7.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+6
-6
@@ -70,9 +70,9 @@
|
||||
"binary": "b621bfd81829a205f2f02e85d446e277ad20e367b92676ec20b3ee59b1bfab3f"
|
||||
},
|
||||
"org.standardnotes.markdown-visual-editor": {
|
||||
"version": "1.1.2",
|
||||
"base64": "2645d31c51fa6a198c14a32737ee4045dcbbecb8bdaa1432ca4686cab78834d2",
|
||||
"binary": "c4479f8768f5f6cda41850212a8e03927990adfa33ebd36e29ee6de2335e8e46"
|
||||
"version": "1.1.3",
|
||||
"base64": "8bb35d66b0ef2bee2243dbdf514c0bf50ef706c034395cb9d50119f017fef9aa",
|
||||
"binary": "1738193ab2a57cf280c29c9ca1e4caf49179009c7382048577d2b34eaf96d5e9"
|
||||
},
|
||||
"org.standardnotes.simple-task-editor": {
|
||||
"version": "1.4.4",
|
||||
@@ -80,9 +80,9 @@
|
||||
"binary": "9df983a144b9686f4f58b03bf78bf1f84f53aea60251621a75586722bf576475"
|
||||
},
|
||||
"org.standardnotes.token-vault": {
|
||||
"version": "2.2.4",
|
||||
"base64": "ac07bd989bfb1100aeaacf6687b492b4aac754645da5a132ecd879dc64786522",
|
||||
"binary": "8f74bacd0dd3a0a6e04bcce9c54694bb2f429b470a3023e96e0a750a271184ae"
|
||||
"version": "2.2.5",
|
||||
"base64": "ca103f730737d3a3046001666b626139692c08ab4dd5748b0290fdcb1b1eb04a",
|
||||
"binary": "52f419cd60da00a2cad13fd357b50993c7694160fcca1c1bdf1859e3bdca3a2b"
|
||||
},
|
||||
"org.standardnotes.standard-sheets": {
|
||||
"version": "1.5.1",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/components-meta",
|
||||
"version": "2.7.15",
|
||||
"version": "2.7.19",
|
||||
"private": true,
|
||||
"author": "Standard Notes.",
|
||||
"main": "dist",
|
||||
|
||||
+4
@@ -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.1.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/markdown-visual
|
||||
|
||||
## [1.1.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/markdown-visual",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"author": "Johnny Almonte <[email protected]>",
|
||||
"description": "A lightweight WYSIWYG markdown editor for Standard Notes, derived from Milkdown.",
|
||||
"keywords": [
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/authenticator
|
||||
|
||||
## [2.2.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/authenticator",
|
||||
"version": "2.2.4",
|
||||
"version": "2.2.5",
|
||||
"main": "dist/dist.js",
|
||||
"author": "Standard Notes",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,80 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.23.209](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.208](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.207](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.206](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.205](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.204](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.203](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.202](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.201](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.200](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.199](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-08)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.198](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **desktop:** use different method to send messages across main and render without node integration in preload ([#1769](https://github.com/standardnotes/app/issues/1769)) ([20226c3](https://github.com/standardnotes/app/commit/20226c326945ad9dc524f6dcbfc1c1274befb652))
|
||||
|
||||
## [3.23.197](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.196](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.195](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.194](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.193](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.192](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.191](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -5,6 +5,7 @@ import { autoUpdater } from 'electron-updater'
|
||||
import { action, autorun, computed, makeObservable, observable } from 'mobx'
|
||||
import { MessageType } from '../../../test/TestIpcMessage'
|
||||
import { AppState } from '../../AppState'
|
||||
import { MessageToWebApp } from '../Shared/IpcMessages'
|
||||
import { BackupsManagerInterface } from './Backups/BackupsManagerInterface'
|
||||
import { StoreKeys } from './Store/StoreKeys'
|
||||
import { updates as str } from './Strings'
|
||||
@@ -113,12 +114,12 @@ export function setupUpdates(window: BrowserWindow, appState: AppState, backupsM
|
||||
setInterval(checkUpdateSafety, oneHour)
|
||||
|
||||
autoUpdater.on('update-downloaded', (info: { version?: string }) => {
|
||||
window.webContents.send('update-available', null)
|
||||
window.webContents.send(MessageToWebApp.UpdateAvailable, null)
|
||||
updateState.autoUpdateHasBeenDownloaded(info.version || null)
|
||||
})
|
||||
|
||||
autoUpdater.on('error', logError)
|
||||
autoUpdater.on('update-available', (info: { version?: string }) => {
|
||||
autoUpdater.on(MessageToWebApp.UpdateAvailable, (info: { version?: string }) => {
|
||||
updateState.checkedForUpdate(info.version || null)
|
||||
if (updateState.enableAutoUpdate) {
|
||||
const canUpdate = checkUpdateSafety()
|
||||
|
||||
@@ -166,6 +166,7 @@ async function createWindow(store: Store): Promise<Electron.BrowserWindow> {
|
||||
spellcheck: true,
|
||||
nodeIntegration: isTesting(),
|
||||
contextIsolation: true,
|
||||
sandbox: true,
|
||||
preload: Paths.preloadJs,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,42 +1,28 @@
|
||||
import { IpcRendererEvent } from 'electron/renderer'
|
||||
import { MessageToWebApp } from '../Shared/IpcMessages'
|
||||
const { ipcRenderer } = require('electron')
|
||||
const path = require('path')
|
||||
const rendererPath = path.join('file://', __dirname, '/renderer.js')
|
||||
const RemoteBridge = require('@electron/remote').getGlobal('RemoteBridge')
|
||||
const { contextBridge } = require('electron')
|
||||
|
||||
type MainEventCallback = (event: IpcRendererEvent, value: any) => void
|
||||
|
||||
process.once('loaded', function () {
|
||||
contextBridge.exposeInMainWorld('electronRemoteBridge', RemoteBridge.exposableValue)
|
||||
|
||||
listenForIpcEventsFromMainProcess()
|
||||
contextBridge.exposeInMainWorld('electronMainEvents', {
|
||||
handleUpdateAvailable: (callback: MainEventCallback) => ipcRenderer.on(MessageToWebApp.UpdateAvailable, callback),
|
||||
|
||||
handlePerformAutomatedBackup: (callback: MainEventCallback) =>
|
||||
ipcRenderer.on(MessageToWebApp.PerformAutomatedBackup, callback),
|
||||
|
||||
handleFinishedSavingBackup: (callback: MainEventCallback) =>
|
||||
ipcRenderer.on(MessageToWebApp.FinishedSavingBackup, callback),
|
||||
|
||||
handleWindowBlurred: (callback: MainEventCallback) => ipcRenderer.on(MessageToWebApp.WindowBlurred, callback),
|
||||
|
||||
handleWindowFocused: (callback: MainEventCallback) => ipcRenderer.on(MessageToWebApp.WindowFocused, callback),
|
||||
|
||||
handleInstallComponentComplete: (callback: MainEventCallback) =>
|
||||
ipcRenderer.on(MessageToWebApp.InstallComponentComplete, callback),
|
||||
})
|
||||
})
|
||||
|
||||
function listenForIpcEventsFromMainProcess() {
|
||||
const sendMessageToRenderProcess = (message: string, payload = {}) => {
|
||||
window.postMessage(JSON.stringify({ message, data: payload }), rendererPath)
|
||||
}
|
||||
|
||||
ipcRenderer.on(MessageToWebApp.UpdateAvailable, function (_event, data) {
|
||||
sendMessageToRenderProcess(MessageToWebApp.UpdateAvailable, data)
|
||||
})
|
||||
|
||||
ipcRenderer.on(MessageToWebApp.PerformAutomatedBackup, function (_event, data) {
|
||||
sendMessageToRenderProcess(MessageToWebApp.PerformAutomatedBackup, data)
|
||||
})
|
||||
|
||||
ipcRenderer.on(MessageToWebApp.FinishedSavingBackup, function (_event, data) {
|
||||
sendMessageToRenderProcess(MessageToWebApp.FinishedSavingBackup, data)
|
||||
})
|
||||
|
||||
ipcRenderer.on(MessageToWebApp.WindowBlurred, function (_event, data) {
|
||||
sendMessageToRenderProcess(MessageToWebApp.WindowBlurred, data)
|
||||
})
|
||||
|
||||
ipcRenderer.on(MessageToWebApp.WindowFocused, function (_event, data) {
|
||||
sendMessageToRenderProcess(MessageToWebApp.WindowFocused, data)
|
||||
})
|
||||
|
||||
ipcRenderer.on(MessageToWebApp.InstallComponentComplete, function (_event, data) {
|
||||
sendMessageToRenderProcess(MessageToWebApp.InstallComponentComplete, data)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DesktopClientRequiresWebMethods } from '@web/Application/Device/DesktopSnjsExports'
|
||||
import { StartApplication } from '@web/Application/Device/StartApplication'
|
||||
import { MessageToWebApp } from '../Shared/IpcMessages'
|
||||
import { IpcRendererEvent } from 'electron/renderer'
|
||||
import { CrossProcessBridge } from './CrossProcessBridge'
|
||||
import { DesktopDevice } from './DesktopDevice'
|
||||
|
||||
@@ -23,6 +23,7 @@ declare global {
|
||||
purchaseUrl: string
|
||||
startApplication: StartApplication
|
||||
zip: any
|
||||
electronMainEvents: any
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +42,6 @@ const loadAndStartApplication = async () => {
|
||||
window.device = await createDesktopDevice(remoteBridge)
|
||||
|
||||
window.startApplication(DEFAULT_SYNC_SERVER, window.device, window.enableUnfinishedFeatures, WEBSOCKET_URL)
|
||||
|
||||
listenForMessagesSentFromMainToPreloadToUs(window.device)
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
@@ -134,35 +133,26 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
|
||||
}
|
||||
}
|
||||
|
||||
function listenForMessagesSentFromMainToPreloadToUs(device: DesktopDevice) {
|
||||
window.addEventListener('message', async (event) => {
|
||||
// We don't have access to the full file path.
|
||||
if (event.origin !== 'file://') {
|
||||
return
|
||||
}
|
||||
let payload
|
||||
try {
|
||||
payload = JSON.parse(event.data)
|
||||
} catch (e) {
|
||||
// message doesn't belong to us
|
||||
return
|
||||
}
|
||||
const receiver = window.webClient
|
||||
const message = payload.message
|
||||
const data = payload.data
|
||||
window.electronMainEvents.handleUpdateAvailable(() => {
|
||||
window.webClient.updateAvailable()
|
||||
})
|
||||
|
||||
if (message === MessageToWebApp.WindowBlurred) {
|
||||
receiver.windowLostFocus()
|
||||
} else if (message === MessageToWebApp.WindowFocused) {
|
||||
receiver.windowGainedFocus()
|
||||
} else if (message === MessageToWebApp.InstallComponentComplete) {
|
||||
receiver.onComponentInstallationComplete(data.component, undefined)
|
||||
} else if (message === MessageToWebApp.UpdateAvailable) {
|
||||
receiver.updateAvailable()
|
||||
} else if (message === MessageToWebApp.PerformAutomatedBackup) {
|
||||
void device.downloadBackup()
|
||||
} else if (message === MessageToWebApp.FinishedSavingBackup) {
|
||||
receiver.didFinishBackup(data.success)
|
||||
}
|
||||
})
|
||||
}
|
||||
window.electronMainEvents.handlePerformAutomatedBackup(() => {
|
||||
void window.device.downloadBackup()
|
||||
})
|
||||
|
||||
window.electronMainEvents.handleFinishedSavingBackup((_: IpcRendererEvent, data: any) => {
|
||||
window.webClient.didFinishBackup(data.success)
|
||||
})
|
||||
|
||||
window.electronMainEvents.handleWindowBlurred(() => {
|
||||
window.webClient.windowLostFocus()
|
||||
})
|
||||
|
||||
window.electronMainEvents.handleWindowFocused(() => {
|
||||
window.webClient.windowGainedFocus()
|
||||
})
|
||||
|
||||
window.electronMainEvents.handleInstallComponentComplete((_: IpcRendererEvent, data: any) => {
|
||||
window.webClient.onComponentInstallationComplete(data.component, undefined)
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.23.191",
|
||||
"version": "3.23.209",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,40 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.17.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
# [1.17.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
|
||||
|
||||
## [1.16.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.16.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.16.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [1.15.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.15.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.15.10",
|
||||
"version": "1.17.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.32.0",
|
||||
"@standardnotes/common": "^1.39.0",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "workspace:*",
|
||||
|
||||
@@ -44,3 +44,8 @@ export enum V004Algorithm {
|
||||
EncryptionKeyLength = 256,
|
||||
EncryptionNonceLength = 192,
|
||||
}
|
||||
|
||||
export enum V005Algorithm {
|
||||
AsymmetricEncryptionNonceLength = 192,
|
||||
SymmetricEncryptionNonceLength = 192,
|
||||
}
|
||||
|
||||
@@ -254,21 +254,21 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
encrypted: EncryptedParameters,
|
||||
key: ItemsKeyInterface | SNRootKey,
|
||||
): DecryptedParameters<C> | ErrorDecryptingParameters {
|
||||
const itemKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
const authenticatedData = this.stringToAuthenticatedData(itemKeyComponents.authenticatedData, {
|
||||
const contentKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
const authenticatedData = this.stringToAuthenticatedData(contentKeyComponents.authenticatedData, {
|
||||
u: encrypted.uuid,
|
||||
v: encrypted.version,
|
||||
})
|
||||
|
||||
const useAuthenticatedString = this.authenticatedDataToString(authenticatedData)
|
||||
const itemKey = this.decryptString004(
|
||||
itemKeyComponents.ciphertext,
|
||||
const contentKey = this.decryptString004(
|
||||
contentKeyComponents.ciphertext,
|
||||
key.itemsKey,
|
||||
itemKeyComponents.nonce,
|
||||
contentKeyComponents.nonce,
|
||||
useAuthenticatedString,
|
||||
)
|
||||
|
||||
if (!itemKey) {
|
||||
if (!contentKey) {
|
||||
console.error('Error decrypting itemKey parameters', encrypted)
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
@@ -279,10 +279,11 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
const contentComponents = this.deconstructEncryptedPayloadString(encrypted.content)
|
||||
const content = this.decryptString004(
|
||||
contentComponents.ciphertext,
|
||||
itemKey,
|
||||
contentKey,
|
||||
contentComponents.nonce,
|
||||
useAuthenticatedString,
|
||||
)
|
||||
|
||||
if (!content) {
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
@@ -305,6 +306,7 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
V004Algorithm.ArgonMemLimit,
|
||||
V004Algorithm.ArgonOutputKeyBytes,
|
||||
)
|
||||
|
||||
const partitions = Utils.splitString(derivedKey, 2)
|
||||
const masterKey = partitions[0]
|
||||
const serverPassword = partitions[1]
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { ProtocolOperator005 } from './Operator005'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
|
||||
describe('operator 005', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let operator: ProtocolOperator005
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = {} as jest.Mocked<PureCryptoInterface>
|
||||
crypto.generateRandomKey = jest.fn().mockImplementation(() => {
|
||||
return 'random-string'
|
||||
})
|
||||
crypto.xchacha20Encrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return `<e>${text}<e>`
|
||||
})
|
||||
crypto.xchacha20Decrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return text.split('<e>')[1]
|
||||
})
|
||||
crypto.sodiumCryptoBoxGenerateKeypair = jest.fn().mockImplementation(() => {
|
||||
return { privateKey: 'private-key', publicKey: 'public-key', keyType: 'x25519' }
|
||||
})
|
||||
crypto.sodiumCryptoBoxEasyEncrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return `<e>${text}<e>`
|
||||
})
|
||||
crypto.sodiumCryptoBoxEasyDecrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return text.split('<e>')[1]
|
||||
})
|
||||
|
||||
operator = new ProtocolOperator005(crypto)
|
||||
})
|
||||
|
||||
it('should generateKeyPair', () => {
|
||||
const result = operator.generateKeyPair()
|
||||
|
||||
expect(result).toEqual({ privateKey: 'private-key', publicKey: 'public-key', keyType: 'x25519' })
|
||||
})
|
||||
|
||||
it('should asymmetricEncryptKey', () => {
|
||||
const senderKeypair = operator.generateKeyPair()
|
||||
const recipientKeypair = operator.generateKeyPair()
|
||||
|
||||
const plaintext = 'foo'
|
||||
|
||||
const result = operator.asymmetricEncryptKey(plaintext, senderKeypair.privateKey, recipientKeypair.publicKey)
|
||||
|
||||
expect(result).toEqual(`${'005_KeyAsym'}:random-string:<e>foo<e>`)
|
||||
})
|
||||
|
||||
it('should asymmetricDecryptKey', () => {
|
||||
const senderKeypair = operator.generateKeyPair()
|
||||
const recipientKeypair = operator.generateKeyPair()
|
||||
const plaintext = 'foo'
|
||||
const ciphertext = operator.asymmetricEncryptKey(plaintext, senderKeypair.privateKey, recipientKeypair.publicKey)
|
||||
const decrypted = operator.asymmetricDecryptKey(ciphertext, senderKeypair.publicKey, recipientKeypair.privateKey)
|
||||
|
||||
expect(decrypted).toEqual('foo')
|
||||
})
|
||||
|
||||
it('should symmetricEncryptPrivateKey', () => {
|
||||
const keypair = operator.generateKeyPair()
|
||||
const symmetricKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
const encryptedKey = operator.symmetricEncryptPrivateKey(keypair.privateKey, symmetricKey)
|
||||
|
||||
expect(encryptedKey).toEqual(`${'005_KeySym'}:random-string:<e>${keypair.privateKey}<e>`)
|
||||
})
|
||||
|
||||
it('should symmetricDecryptPrivateKey', () => {
|
||||
const keypair = operator.generateKeyPair()
|
||||
const symmetricKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
const encryptedKey = operator.symmetricEncryptPrivateKey(keypair.privateKey, symmetricKey)
|
||||
const decryptedKey = operator.symmetricDecryptPrivateKey(encryptedKey, symmetricKey)
|
||||
|
||||
expect(decryptedKey).toEqual(keypair.privateKey)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,80 @@
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { Base64String, HexString, PkcKeyPair, Utf8String } from '@standardnotes/sncrypto-common'
|
||||
import { V005Algorithm } from '../../Algorithm'
|
||||
import { SNProtocolOperator004 } from '../004/Operator004'
|
||||
|
||||
const VersionString = '005'
|
||||
const SymmetricCiphertextPrefix = `${VersionString}_KeySym`
|
||||
const AsymmetricCiphertextPrefix = `${VersionString}_KeyAsym`
|
||||
|
||||
export type AsymmetricallyEncryptedKey = Base64String
|
||||
export type SymmetricallyEncryptedPrivateKey = Base64String
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
* @unreleased
|
||||
*/
|
||||
export class ProtocolOperator005 extends SNProtocolOperator004 {
|
||||
public override getEncryptionDisplayName(): string {
|
||||
return 'XChaCha20-Poly1305'
|
||||
}
|
||||
|
||||
override get version(): ProtocolVersion {
|
||||
return VersionString as ProtocolVersion
|
||||
}
|
||||
|
||||
generateKeyPair(): PkcKeyPair {
|
||||
return this.crypto.sodiumCryptoBoxGenerateKeypair()
|
||||
}
|
||||
|
||||
asymmetricEncryptKey(
|
||||
keyToEncrypt: HexString,
|
||||
senderSecretKey: HexString,
|
||||
recipientPublicKey: HexString,
|
||||
): AsymmetricallyEncryptedKey {
|
||||
const nonce = this.crypto.generateRandomKey(V005Algorithm.AsymmetricEncryptionNonceLength)
|
||||
|
||||
const ciphertext = this.crypto.sodiumCryptoBoxEasyEncrypt(keyToEncrypt, nonce, senderSecretKey, recipientPublicKey)
|
||||
|
||||
return [AsymmetricCiphertextPrefix, nonce, ciphertext].join(':')
|
||||
}
|
||||
|
||||
asymmetricDecryptKey(
|
||||
keyToDecrypt: AsymmetricallyEncryptedKey,
|
||||
senderPublicKey: HexString,
|
||||
recipientSecretKey: HexString,
|
||||
): Utf8String {
|
||||
const components = keyToDecrypt.split(':')
|
||||
|
||||
const nonce = components[1]
|
||||
|
||||
return this.crypto.sodiumCryptoBoxEasyDecrypt(keyToDecrypt, nonce, senderPublicKey, recipientSecretKey)
|
||||
}
|
||||
|
||||
symmetricEncryptPrivateKey(privateKey: HexString, symmetricKey: HexString): SymmetricallyEncryptedPrivateKey {
|
||||
if (symmetricKey.length !== 64) {
|
||||
throw new Error('Symmetric key length must be 256 bits')
|
||||
}
|
||||
|
||||
const nonce = this.crypto.generateRandomKey(V005Algorithm.SymmetricEncryptionNonceLength)
|
||||
|
||||
const encryptedKey = this.crypto.xchacha20Encrypt(privateKey, nonce, symmetricKey)
|
||||
|
||||
return [SymmetricCiphertextPrefix, nonce, encryptedKey].join(':')
|
||||
}
|
||||
|
||||
symmetricDecryptPrivateKey(
|
||||
encryptedPrivateKey: SymmetricallyEncryptedPrivateKey,
|
||||
symmetricKey: HexString,
|
||||
): HexString | null {
|
||||
if (symmetricKey.length !== 64) {
|
||||
throw new Error('Symmetric key length must be 256 bits')
|
||||
}
|
||||
|
||||
const components = encryptedPrivateKey.split(':')
|
||||
|
||||
const nonce = components[1]
|
||||
|
||||
return this.crypto.xchacha20Decrypt(encryptedPrivateKey, nonce, symmetricKey)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@ import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit'
|
||||
import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit'
|
||||
|
||||
export interface EncryptionProvider {
|
||||
export interface EncryptionProviderInterface {
|
||||
encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface>
|
||||
|
||||
encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]>
|
||||
@@ -14,11 +14,12 @@ export * from './Operator/001/Operator001'
|
||||
export * from './Operator/002/Operator002'
|
||||
export * from './Operator/003/Operator003'
|
||||
export * from './Operator/004/Operator004'
|
||||
export * from './Operator/005/Operator005'
|
||||
export * from './Operator/Functions'
|
||||
export * from './Operator/Operator'
|
||||
export * from './Operator/OperatorManager'
|
||||
export * from './Operator/OperatorWrapper'
|
||||
export * from './Service/Encryption/EncryptionProvider'
|
||||
export * from './Service/Encryption/EncryptionProviderInterface'
|
||||
export * from './Service/Functions'
|
||||
export * from './Service/RootKey/KeyMode'
|
||||
export * from './Service/RootKey/RootKeyServiceEvent'
|
||||
|
||||
@@ -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.53.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
|
||||
|
||||
## [1.52.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dark mode not working in editors ([#1773](https://github.com/standardnotes/app/issues/1773)) ([27d2c95](https://github.com/standardnotes/app/commit/27d2c95b5b82645bf6c0372979f05236c83a10f6))
|
||||
|
||||
## [1.52.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
|
||||
|
||||
## [1.52.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/features",
|
||||
"version": "1.52.2",
|
||||
"version": "1.53.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/auth": "^3.19.4",
|
||||
"@standardnotes/common": "^1.32.0",
|
||||
"@standardnotes/common": "^1.39.0",
|
||||
"@standardnotes/security": "^1.2.0",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ export enum FeatureIdentifier {
|
||||
|
||||
AutobiographyTheme = 'org.standardnotes.theme-autobiography',
|
||||
DynamicTheme = 'org.standardnotes.theme-dynamic',
|
||||
FocusedTheme = 'org.standardnotes.theme-focus',
|
||||
DarkTheme = 'org.standardnotes.theme-focus',
|
||||
FocusMode = 'org.standardnotes.focus-mode',
|
||||
FuturaTheme = 'org.standardnotes.theme-futura',
|
||||
MidnightTheme = 'org.standardnotes.theme-midnight',
|
||||
|
||||
@@ -68,10 +68,10 @@ export function themes(): ThemeFeatureDescription[] {
|
||||
},
|
||||
})
|
||||
|
||||
const focus: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
const dark: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Focus',
|
||||
identifier: FeatureIdentifier.FocusedTheme,
|
||||
name: 'Dark',
|
||||
identifier: FeatureIdentifier.DarkTheme,
|
||||
permission_name: PermissionName.FocusedTheme,
|
||||
description: 'For when you need to go in.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/focus-with-mobile.jpg',
|
||||
@@ -109,5 +109,5 @@ export function themes(): ThemeFeatureDescription[] {
|
||||
description: 'A smart theme that minimizes the tags and notes panels when they are not in use.',
|
||||
})
|
||||
|
||||
return [midnight, futura, solarizedDark, autobiography, focus, titanium, dynamic]
|
||||
return [midnight, futura, solarizedDark, autobiography, dark, titanium, dynamic]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.24.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
# [1.24.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
|
||||
|
||||
## [1.23.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
|
||||
|
||||
## [1.23.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.23.10",
|
||||
"version": "1.24.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -31,7 +31,7 @@
|
||||
"ts-node": "^10.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.32.0",
|
||||
"@standardnotes/common": "^1.39.0",
|
||||
"@standardnotes/files": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
"@types/wicg-file-system-access": "^2020.9.5",
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
|
||||
|
||||
## [1.10.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
|
||||
|
||||
## [1.10.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.10.10",
|
||||
"version": "1.11.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"ts-jest": "^28.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.32.0",
|
||||
"@standardnotes/common": "^1.39.0",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
|
||||
@@ -3,6 +3,106 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.42.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.42.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.42.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* item linking ([#1779](https://github.com/standardnotes/app/issues/1779)) ([e3f2842](https://github.com/standardnotes/app/commit/e3f28421ff042c635ad2ae645c102c27e3e3f9c7))
|
||||
|
||||
## [3.41.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** issue with black screen on iOS ([#1777](https://github.com/standardnotes/app/issues/1777)) ([9fc77d8](https://github.com/standardnotes/app/commit/9fc77d861e501d63ee97875bba2ddda51d14fd0b))
|
||||
|
||||
## [3.41.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-09)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-08)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.41.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.41.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** disable landscape for iPhone ([a365c17](https://github.com/standardnotes/app/commit/a365c17c46f38e6d8cacade47542fd4ec625bc0d))
|
||||
|
||||
### Features
|
||||
|
||||
* **mobile:** delete account option in settings ([#1768](https://github.com/standardnotes/app/issues/1768)) ([7d21046](https://github.com/standardnotes/app/commit/7d21046b5189eb14a5957bda6fa788d743d8ebb6))
|
||||
|
||||
## [3.40.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.40.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.40.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [3.39.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.39.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add android platform check for status bar color sync ([1602d81](https://github.com/standardnotes/app/commit/1602d8157f9e61c6ad842bcce28f3aaf6a8f7154))
|
||||
|
||||
## [3.39.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.39.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** passcode timing options ([#1744](https://github.com/standardnotes/app/issues/1744)) ([6c26b96](https://github.com/standardnotes/app/commit/6c26b96cdce0d69a4a459104709880850a401a92))
|
||||
|
||||
## [3.39.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -302,7 +302,7 @@ PODS:
|
||||
- React
|
||||
- react-native-version-info (1.1.1):
|
||||
- React-Core
|
||||
- react-native-webview (11.17.2):
|
||||
- react-native-webview (11.23.1):
|
||||
- React-Core
|
||||
- React-perflogger (0.67.4)
|
||||
- React-RCTActionSheet (0.67.4):
|
||||
@@ -719,7 +719,7 @@ SPEC CHECKSUMS:
|
||||
react-native-sodium-jsi: c8901320767d00385f9111bc95ba25aaa9a29890
|
||||
react-native-static-server: 880d9b697ef68722e6fa06ae06e6b843e906ed0b
|
||||
react-native-version-info: a106f23009ac0db4ee00de39574eb546682579b9
|
||||
react-native-webview: 380c1a03ec94b7ed764dac8db1e7c9952d08c93a
|
||||
react-native-webview: d33e2db8925d090871ffeb232dfa50cb3a727581
|
||||
React-perflogger: 0afaf2f01a47fd0fc368a93bfbb5bd3b26db6e7f
|
||||
React-RCTActionSheet: 59f35c4029e0b532fc42114241a06e170b7431a2
|
||||
React-RCTAnimation: aae4f4bed122e78bdab72f7118d291d70a932ce2
|
||||
|
||||
@@ -138,6 +138,10 @@
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~iphone</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>supportsAlternateIcons</key>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.39.8",
|
||||
"version": "3.42.2",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
@@ -80,7 +80,7 @@
|
||||
"react-native-url-polyfill": "^1.3.0",
|
||||
"react-native-vector-icons": "^9.1.0",
|
||||
"react-native-version-info": "^1.1.1",
|
||||
"react-native-webview": "11.17.2",
|
||||
"react-native-webview": "11.23.1",
|
||||
"react-native-zip-archive": "^6.0.6",
|
||||
"react-navigation-header-buttons": "^9.0.1",
|
||||
"styled-components": "5.3.5"
|
||||
|
||||
@@ -68,7 +68,7 @@ export const useFiles = ({ note }: Props) => {
|
||||
const filesService = application.getFilesService()
|
||||
|
||||
const reloadAttachedFiles = useCallback(() => {
|
||||
setAttachedFiles(application.items.getFilesForNote(note).sort(filesService.sortByName))
|
||||
setAttachedFiles(application.items.getSortedFilesForItem(note).sort(filesService.sortByName))
|
||||
}, [application.items, filesService.sortByName, note])
|
||||
|
||||
const reloadAllFiles = useCallback(() => {
|
||||
|
||||
@@ -483,7 +483,7 @@ export class ApplicationState extends ApplicationService {
|
||||
return
|
||||
}
|
||||
|
||||
const hasBiometrics = this.application.hasBiometrics()
|
||||
const hasBiometrics = this.application.protections.hasBiometricsEnabled()
|
||||
const hasPasscode = this.application.hasPasscode()
|
||||
const passcodeLockImmediately = hasPasscode && this.passcodeTiming === MobileUnlockTiming.Immediately
|
||||
const biometricsLockImmediately =
|
||||
@@ -571,19 +571,19 @@ export class ApplicationState extends ApplicationService {
|
||||
}
|
||||
|
||||
private async getScreenshotPrivacyEnabled(): Promise<boolean> {
|
||||
return this.application.getMobileScreenshotPrivacyEnabled()
|
||||
return this.application.protections.getMobileScreenshotPrivacyEnabled()
|
||||
}
|
||||
|
||||
private async getPasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.application.getMobilePasscodeTiming()
|
||||
return this.application.protections.getMobilePasscodeTiming()
|
||||
}
|
||||
|
||||
private async getBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.application.getMobileBiometricsTiming()
|
||||
return this.application.protections.getMobileBiometricsTiming()
|
||||
}
|
||||
|
||||
public async setScreenshotPrivacyEnabled(enabled: boolean) {
|
||||
await this.application.setMobileScreenshotPrivacyEnabled(enabled)
|
||||
await this.application.protections.setMobileScreenshotPrivacyEnabled(enabled)
|
||||
this.screenshotPrivacyEnabled = enabled
|
||||
await (this.application.deviceInterface as MobileDevice).setAndroidScreenshotPrivacy(enabled)
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ export class MobileDevice implements MobileDeviceInterface {
|
||||
}
|
||||
|
||||
reloadStatusBarStyle(animated = true) {
|
||||
if (this.statusBarBgColor) {
|
||||
if (this.statusBarBgColor && Platform.OS === 'android') {
|
||||
StatusBar.setBackgroundColor(this.statusBarBgColor, animated)
|
||||
}
|
||||
StatusBar.setBarStyle(this.isDarkMode ? 'light-content' : 'dark-content', animated)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Base64String,
|
||||
HexString,
|
||||
PkcKeyPair,
|
||||
PureCryptoInterface,
|
||||
SodiumConstant,
|
||||
StreamDecryptorResult,
|
||||
@@ -129,6 +130,28 @@ export class SNReactNativeCrypto implements PureCryptoInterface {
|
||||
}
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxEasyEncrypt(
|
||||
_message: Utf8String,
|
||||
_nonce: HexString,
|
||||
_senderSecretKey: HexString,
|
||||
_recipientPublicKey: HexString,
|
||||
): Base64String {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxEasyDecrypt(
|
||||
_ciphertext: Base64String,
|
||||
_nonce: HexString,
|
||||
_senderPublicKey: HexString,
|
||||
_recipientSecretKey: HexString,
|
||||
): Utf8String {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxGenerateKeypair(): PkcKeyPair {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
public generateUUID() {
|
||||
const randomBuf = Sodium.randombytes_buf(16)
|
||||
const tempBuf = new Uint8Array(randomBuf.length / 2)
|
||||
|
||||
@@ -204,6 +204,12 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
||||
onError={(err) => console.error('An error has occurred', err)}
|
||||
onHttpError={() => console.error('An HTTP error occurred')}
|
||||
onMessage={onMessage}
|
||||
onContentProcessDidTerminate={() => {
|
||||
webViewRef.current?.reload()
|
||||
}}
|
||||
onRenderProcessGone={() => {
|
||||
webViewRef.current?.reload()
|
||||
}}
|
||||
allowFileAccess={true}
|
||||
allowUniversalAccessFromFileURLs={true}
|
||||
injectedJavaScriptBeforeContentLoaded={injectedJS}
|
||||
|
||||
@@ -197,7 +197,7 @@ export const Authenticate = ({
|
||||
state: AuthenticationValueStateType.Pending,
|
||||
})
|
||||
|
||||
if (await application?.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (application?.protections.getMobileScreenshotPrivacyEnabled()) {
|
||||
hide()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { ApplicationContext } from '@Root/ApplicationContext'
|
||||
import { ButtonCell } from '@Root/Components/ButtonCell'
|
||||
import { SectionHeader } from '@Root/Components/SectionHeader'
|
||||
import { TableSection } from '@Root/Components/TableSection'
|
||||
import { ButtonType } from '@standardnotes/snjs'
|
||||
import React, { useContext, useState } from 'react'
|
||||
import { RegularView } from './AuthSection.styled'
|
||||
|
||||
export const DeleteSection = () => {
|
||||
const application = useContext(ApplicationContext)
|
||||
const [deleting, setDeleting] = useState(false)
|
||||
|
||||
const deleteAccount = async () => {
|
||||
const message =
|
||||
"This action is irreversible. After deletion completes, you will be signed out on all devices, and this application will exit. If you have an active paid subscription, cancel the subscription first. Otherwise, if you'd like to keep the subscription, you can re-register with the same email after deletion, and your subscription will be linked back up with your account."
|
||||
const confirmed = await application!.alertService.confirm(
|
||||
message,
|
||||
'Are you sure?',
|
||||
'Delete Account',
|
||||
ButtonType.Danger,
|
||||
)
|
||||
|
||||
if (!confirmed) {
|
||||
return
|
||||
}
|
||||
|
||||
setDeleting(true)
|
||||
|
||||
const result = await application!.user.deleteAccount()
|
||||
if (result.error) {
|
||||
void application!.alertService.alert('An error occurred while deleting your account. Please try again.')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<RegularView>
|
||||
<SectionHeader />
|
||||
<TableSection>
|
||||
<ButtonCell
|
||||
first
|
||||
last
|
||||
important
|
||||
leftAligned={true}
|
||||
title={deleting ? 'Deleting...' : 'Delete Account'}
|
||||
onPress={deleteAccount}
|
||||
></ButtonCell>
|
||||
</TableSection>
|
||||
</RegularView>
|
||||
)
|
||||
}
|
||||
@@ -31,21 +31,23 @@ export const SecuritySection = (props: Props) => {
|
||||
const [hasBiometrics, setHasBiometrics] = useState(false)
|
||||
const [supportsBiometrics, setSupportsBiometrics] = useState(false)
|
||||
const [biometricsTimingOptions, setBiometricsTimingOptions] = useState(() =>
|
||||
application!.getBiometricsTimingOptions(),
|
||||
application!.protections.getMobileBiometricsTimingOptions(),
|
||||
)
|
||||
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() =>
|
||||
application!.protections.getMobilePasscodeTimingOptions(),
|
||||
)
|
||||
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() => application!.getPasscodeTimingOptions())
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
const getHasScreenshotPrivacy = async () => {
|
||||
const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled()
|
||||
const hasScreenshotPrivacyEnabled = application?.protections.getMobileScreenshotPrivacyEnabled()
|
||||
if (mounted) {
|
||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||
}
|
||||
}
|
||||
void getHasScreenshotPrivacy()
|
||||
const getHasBiometrics = async () => {
|
||||
const appHasBiometrics = application!.hasBiometrics()
|
||||
const appHasBiometrics = application!.protections.hasBiometricsEnabled()
|
||||
if (mounted) {
|
||||
setHasBiometrics(appHasBiometrics)
|
||||
}
|
||||
@@ -68,7 +70,7 @@ export const SecuritySection = (props: Props) => {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (props.hasPasscode) {
|
||||
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
|
||||
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
|
||||
}
|
||||
}, [application, props.hasPasscode]),
|
||||
)
|
||||
@@ -126,12 +128,12 @@ export const SecuritySection = (props: Props) => {
|
||||
|
||||
const setBiometricsTiming = async (timing: MobileUnlockTiming) => {
|
||||
await application?.getAppState().setBiometricsTiming(timing)
|
||||
setBiometricsTimingOptions(() => application!.getBiometricsTimingOptions())
|
||||
setBiometricsTimingOptions(() => application!.protections.getMobileBiometricsTimingOptions())
|
||||
}
|
||||
|
||||
const setPasscodeTiming = async (timing: MobileUnlockTiming) => {
|
||||
await application?.getAppState().setPasscodeTiming(timing)
|
||||
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
|
||||
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
|
||||
}
|
||||
|
||||
const onScreenshotPrivacyPress = async () => {
|
||||
@@ -153,14 +155,14 @@ export const SecuritySection = (props: Props) => {
|
||||
void disableAuthentication('biometrics')
|
||||
} else {
|
||||
setHasBiometrics(true)
|
||||
await application?.enableBiometrics()
|
||||
await application?.protections.enableBiometrics()
|
||||
await setBiometricsTiming(MobileUnlockTiming.OnQuit)
|
||||
props.updateProtectionsAvailable()
|
||||
}
|
||||
}
|
||||
|
||||
const disableBiometrics = useCallback(async () => {
|
||||
if (await application?.disableBiometrics()) {
|
||||
if (await application?.protections.disableBiometrics()) {
|
||||
setHasBiometrics(false)
|
||||
props.updateProtectionsAvailable()
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export const WorkspacesSection = () => {
|
||||
}
|
||||
|
||||
await appGroup.unloadCurrentAndCreateNewDescriptor()
|
||||
}, [WorkspaceAction.AddAnother, appGroup, applicationDescriptors, getWorkspaceActionConfirmation])
|
||||
}, [WorkspaceAction.AddAnother, appGroup, getWorkspaceActionConfirmation])
|
||||
|
||||
const signOutAllWorkspaces = useCallback(async () => {
|
||||
try {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ApplicationEvent, FeatureIdentifier, FeatureStatus } from '@standardnot
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { AuthSection } from './Sections/AuthSection'
|
||||
import { CompanySection } from './Sections/CompanySection'
|
||||
import { DeleteSection } from './Sections/DeleteSection'
|
||||
import { EncryptionSection } from './Sections/EncryptionSection'
|
||||
import { NewMobileSection } from './Sections/NewMobilePreview'
|
||||
import { OptionsSection } from './Sections/OptionsSection'
|
||||
@@ -68,6 +69,7 @@ export const Settings = (props: Props) => {
|
||||
<ProtectionsSection title="Protections" protectionsAvailable={protectionsAvailable} />
|
||||
<EncryptionSection encryptionAvailable={!!encryptionAvailable} title={'Encryption Status'} />
|
||||
<CompanySection title="Standard Notes" />
|
||||
{application.hasAccount() && <DeleteSection />}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
setAttachedFilesLength(0)
|
||||
return
|
||||
}
|
||||
setAttachedFilesLength(application.items.getFilesForNote(note).length)
|
||||
setAttachedFilesLength(application.items.getSortedFilesForItem(note).length)
|
||||
}, [application, note])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -147,7 +147,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
|
||||
return
|
||||
}
|
||||
const removeFilesObserver = application.streamItems(ContentType.File, () => {
|
||||
setAttachedFilesLength(application.items.getFilesForNote(note).length)
|
||||
setAttachedFilesLength(application.items.getSortedFilesForItem(note).length)
|
||||
})
|
||||
return () => {
|
||||
removeFilesObserver()
|
||||
|
||||
@@ -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.27.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* item linking ([#1779](https://github.com/standardnotes/app/issues/1779)) ([e3f2842](https://github.com/standardnotes/app/commit/e3f28421ff042c635ad2ae645c102c27e3e3f9c7))
|
||||
|
||||
# [1.26.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
|
||||
|
||||
# [1.25.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add listing workspaces ([8376381](https://github.com/standardnotes/app/commit/837638198112df24c30e53803a7ddf5f841975b0))
|
||||
|
||||
## [1.24.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [1.24.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
|
||||
|
||||
# [1.24.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
# [1.23.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
|
||||
|
||||
## [1.22.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.22.1",
|
||||
"version": "1.27.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -31,7 +31,7 @@
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.32.0",
|
||||
"@standardnotes/common": "^1.39.0",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
|
||||
@@ -24,11 +24,11 @@ export class DecryptedItem<C extends ItemContent = ItemContent>
|
||||
|
||||
constructor(payload: DecryptedPayloadInterface<C>) {
|
||||
super(payload)
|
||||
this.conflictOf = payload.content.conflict_of
|
||||
|
||||
const userModVal = this.getAppDomainValueWithDefault(AppDataField.UserModifiedDate, this.serverUpdatedAt || 0)
|
||||
|
||||
this.userModifiedDate = new Date(userModVal as number | Date)
|
||||
|
||||
this.conflictOf = payload.content.conflict_of
|
||||
this.updatedAtString = dateToLocalizedString(this.userModifiedDate)
|
||||
this.protected = useBoolean(this.payload.content.protected, false)
|
||||
this.trashed = useBoolean(this.payload.content.trashed, false)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ContenteReferenceType } from './ContenteReferenceType'
|
||||
import { ContentReferenceType } from './ContenteReferenceType'
|
||||
|
||||
export interface AnonymousReference {
|
||||
uuid: string
|
||||
content_type: ContentType
|
||||
reference_type: ContenteReferenceType
|
||||
reference_type: ContentReferenceType
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export enum ContenteReferenceType {
|
||||
export enum ContentReferenceType {
|
||||
TagToParentTag = 'TagToParentTag',
|
||||
FileToNote = 'FileToNote',
|
||||
TagToFile = 'TagToFile',
|
||||
FileToNote = 'FileToNote',
|
||||
FileToFile = 'FileToFile',
|
||||
NoteToNote = 'NoteToNote',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { AnonymousReference } from './AnonymousReference'
|
||||
import { ContentReferenceType } from './ContenteReferenceType'
|
||||
|
||||
export interface FileToFileReference extends AnonymousReference {
|
||||
content_type: ContentType.File
|
||||
reference_type: ContentReferenceType.FileToFile
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { AnonymousReference } from './AnonymousReference'
|
||||
import { ContenteReferenceType } from './ContenteReferenceType'
|
||||
import { ContentReferenceType } from './ContenteReferenceType'
|
||||
|
||||
export interface FileToNoteReference extends AnonymousReference {
|
||||
content_type: ContentType.Note
|
||||
reference_type: ContenteReferenceType.FileToNote
|
||||
reference_type: ContentReferenceType.FileToNote
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ItemInterface } from '../Item/Interfaces/ItemInterface'
|
||||
import { ContenteReferenceType } from './ContenteReferenceType'
|
||||
import { ContentReferenceType } from './ContenteReferenceType'
|
||||
import { ContentReference } from './ContentReference'
|
||||
import { LegacyAnonymousReference } from './LegacyAnonymousReference'
|
||||
import { LegacyTagToNoteReference } from './LegacyTagToNoteReference'
|
||||
@@ -26,5 +26,5 @@ export const isLegacyTagToNoteReference = (
|
||||
}
|
||||
|
||||
export const isTagToParentTagReference = (x: ContentReference): x is TagToParentTagReference => {
|
||||
return isReference(x) && x.reference_type === ContenteReferenceType.TagToParentTag
|
||||
return isReference(x) && x.reference_type === ContentReferenceType.TagToParentTag
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { AnonymousReference } from './AnonymousReference'
|
||||
import { ContentReferenceType } from './ContenteReferenceType'
|
||||
|
||||
export interface NoteToNoteReference extends AnonymousReference {
|
||||
content_type: ContentType.Note
|
||||
reference_type: ContentReferenceType.NoteToNote
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { AnonymousReference } from './AnonymousReference'
|
||||
import { ContenteReferenceType } from './ContenteReferenceType'
|
||||
import { ContentReferenceType } from './ContenteReferenceType'
|
||||
|
||||
export interface TagToFileReference extends AnonymousReference {
|
||||
content_type: ContentType.File
|
||||
reference_type: ContenteReferenceType.TagToFile
|
||||
reference_type: ContentReferenceType.TagToFile
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { AnonymousReference } from './AnonymousReference'
|
||||
import { ContenteReferenceType } from './ContenteReferenceType'
|
||||
import { ContentReferenceType } from './ContenteReferenceType'
|
||||
|
||||
export interface TagToParentTagReference extends AnonymousReference {
|
||||
content_type: ContentType.Tag
|
||||
reference_type: ContenteReferenceType.TagToParentTag
|
||||
reference_type: ContentReferenceType.TagToParentTag
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { WorkspaceType } from '@standardnotes/common'
|
||||
|
||||
export type Workspace = {
|
||||
uuid: string
|
||||
type: WorkspaceType
|
||||
name: string | null
|
||||
keyRotationIndex: number
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Uuid, WorkspaceAccessLevel, WorkspaceUserStatus } from '@standardnotes/common'
|
||||
|
||||
export type WorkspaceUser = {
|
||||
uuid: Uuid
|
||||
accessLevel: WorkspaceAccessLevel
|
||||
userUuid: Uuid
|
||||
userDisplayName: string | null
|
||||
workspaceUuid: Uuid
|
||||
encryptedWorkspaceKey: string | null
|
||||
publicKey: string | null
|
||||
encryptedPrivateKey: string | null
|
||||
status: WorkspaceUserStatus
|
||||
keyRotationIndex: number
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
+20
-7
@@ -1,10 +1,10 @@
|
||||
import { NoteContent } from './../../../Syncable/Note/NoteContent'
|
||||
import { NoteContent } from '../../../Syncable/Note/NoteContent'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { DecryptedItem, EncryptedItem } from '../../../Abstract/Item'
|
||||
import { DecryptedPayload, EncryptedPayload, PayloadTimestampDefaults } from '../../../Abstract/Payload'
|
||||
import { ItemCollection } from './ItemCollection'
|
||||
import { FillItemContent } from '../../../Abstract/Content/ItemContent'
|
||||
import { TagNotesIndex } from './TagNotesIndex'
|
||||
import { TagItemsIndex } from './TagItemsIndex'
|
||||
import { ItemDelta } from '../../Index/ItemDelta'
|
||||
import { AnyItemInterface } from '../../../Abstract/Item/Interfaces/UnionTypes'
|
||||
|
||||
@@ -24,10 +24,10 @@ describe('tag notes index', () => {
|
||||
return new EncryptedItem(payload)
|
||||
}
|
||||
|
||||
const createDecryptedItem = (uuid?: string) => {
|
||||
const createDecryptedItem = (uuid?: string, content_type = ContentType.Note) => {
|
||||
const payload = new DecryptedPayload({
|
||||
uuid: uuid || String(Math.random()),
|
||||
content_type: ContentType.Note,
|
||||
content_type,
|
||||
content: FillItemContent<NoteContent>({
|
||||
title: 'foo',
|
||||
}),
|
||||
@@ -46,20 +46,33 @@ describe('tag notes index', () => {
|
||||
}
|
||||
}
|
||||
|
||||
it('should count both notes and files', () => {
|
||||
const collection = new ItemCollection()
|
||||
const index = new TagItemsIndex(collection)
|
||||
|
||||
const decryptedNote = createDecryptedItem('note')
|
||||
const decryptedFile = createDecryptedItem('file')
|
||||
collection.set([decryptedNote, decryptedFile])
|
||||
index.onChange(createChangeDelta(decryptedNote))
|
||||
index.onChange(createChangeDelta(decryptedFile))
|
||||
|
||||
expect(index.allCountableItemsCount()).toEqual(2)
|
||||
})
|
||||
|
||||
it('should decrement count after decrypted note becomes errored', () => {
|
||||
const collection = new ItemCollection()
|
||||
const index = new TagNotesIndex(collection)
|
||||
const index = new TagItemsIndex(collection)
|
||||
|
||||
const decryptedItem = createDecryptedItem()
|
||||
collection.set(decryptedItem)
|
||||
index.onChange(createChangeDelta(decryptedItem))
|
||||
|
||||
expect(index.allCountableNotesCount()).toEqual(1)
|
||||
expect(index.allCountableItemsCount()).toEqual(1)
|
||||
|
||||
const encryptedItem = createEncryptedItem(decryptedItem.uuid)
|
||||
collection.set(encryptedItem)
|
||||
index.onChange(createChangeDelta(encryptedItem))
|
||||
|
||||
expect(index.allCountableNotesCount()).toEqual(0)
|
||||
expect(index.allCountableItemsCount()).toEqual(0)
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user