mirror of
https://github.com/standardnotes/app
synced 2026-09-13 09:46:02 -04:00
Compare commits
50
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac4231d8eb | ||
|
|
3281ac9d37 | ||
|
|
f2d089ab24 | ||
|
|
a91ab4abbf | ||
|
|
0af41cbcad | ||
|
|
1adb21a06e | ||
|
|
412b192b85 | ||
|
|
8ad9b8ae2a | ||
|
|
c52d8e7309 | ||
|
|
406abb4929 | ||
|
|
814a156e1b | ||
|
|
e67f694c85 | ||
|
|
a77535456c | ||
|
|
e698b1c990 | ||
|
|
ab2e88a710 | ||
|
|
68db05581b | ||
|
|
7acf23033d | ||
|
|
78b87672de | ||
|
|
2b0df30ca2 | ||
|
|
58cf0e542f | ||
|
|
edead7e251 | ||
|
|
166b4f3a5d | ||
|
|
1566c862a5 | ||
|
|
7df9873871 | ||
|
|
071b345e93 | ||
|
|
072e9f803b | ||
|
|
8ceb6938b3 | ||
|
|
e7d805b91a | ||
|
|
eba1289ab9 | ||
|
|
7fb33c400c | ||
|
|
a56e1f5398 | ||
|
|
bf115b5eca | ||
|
|
5932eb814c | ||
|
|
e1f1be96cb | ||
|
|
e86dff4e58 | ||
|
|
bac665fe1e | ||
|
|
10cddeee25 | ||
|
|
bd7b5a2b99 | ||
|
|
f64a9b66ca | ||
|
|
2662f5abbb | ||
|
|
526c70f623 | ||
|
|
af8aca612a | ||
|
|
a890f44cbe | ||
|
|
415757cc30 | ||
|
|
2a91dd0636 | ||
|
|
953870beaf | ||
|
|
2091e4c6aa | ||
|
|
1eb3e35f99 | ||
|
|
cb7f9d33ff | ||
|
|
88ef5887a9 |
@@ -6,6 +6,3 @@ DEFAULT_SYNC_SERVER=https://api.standardnotes.com
|
||||
DASHBOARD_URL=http://standardnotes.com/dashboard
|
||||
PLANS_URL=https://standardnotes.com/plans
|
||||
PURCHASE_URL=https://standardnotes.com/purchase
|
||||
|
||||
# Used by Rails internals and not Standard Notes related
|
||||
SECRET_KEY_BASE=<enter a secret string here>
|
||||
@@ -154,13 +154,14 @@ jobs:
|
||||
|
||||
Publish:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
if: ${{ always() }}
|
||||
needs: [Windows, Mac, Linux]
|
||||
defaults:
|
||||
run:
|
||||
working-directory: packages/desktop
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -209,6 +210,5 @@ jobs:
|
||||
continue-on-error: true
|
||||
run: |
|
||||
sudo snap install snapcraft --classic
|
||||
echo "${{ secrets.SNAPCRAFT_LOGIN_FILE }}" >> snapauth.txt
|
||||
snapcraft login --with=snapauth.txt
|
||||
snapcraft login
|
||||
snapcraft upload dist/standard-notes-${{ env.APP_VERSION }}-linux-amd64.snap --release stable,candidate,beta,edge
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.26.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixes issue where some accounts were not correctly loading subscription info ([#2361](https://github.com/standardnotes/app/issues/2361)) ([eba1289](https://github.com/standardnotes/app/commit/eba1289ab9da10a545b58572bcf0dde8c050de61))
|
||||
|
||||
## [1.26.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.26.28",
|
||||
"version": "1.26.30",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -39,7 +39,6 @@
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/security": "^1.7.6",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
}
|
||||
|
||||
@@ -65,13 +65,26 @@ export class FetchRequestHandler implements RequestHandlerInterface {
|
||||
}
|
||||
|
||||
private async runRequest<T>(request: Request, body?: string | Uint8Array | undefined): Promise<HttpResponse<T>> {
|
||||
const fetchResponse = await fetch(request, {
|
||||
body,
|
||||
})
|
||||
try {
|
||||
const fetchResponse = await fetch(request, {
|
||||
body,
|
||||
})
|
||||
|
||||
const response = await this.handleFetchResponse<T>(fetchResponse)
|
||||
const response = await this.handleFetchResponse<T>(fetchResponse)
|
||||
|
||||
return response
|
||||
return response
|
||||
} catch (error) {
|
||||
return {
|
||||
status: HttpStatusCode.InternalServerError,
|
||||
headers: new Map<string, string | null>(),
|
||||
data: {
|
||||
error: {
|
||||
message:
|
||||
'message' in (error as { message: string }) ? (error as { message: string }).message : 'Unknown error',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async handleFetchResponse<T>(fetchResponse: Response): Promise<HttpResponse<T>> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { joinPaths, sleep } from '@standardnotes/utils'
|
||||
import { Environment } from '@standardnotes/models'
|
||||
import { Session, SessionToken } from '@standardnotes/domain-core'
|
||||
import { LegacySession, Session, SessionToken } from '@standardnotes/domain-core'
|
||||
import {
|
||||
HttpStatusCode,
|
||||
HttpRequestParams,
|
||||
@@ -18,7 +18,7 @@ import { FetchRequestHandler } from './FetchRequestHandler'
|
||||
import { RequestHandlerInterface } from './RequestHandlerInterface'
|
||||
|
||||
export class HttpService implements HttpServiceInterface {
|
||||
private session: Session | null
|
||||
private session?: Session | LegacySession
|
||||
private __latencySimulatorMs?: number
|
||||
private declare host: string
|
||||
|
||||
@@ -29,7 +29,6 @@ export class HttpService implements HttpServiceInterface {
|
||||
private requestHandler: RequestHandlerInterface
|
||||
|
||||
constructor(private environment: Environment, private appVersion: string, private snjsVersion: string) {
|
||||
this.session = null
|
||||
this.requestHandler = new FetchRequestHandler(this.snjsVersion, this.appVersion, this.environment)
|
||||
}
|
||||
|
||||
@@ -42,12 +41,12 @@ export class HttpService implements HttpServiceInterface {
|
||||
}
|
||||
|
||||
public deinit(): void {
|
||||
this.session = null
|
||||
;(this.session as unknown) = undefined
|
||||
;(this.updateMetaCallback as unknown) = undefined
|
||||
;(this.refreshSessionCallback as unknown) = undefined
|
||||
}
|
||||
|
||||
setSession(session: Session): void {
|
||||
setSession(session: Session | LegacySession): void {
|
||||
this.session = session
|
||||
}
|
||||
|
||||
@@ -59,6 +58,18 @@ export class HttpService implements HttpServiceInterface {
|
||||
return this.host
|
||||
}
|
||||
|
||||
private getSessionAccessToken(): string | undefined {
|
||||
if (!this.session) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (this.session instanceof Session) {
|
||||
return this.session.accessToken.value
|
||||
} else {
|
||||
return this.session.accessToken
|
||||
}
|
||||
}
|
||||
|
||||
async get<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>> {
|
||||
if (!this.host) {
|
||||
throw new Error('Attempting to make network request before host is set')
|
||||
@@ -68,7 +79,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
url: joinPaths(this.host, path),
|
||||
params,
|
||||
verb: HttpVerb.Get,
|
||||
authentication: authentication ?? this.session?.accessToken.value,
|
||||
authentication: authentication ?? this.getSessionAccessToken(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -90,7 +101,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
url: joinPaths(this.host, path),
|
||||
params,
|
||||
verb: HttpVerb.Post,
|
||||
authentication: authentication ?? this.session?.accessToken.value,
|
||||
authentication: authentication ?? this.getSessionAccessToken(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,7 +110,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
url: joinPaths(this.host, path),
|
||||
params,
|
||||
verb: HttpVerb.Put,
|
||||
authentication: authentication ?? this.session?.accessToken.value,
|
||||
authentication: authentication ?? this.getSessionAccessToken(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -108,7 +119,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
url: joinPaths(this.host, path),
|
||||
params,
|
||||
verb: HttpVerb.Patch,
|
||||
authentication: authentication ?? this.session?.accessToken.value,
|
||||
authentication: authentication ?? this.getSessionAccessToken(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,7 +128,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
url: joinPaths(this.host, path),
|
||||
params,
|
||||
verb: HttpVerb.Delete,
|
||||
authentication: authentication ?? this.session?.accessToken.value,
|
||||
authentication: authentication ?? this.getSessionAccessToken(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -130,7 +141,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
if (this.inProgressRefreshSessionPromise && !isRefreshRequest) {
|
||||
await this.inProgressRefreshSessionPromise
|
||||
|
||||
httpRequest.authentication = this.session?.accessToken.value
|
||||
httpRequest.authentication = this.getSessionAccessToken()
|
||||
}
|
||||
|
||||
const response = await this.requestHandler.handleRequest<T>(httpRequest)
|
||||
@@ -152,7 +163,7 @@ export class HttpService implements HttpServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
httpRequest.authentication = this.session?.accessToken.value
|
||||
httpRequest.authentication = this.getSessionAccessToken()
|
||||
|
||||
return this.runHttp(httpRequest)
|
||||
}
|
||||
@@ -161,7 +172,11 @@ export class HttpService implements HttpServiceInterface {
|
||||
}
|
||||
|
||||
private async refreshSession(): Promise<boolean> {
|
||||
if (this.session === null) {
|
||||
if (!this.session) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.session instanceof LegacySession) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Session } from '@standardnotes/domain-core'
|
||||
import { LegacySession, Session } from '@standardnotes/domain-core'
|
||||
import { HttpRequest, HttpRequestParams, HttpResponse, HttpResponseMeta } from '@standardnotes/responses'
|
||||
|
||||
export interface HttpServiceInterface {
|
||||
setHost(host: string): void
|
||||
getHost(): string
|
||||
setSession(session: Session): void
|
||||
|
||||
get<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
getExternal<T>(url: string, params?: HttpRequestParams): Promise<HttpResponse<T>>
|
||||
post<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
@@ -12,9 +12,12 @@ export interface HttpServiceInterface {
|
||||
patch<T>(path: string, params: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
delete<T>(path: string, params?: HttpRequestParams, authentication?: string): Promise<HttpResponse<T>>
|
||||
runHttp<T>(httpRequest: HttpRequest): Promise<HttpResponse<T>>
|
||||
|
||||
setSession(session: Session | LegacySession): void
|
||||
setCallbacks(
|
||||
updateMetaCallback: (meta: HttpResponseMeta) => void,
|
||||
refreshSessionCallback: (session: Session) => void,
|
||||
): void
|
||||
|
||||
deinit(): void
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ValetTokenOperation } from '@standardnotes/responses'
|
||||
import { SharedVaultMoveType } from './SharedVaultMoveType'
|
||||
import { SharedVaultMoveType, ValetTokenOperation } from '@standardnotes/responses'
|
||||
|
||||
export type CreateSharedVaultValetTokenParams = {
|
||||
sharedVaultUuid: string
|
||||
|
||||
@@ -18,4 +18,3 @@ export * from './User/UserRegistrationRequestParams'
|
||||
export * from './User/UserUpdateRequestParams'
|
||||
export * from './UserRequest/UserRequestRequestParams'
|
||||
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
|
||||
export * from './SharedVault/SharedVaultMoveType'
|
||||
|
||||
@@ -13,4 +13,5 @@ export const SharedVaultInvitesPaths = {
|
||||
`/v1/shared-vaults/${sharedVaultUuid}/invites/${inviteUuid}`,
|
||||
deleteAllSharedVaultInvites: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/invites`,
|
||||
deleteAllInboundInvites: '/v1/shared-vaults/invites/inbound',
|
||||
deleteAllOutboundInvites: '/v1/shared-vaults/invites/outbound',
|
||||
}
|
||||
|
||||
@@ -72,4 +72,8 @@ export class SharedVaultInvitesServer implements SharedVaultInvitesServerInterfa
|
||||
deleteAllInboundInvites(): Promise<HttpResponse<{ success: boolean }>> {
|
||||
return this.httpService.delete(SharedVaultInvitesPaths.deleteAllInboundInvites)
|
||||
}
|
||||
|
||||
deleteAllOutboundInvites(): Promise<HttpResponse<{ success: boolean }>> {
|
||||
return this.httpService.delete(SharedVaultInvitesPaths.deleteAllOutboundInvites)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,5 @@ export interface SharedVaultInvitesServerInterface {
|
||||
): Promise<HttpResponse<DeleteAllSharedVaultInvitesResponse>>
|
||||
deleteInvite(params: DeleteInviteRequestParams): Promise<HttpResponse<DeleteInviteResponse>>
|
||||
deleteAllInboundInvites(): Promise<HttpResponse<{ success: boolean }>>
|
||||
deleteAllOutboundInvites(): Promise<HttpResponse<{ success: boolean }>>
|
||||
}
|
||||
|
||||
@@ -3,6 +3,78 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.110](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.109](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.108](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.107](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.106](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.105](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.104](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.103](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.102](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.101](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.100](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.99](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.98](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.97](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.96](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.95](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.94](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.93](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.92](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/clipper",
|
||||
"description": "Web clipper browser extension for Standard Notes",
|
||||
"version": "1.1.92",
|
||||
"version": "1.1.110",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
|
||||
|
||||
@@ -3,6 +3,78 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.108.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.31](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.27](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.108.23",
|
||||
"version": "3.108.41",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.21.53](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.21.52](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.21.51](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.21.51",
|
||||
"version": "1.21.53",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
KeySystemRootKeyInterface,
|
||||
RootKeyInterface,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
PortablePublicKeySet,
|
||||
} from '@standardnotes/models'
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { firstHalfOfString, secondHalfOfString, splitString, UuidGenerator } from '@standardnotes/utils'
|
||||
@@ -28,11 +29,12 @@ import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
||||
import { OperatorInterface } from '../OperatorInterface/OperatorInterface'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsyncOperatorInterface } from '../OperatorInterface/AsyncOperatorInterface'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ContentType, Result } from '@standardnotes/domain-core'
|
||||
import { AsymmetricItemAdditionalData } from '../../Types/EncryptionAdditionalData'
|
||||
|
||||
const NO_IV = '00000000000000000000000000000000'
|
||||
|
||||
@@ -272,11 +274,23 @@ export class SNProtocolOperator001 implements OperatorInterface, AsyncOperatorIn
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
asymmetricDecryptOwnMessage(_dto: {
|
||||
message: string
|
||||
ownPrivateKey: string
|
||||
recipientPublicKey: string
|
||||
}): Result<AsymmetricDecryptResult> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
asymmetricSignatureVerifyDetached(_encryptedString: string): AsymmetricSignatureVerificationDetachedResult {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(_string: string): PublicKeySet {
|
||||
asymmetricStringGetAdditionalData(_dto: { encryptedString: string }): Result<AsymmetricItemAdditionalData> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(_string: string): PortablePublicKeySet {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
KeySystemIdentifier,
|
||||
RootKeyInterface,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
PortablePublicKeySet,
|
||||
} from '@standardnotes/models'
|
||||
import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||
import { HexString, PkcKeyPair, PureCryptoInterface, Utf8String } from '@standardnotes/sncrypto-common'
|
||||
@@ -30,9 +31,9 @@ import { OperatorInterface } from '../OperatorInterface/OperatorInterface'
|
||||
import { AsymmetricallyEncryptedString } from '../Types/Types'
|
||||
import { AsymmetricItemAdditionalData } from '../../Types/EncryptionAdditionalData'
|
||||
import { V004AsymmetricStringComponents } from './V004AlgorithmTypes'
|
||||
import { AsymmetricEncryptUseCase } from './UseCase/Asymmetric/AsymmetricEncrypt'
|
||||
import { AsymmetricEncrypt004 } from './UseCase/Asymmetric/AsymmetricEncrypt'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from './UseCase/Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricDecryptUseCase } from './UseCase/Asymmetric/AsymmetricDecrypt'
|
||||
import { AsymmetricDecrypt004 } from './UseCase/Asymmetric/AsymmetricDecrypt'
|
||||
import { GenerateDecryptedParametersUseCase } from './UseCase/Symmetric/GenerateDecryptedParameters'
|
||||
import { GenerateEncryptedParametersUseCase } from './UseCase/Symmetric/GenerateEncryptedParameters'
|
||||
import { DeriveRootKeyUseCase } from './UseCase/RootKey/DeriveRootKey'
|
||||
@@ -41,14 +42,15 @@ import { CreateRootKeyUseCase } from './UseCase/RootKey/CreateRootKey'
|
||||
import { UuidGenerator } from '@standardnotes/utils'
|
||||
import { CreateKeySystemItemsKeyUseCase } from './UseCase/KeySystem/CreateKeySystemItemsKey'
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
import { CreateRandomKeySystemRootKey } from './UseCase/KeySystem/CreateRandomKeySystemRootKey'
|
||||
import { CreateUserInputKeySystemRootKey } from './UseCase/KeySystem/CreateUserInputKeySystemRootKey'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsymmetricSignatureVerificationDetachedUseCase } from './UseCase/Asymmetric/AsymmetricSignatureVerificationDetached'
|
||||
import { AsymmetricSignatureVerificationDetached004 } from './UseCase/Asymmetric/AsymmetricSignatureVerificationDetached'
|
||||
import { DeriveKeySystemRootKeyUseCase } from './UseCase/KeySystem/DeriveKeySystemRootKey'
|
||||
import { SyncOperatorInterface } from '../OperatorInterface/SyncOperatorInterface'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ContentType, Result } from '@standardnotes/domain-core'
|
||||
import { AsymmetricStringGetAdditionalData004 } from './UseCase/Asymmetric/AsymmetricStringGetAdditionalData'
|
||||
import { AsymmetricDecryptOwnMessage004 } from './UseCase/Asymmetric/AsymmetricDecryptOwnMessage'
|
||||
|
||||
export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInterface {
|
||||
constructor(protected readonly crypto: PureCryptoInterface) {}
|
||||
@@ -167,7 +169,7 @@ export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInt
|
||||
senderSigningKeyPair: PkcKeyPair
|
||||
recipientPublicKey: HexString
|
||||
}): AsymmetricallyEncryptedString {
|
||||
const usecase = new AsymmetricEncryptUseCase(this.crypto)
|
||||
const usecase = new AsymmetricEncrypt004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
@@ -175,18 +177,34 @@ export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInt
|
||||
stringToDecrypt: AsymmetricallyEncryptedString
|
||||
recipientSecretKey: HexString
|
||||
}): AsymmetricDecryptResult | null {
|
||||
const usecase = new AsymmetricDecryptUseCase(this.crypto)
|
||||
const usecase = new AsymmetricDecrypt004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
asymmetricDecryptOwnMessage(dto: {
|
||||
message: AsymmetricallyEncryptedString
|
||||
ownPrivateKey: HexString
|
||||
recipientPublicKey: HexString
|
||||
}): Result<AsymmetricDecryptResult> {
|
||||
const usecase = new AsymmetricDecryptOwnMessage004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
asymmetricSignatureVerifyDetached(
|
||||
encryptedString: AsymmetricallyEncryptedString,
|
||||
): AsymmetricSignatureVerificationDetachedResult {
|
||||
const usecase = new AsymmetricSignatureVerificationDetachedUseCase(this.crypto)
|
||||
const usecase = new AsymmetricSignatureVerificationDetached004(this.crypto)
|
||||
return usecase.execute({ encryptedString })
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet {
|
||||
asymmetricStringGetAdditionalData(dto: {
|
||||
encryptedString: AsymmetricallyEncryptedString
|
||||
}): Result<AsymmetricItemAdditionalData> {
|
||||
const usecase = new AsymmetricStringGetAdditionalData004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PortablePublicKeySet {
|
||||
const [_, __, ___, additionalDataString] = <V004AsymmetricStringComponents>string.split(':')
|
||||
const parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
const additionalData = parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
+5
-5
@@ -1,27 +1,27 @@
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { getMockedCrypto } from '../../MockedCrypto'
|
||||
import { AsymmetricDecryptUseCase } from './AsymmetricDecrypt'
|
||||
import { AsymmetricEncryptUseCase } from './AsymmetricEncrypt'
|
||||
import { AsymmetricDecrypt004 } from './AsymmetricDecrypt'
|
||||
import { AsymmetricEncrypt004 } from './AsymmetricEncrypt'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
describe('asymmetric decrypt use case', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let usecase: AsymmetricDecryptUseCase
|
||||
let usecase: AsymmetricDecrypt004
|
||||
let recipientKeyPair: PkcKeyPair
|
||||
let senderKeyPair: PkcKeyPair
|
||||
let senderSigningKeyPair: PkcKeyPair
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = getMockedCrypto()
|
||||
usecase = new AsymmetricDecryptUseCase(crypto)
|
||||
usecase = new AsymmetricDecrypt004(crypto)
|
||||
recipientKeyPair = crypto.sodiumCryptoBoxSeedKeypair('recipient-seedling')
|
||||
senderKeyPair = crypto.sodiumCryptoBoxSeedKeypair('sender-seedling')
|
||||
senderSigningKeyPair = crypto.sodiumCryptoSignSeedKeypair('sender-signing-seedling')
|
||||
})
|
||||
|
||||
const getEncryptedString = () => {
|
||||
const encryptUsecase = new AsymmetricEncryptUseCase(crypto)
|
||||
const encryptUsecase = new AsymmetricEncrypt004(crypto)
|
||||
|
||||
const result = encryptUsecase.execute({
|
||||
stringToEncrypt: 'foobar',
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsisten
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricDecryptResult } from '../../../Types/AsymmetricDecryptResult'
|
||||
|
||||
export class AsymmetricDecryptUseCase {
|
||||
export class AsymmetricDecrypt004 {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { HexString, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../../../Types/Types'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricDecryptResult } from '../../../Types/AsymmetricDecryptResult'
|
||||
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class AsymmetricDecryptOwnMessage004 implements SyncUseCaseInterface<AsymmetricDecryptResult> {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
execute(dto: {
|
||||
message: AsymmetricallyEncryptedString
|
||||
ownPrivateKey: HexString
|
||||
recipientPublicKey: HexString
|
||||
}): Result<AsymmetricDecryptResult> {
|
||||
const [_, nonce, ciphertext, additionalDataString] = <V004AsymmetricStringComponents>dto.message.split(':')
|
||||
|
||||
const additionalData = this.parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
try {
|
||||
const plaintext = this.crypto.sodiumCryptoBoxEasyDecrypt(
|
||||
ciphertext,
|
||||
nonce,
|
||||
dto.recipientPublicKey,
|
||||
dto.ownPrivateKey,
|
||||
)
|
||||
|
||||
if (!plaintext) {
|
||||
return Result.fail('Could not decrypt message')
|
||||
}
|
||||
|
||||
const signatureVerified = this.crypto.sodiumCryptoSignVerify(
|
||||
ciphertext,
|
||||
additionalData.signingData.signature,
|
||||
additionalData.signingData.publicKey,
|
||||
)
|
||||
|
||||
return Result.ok({
|
||||
plaintext,
|
||||
signatureVerified,
|
||||
signaturePublicKey: additionalData.signingData.publicKey,
|
||||
senderPublicKey: additionalData.senderPublicKey,
|
||||
})
|
||||
} catch (error) {
|
||||
return Result.fail('Could not decrypt message')
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,20 +1,20 @@
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { getMockedCrypto } from '../../MockedCrypto'
|
||||
import { AsymmetricEncryptUseCase } from './AsymmetricEncrypt'
|
||||
import { AsymmetricEncrypt004 } from './AsymmetricEncrypt'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
describe('asymmetric encrypt use case', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let usecase: AsymmetricEncryptUseCase
|
||||
let usecase: AsymmetricEncrypt004
|
||||
let encryptionKeyPair: PkcKeyPair
|
||||
let signingKeyPair: PkcKeyPair
|
||||
let parseBase64Usecase: ParseConsistentBase64JsonPayloadUseCase
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = getMockedCrypto()
|
||||
usecase = new AsymmetricEncryptUseCase(crypto)
|
||||
usecase = new AsymmetricEncrypt004(crypto)
|
||||
encryptionKeyPair = crypto.sodiumCryptoBoxSeedKeypair('seedling')
|
||||
signingKeyPair = crypto.sodiumCryptoSignSeedKeypair('seedling')
|
||||
parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(crypto)
|
||||
|
||||
@@ -5,7 +5,7 @@ import { V004AsymmetricCiphertextPrefix, V004AsymmetricStringComponents } from '
|
||||
import { CreateConsistentBase64JsonPayloadUseCase } from '../Utils/CreateConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
export class AsymmetricEncryptUseCase {
|
||||
export class AsymmetricEncrypt004 {
|
||||
private base64DataUsecase = new CreateConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
@@ -21,8 +21,8 @@ export class AsymmetricEncryptUseCase {
|
||||
const ciphertext = this.crypto.sodiumCryptoBoxEasyEncrypt(
|
||||
dto.stringToEncrypt,
|
||||
nonce,
|
||||
dto.senderKeyPair.privateKey,
|
||||
dto.recipientPublicKey,
|
||||
dto.senderKeyPair.privateKey,
|
||||
)
|
||||
|
||||
const additionalData: AsymmetricItemAdditionalData = {
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsisten
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../../../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
|
||||
export class AsymmetricSignatureVerificationDetachedUseCase {
|
||||
export class AsymmetricSignatureVerificationDetached004 {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../../../Types/Types'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class AsymmetricStringGetAdditionalData004 implements SyncUseCaseInterface<AsymmetricItemAdditionalData> {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
execute(dto: { encryptedString: AsymmetricallyEncryptedString }): Result<AsymmetricItemAdditionalData> {
|
||||
const [_, __, ___, additionalDataString] = <V004AsymmetricStringComponents>dto.encryptedString.split(':')
|
||||
|
||||
const additionalData = this.parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
return Result.ok(additionalData)
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -2,8 +2,9 @@ import { ProtocolVersion, ProtocolVersionLatest } from '@standardnotes/common'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { createOperatorForVersion } from './Functions'
|
||||
import { AnyOperatorInterface } from './OperatorInterface/TypeCheck'
|
||||
import { EncryptionOperatorsInterface } from './EncryptionOperatorsInterface'
|
||||
|
||||
export class OperatorManager {
|
||||
export class EncryptionOperators implements EncryptionOperatorsInterface {
|
||||
private operators: Record<string, AnyOperatorInterface> = {}
|
||||
|
||||
constructor(private crypto: PureCryptoInterface) {
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { AnyOperatorInterface } from './OperatorInterface/TypeCheck'
|
||||
|
||||
export interface EncryptionOperatorsInterface {
|
||||
operatorForVersion(version: ProtocolVersion): AnyOperatorInterface
|
||||
defaultOperator(): AnyOperatorInterface
|
||||
deinit(): void
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
KeySystemRootKeyInterface,
|
||||
KeySystemIdentifier,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
PortablePublicKeySet,
|
||||
} from '@standardnotes/models'
|
||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { EncryptedOutputParameters } from '../../Types/EncryptedParameters'
|
||||
@@ -15,8 +16,9 @@ import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedA
|
||||
import { HexString, PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../Types/Types'
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsymmetricItemAdditionalData } from '../../Types/EncryptionAdditionalData'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
/**w
|
||||
* An operator is responsible for performing crypto operations, such as generating keys
|
||||
@@ -92,11 +94,21 @@ export interface OperatorInterface {
|
||||
recipientSecretKey: HexString
|
||||
}): AsymmetricDecryptResult | null
|
||||
|
||||
asymmetricDecryptOwnMessage(dto: {
|
||||
message: AsymmetricallyEncryptedString
|
||||
ownPrivateKey: HexString
|
||||
recipientPublicKey: HexString
|
||||
}): Result<AsymmetricDecryptResult>
|
||||
|
||||
asymmetricSignatureVerifyDetached(
|
||||
encryptedString: AsymmetricallyEncryptedString,
|
||||
): AsymmetricSignatureVerificationDetachedResult
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet
|
||||
asymmetricStringGetAdditionalData(dto: {
|
||||
encryptedString: AsymmetricallyEncryptedString
|
||||
}): Result<AsymmetricItemAdditionalData>
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PortablePublicKeySet
|
||||
|
||||
versionForAsymmetricallyEncryptedString(encryptedString: string): ProtocolVersion
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ import {
|
||||
ErrorDecryptingParameters,
|
||||
} from '../Types/EncryptedParameters'
|
||||
import { DecryptedParameters } from '../Types/DecryptedParameters'
|
||||
import { OperatorManager } from './OperatorManager'
|
||||
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { isAsyncOperator } from './OperatorInterface/TypeCheck'
|
||||
import { EncryptionOperatorsInterface } from './EncryptionOperatorsInterface'
|
||||
|
||||
export async function encryptPayload(
|
||||
payload: DecryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||
operatorManager: OperatorManager,
|
||||
operatorManager: EncryptionOperatorsInterface,
|
||||
signingKeyPair: PkcKeyPair | undefined,
|
||||
): Promise<EncryptedOutputParameters> {
|
||||
const operator = operatorManager.operatorForVersion(key.keyVersion)
|
||||
@@ -42,7 +42,7 @@ export async function encryptPayload(
|
||||
export async function decryptPayload<C extends ItemContent = ItemContent>(
|
||||
payload: EncryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||
operatorManager: OperatorManager,
|
||||
operatorManager: EncryptionOperatorsInterface,
|
||||
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
||||
const operator = operatorManager.operatorForVersion(payload.version)
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { ItemsKeyInterface } from '@standardnotes/models'
|
||||
|
||||
export function findDefaultItemsKey(itemsKeys: ItemsKeyInterface[]): ItemsKeyInterface | undefined {
|
||||
if (itemsKeys.length === 1) {
|
||||
return itemsKeys[0]
|
||||
}
|
||||
|
||||
const defaultKeys = itemsKeys.filter((key) => {
|
||||
return key.isDefault
|
||||
})
|
||||
|
||||
if (defaultKeys.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (defaultKeys.length === 1) {
|
||||
return defaultKeys[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Prioritize one that is synced, as neverSynced keys will likely be deleted after
|
||||
* DownloadFirst sync.
|
||||
*/
|
||||
const syncedKeys = defaultKeys.filter((key) => !key.neverSynced)
|
||||
if (syncedKeys.length > 0) {
|
||||
return syncedKeys[0]
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
@@ -1,42 +1,30 @@
|
||||
export * from './Algorithm'
|
||||
export * from './Backups/BackupFileType'
|
||||
|
||||
export * from './Keys/ItemsKey/ItemsKey'
|
||||
export * from './Keys/ItemsKey/ItemsKeyMutator'
|
||||
export * from './Keys/ItemsKey/Registration'
|
||||
|
||||
export * from './Keys/KeySystemItemsKey/KeySystemItemsKey'
|
||||
export * from './Keys/KeySystemItemsKey/KeySystemItemsKeyMutator'
|
||||
export * from './Keys/KeySystemItemsKey/Registration'
|
||||
|
||||
export * from './Keys/RootKey/Functions'
|
||||
export * from './Keys/RootKey/KeyParamsFunctions'
|
||||
export * from './Keys/RootKey/ProtocolVersionForKeyParams'
|
||||
export * from './Keys/RootKey/RootKey'
|
||||
export * from './Keys/RootKey/RootKeyParams'
|
||||
export * from './Keys/RootKey/ValidKeyParamsKeys'
|
||||
|
||||
export * from './Keys/Utils/KeyRecoveryStrings'
|
||||
|
||||
export * from './Operator/001/Operator001'
|
||||
export * from './Operator/002/Operator002'
|
||||
export * from './Operator/003/Operator003'
|
||||
export * from './Operator/004/Operator004'
|
||||
export * from './Operator/004/V004AlgorithmHelpers'
|
||||
|
||||
export * from './Operator/EncryptionOperators'
|
||||
export * from './Operator/EncryptionOperatorsInterface'
|
||||
export * from './Operator/Functions'
|
||||
export * from './Operator/OperatorInterface/OperatorInterface'
|
||||
export * from './Operator/OperatorManager'
|
||||
export * from './Operator/OperatorWrapper'
|
||||
export * from './Operator/Types/PublicKeySet'
|
||||
export * from './Operator/Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
export * from './Operator/Types/Types'
|
||||
|
||||
export * from './Service/Encryption/EncryptionProviderInterface'
|
||||
export * from './Service/KeySystemKeyManagerInterface'
|
||||
export * from './Service/Functions'
|
||||
export * from './Service/RootKey/KeyMode'
|
||||
|
||||
export * from './Split/AbstractKeySplit'
|
||||
export * from './Split/EncryptionSplit'
|
||||
export * from './Split/EncryptionTypeSplit'
|
||||
@@ -44,11 +32,9 @@ export * from './Split/Functions'
|
||||
export * from './Split/KeyedDecryptionSplit'
|
||||
export * from './Split/KeyedEncryptionSplit'
|
||||
export * from './StandardException'
|
||||
|
||||
export * from './Types/EncryptedParameters'
|
||||
export * from './Types/DecryptedParameters'
|
||||
export * from './Types/EncryptedParameters'
|
||||
export * from './Types/ItemAuthenticatedData'
|
||||
export * from './Types/LegacyAttachedData'
|
||||
export * from './Types/RootKeyEncryptedAuthenticatedData'
|
||||
|
||||
export * from './Username/PrivateUsername'
|
||||
|
||||
@@ -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.59.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/features
|
||||
|
||||
## [1.59.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/features",
|
||||
"version": "1.59.9",
|
||||
"version": "1.59.10",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -27,7 +27,6 @@
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/security": "^1.7.6",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.28.62](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.28.61](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.28.60](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.28.60",
|
||||
"version": "1.28.62",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.16.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.16.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.16.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.16.6",
|
||||
"version": "1.16.8",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,94 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.56.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Shared image links are now correctly handled on iOS ([ab2e88a](https://github.com/standardnotes/app/commit/ab2e88a710c65771312bd9d02cec0b400d352a0e))
|
||||
|
||||
## [3.56.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.56.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
|
||||
|
||||
### Features
|
||||
|
||||
* You can now share files & text from other apps into SN on iOS ([#2358](https://github.com/standardnotes/app/issues/2358)) ([88ef588](https://github.com/standardnotes/app/commit/88ef5887a91b4a0eace68faec90cd53381436dea))
|
||||
|
||||
## [3.55.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
@@ -2,7 +2,7 @@ storage_mode("git")
|
||||
|
||||
# type("development") # The default type, can be: appstore, adhoc, enterprise or development
|
||||
|
||||
app_identifier(["com.standardnotes.standardnotes"])
|
||||
app_identifier(["com.standardnotes.standardnotes", "com.standardnotes.standardnotes.Share-To-SN"])
|
||||
|
||||
# For all available options run `fastlane match --help`
|
||||
# Remove the # in the beginning of the line to enable the other options
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
|
||||
@interface RCT_EXTERN_MODULE(ReceiveSharingIntent, NSObject)
|
||||
|
||||
RCT_EXTERN_METHOD(getFileNames:(NSString)url
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter: (RCTPromiseRejectBlock)reject);
|
||||
|
||||
RCT_EXTERN_METHOD(clearFileNames)
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,147 @@
|
||||
import Foundation
|
||||
import Photos
|
||||
import MobileCoreServices
|
||||
|
||||
@objc(ReceiveSharingIntent)
|
||||
class ReceiveSharingIntent: NSObject {
|
||||
|
||||
struct Share: Codable {
|
||||
var media: [SharedMediaFile] = []
|
||||
var text: [String] = []
|
||||
var urls: [String] = []
|
||||
}
|
||||
|
||||
private var share = Share()
|
||||
|
||||
@objc
|
||||
func getFileNames(_ url: String,
|
||||
resolver resolve: RCTPromiseResolveBlock,
|
||||
rejecter reject: RCTPromiseRejectBlock
|
||||
) -> Void {
|
||||
let fileUrl = URL(string: url)
|
||||
let json = handleUrl(url: fileUrl);
|
||||
if(json == "error"){
|
||||
let error = NSError(domain: "", code: 400, userInfo: nil)
|
||||
reject("message", "file type is Invalid", error);
|
||||
}else if(json == "invalid group name"){
|
||||
let error = NSError(domain: "", code: 400, userInfo: nil)
|
||||
reject("message", "invalid group name. Please check your share extention bundle name is same as `group.mainbundle name` ", error);
|
||||
}else{
|
||||
resolve(json);
|
||||
}
|
||||
}
|
||||
|
||||
private func handleUrl(url: URL?) -> String? {
|
||||
if let url = url {
|
||||
let appDomain = Bundle.main.bundleIdentifier!
|
||||
let userDefaults = UserDefaults(suiteName: "group.\(appDomain)")
|
||||
if let key = url.host?.components(separatedBy: "=").last {
|
||||
if let mediaJson = userDefaults?.object(forKey: "\(key).media") as? Data {
|
||||
let mediaSharedArray = decode(data: mediaJson)
|
||||
let sharedMediaFiles: [SharedMediaFile] = mediaSharedArray.compactMap {
|
||||
guard let path = getAbsolutePath(for: $0.path) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return SharedMediaFile.init(path: path, fileName: fileNameForPath(path: path), mimeType: mimeTypeForPath(path: path))
|
||||
}
|
||||
self.share.media = sharedMediaFiles
|
||||
}
|
||||
if let textSharedArray = userDefaults?.object(forKey: "\(key).text") as? [String] {
|
||||
self.share.text = textSharedArray
|
||||
}
|
||||
if let textSharedArray = userDefaults?.object(forKey: "\(key).url") as? [String] {
|
||||
self.share.urls = textSharedArray
|
||||
}
|
||||
let encodedData = try? JSONEncoder().encode(self.share)
|
||||
let json = String(data: encodedData!, encoding: .utf8)!
|
||||
return json
|
||||
}
|
||||
return "error"
|
||||
}
|
||||
return "invalid group name"
|
||||
}
|
||||
|
||||
|
||||
private func getAbsolutePath(for identifier: String) -> String? {
|
||||
if (identifier.starts(with: "file://") || identifier.starts(with: "/var/mobile/Media") || identifier.starts(with: "/private/var/mobile")) {
|
||||
return identifier;
|
||||
}
|
||||
let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: .none).firstObject
|
||||
if(phAsset == nil) {
|
||||
return nil
|
||||
}
|
||||
let (url, _) = getFullSizeImageURLAndOrientation(for: phAsset!)
|
||||
return url
|
||||
}
|
||||
|
||||
private func getFullSizeImageURLAndOrientation(for asset: PHAsset)-> (String?, Int) {
|
||||
var url: String? = nil
|
||||
var orientation: Int = 0
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
let options2 = PHContentEditingInputRequestOptions()
|
||||
options2.isNetworkAccessAllowed = true
|
||||
asset.requestContentEditingInput(with: options2){(input, info) in
|
||||
orientation = Int(input?.fullSizeImageOrientation ?? 0)
|
||||
url = input?.fullSizeImageURL?.path
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
return (url, orientation)
|
||||
}
|
||||
|
||||
private func decode(data: Data) -> [SharedMediaFile] {
|
||||
let encodedData = try? JSONDecoder().decode([SharedMediaFile].self, from: data)
|
||||
return encodedData!
|
||||
}
|
||||
|
||||
private func toJson(data: [SharedMediaFile]?) -> String? {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
let encodedData = try? JSONEncoder().encode(data)
|
||||
let json = String(data: encodedData!, encoding: .utf8)!
|
||||
return json
|
||||
}
|
||||
|
||||
class SharedMediaFile: Codable {
|
||||
var path: String;
|
||||
var fileName: String?;
|
||||
var mimeType: String?;
|
||||
|
||||
init(path: String, fileName: String?, mimeType: String?) {
|
||||
self.path = path
|
||||
self.fileName = fileName
|
||||
self.mimeType = mimeType
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func clearFileNames(){
|
||||
print("clearFileNames");
|
||||
}
|
||||
|
||||
|
||||
@objc
|
||||
static func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func fileNameForPath(path: String) -> String? {
|
||||
let url = NSURL(fileURLWithPath: path)
|
||||
return url.lastPathComponent
|
||||
}
|
||||
|
||||
func mimeTypeForPath(path: String) -> String {
|
||||
let url = NSURL(fileURLWithPath: path)
|
||||
let pathExtension = url.pathExtension
|
||||
|
||||
if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension! as NSString, nil)?.takeRetainedValue() {
|
||||
if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
|
||||
return mimetype as String
|
||||
}
|
||||
return "application/octet-stream"
|
||||
}
|
||||
return "application/octet-stream"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Share View Controller-->
|
||||
<scene sceneID="ceB-am-kn3">
|
||||
<objects>
|
||||
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>PHSupportedMediaTypes</key>
|
||||
<array>
|
||||
<string>Video</string>
|
||||
<string>Image</string>
|
||||
</array>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||
<integer>100</integer>
|
||||
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
|
||||
<integer>100</integer>
|
||||
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
|
||||
<integer>100</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.standardnotes.standardnotes</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.standardnotes.standardnotes</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,336 @@
|
||||
import UIKit
|
||||
import Social
|
||||
import MobileCoreServices
|
||||
import Photos
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
let hostAppBundleIdentifier = "com.standardnotes.standardnotes"
|
||||
let shareProtocol = hostAppBundleIdentifier
|
||||
|
||||
class ShareViewController: SLComposeServiceViewController {
|
||||
let sharedKey = "ShareKey"
|
||||
var sharedMedia: [SharedMediaFile] = []
|
||||
var sharedText: [String] = []
|
||||
var sharedURL: [String] = []
|
||||
let imageContentType = UTType.image.identifier
|
||||
let videoContentType = UTType.movie.identifier
|
||||
let textContentType = UTType.text.identifier
|
||||
let urlContentType = UTType.url.identifier
|
||||
let fileURLType = UTType.fileURL.identifier;
|
||||
|
||||
override func isContentValid() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad();
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
|
||||
if let contents = content.attachments {
|
||||
for (index, attachment) in (contents).enumerated() {
|
||||
if attachment.hasItemConformingToTypeIdentifier(fileURLType) {
|
||||
handleFiles(content: content, attachment: attachment, index: index)
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(imageContentType) {
|
||||
handleImages(content: content, attachment: attachment, index: index)
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(textContentType) {
|
||||
handleText(content: content, attachment: attachment, index: index)
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(urlContentType) {
|
||||
handleUrl(content: content, attachment: attachment, index: index)
|
||||
} else if attachment.hasItemConformingToTypeIdentifier(videoContentType) {
|
||||
handleVideos(content: content, attachment: attachment, index: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func didSelectPost() {
|
||||
print("didSelectPost");
|
||||
}
|
||||
|
||||
override func configurationItems() -> [Any]! {
|
||||
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
|
||||
return []
|
||||
}
|
||||
|
||||
private func handleText (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||
attachment.loadItem(forTypeIdentifier: textContentType, options: nil) { [weak self] data, error in
|
||||
|
||||
if error == nil, let item = data as? String, let this = self {
|
||||
|
||||
this.sharedText.append(item)
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
this.redirectToHostApp()
|
||||
}
|
||||
|
||||
} else {
|
||||
self?.dismissWithError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleUrl (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||
attachment.loadItem(forTypeIdentifier: urlContentType, options: nil) { [weak self] data, error in
|
||||
|
||||
if error == nil, let item = data as? URL, let this = self {
|
||||
|
||||
this.sharedURL.append(item.absoluteString)
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
this.redirectToHostApp()
|
||||
}
|
||||
|
||||
} else {
|
||||
self?.dismissWithError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleImages (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||
attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in
|
||||
|
||||
if error == nil, let url = data as? URL, let this = self {
|
||||
// this.redirectToHostApp(type: .media)
|
||||
// Always copy
|
||||
let fileExtension = this.getExtension(from: url, type: .video)
|
||||
let newName = UUID().uuidString
|
||||
let newPath = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(newName).\(fileExtension)")
|
||||
let copied = this.copyFile(at: url, to: newPath)
|
||||
if(copied) {
|
||||
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .image))
|
||||
}
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
this.redirectToHostApp()
|
||||
}
|
||||
|
||||
} else {
|
||||
self?.dismissWithError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleVideos (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||
attachment.loadItem(forTypeIdentifier: videoContentType, options:nil) { [weak self] data, error in
|
||||
|
||||
if error == nil, let url = data as? URL, let this = self {
|
||||
|
||||
// Always copy
|
||||
let fileExtension = this.getExtension(from: url, type: .video)
|
||||
let newName = UUID().uuidString
|
||||
let newPath = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(newName).\(fileExtension)")
|
||||
let copied = this.copyFile(at: url, to: newPath)
|
||||
if(copied) {
|
||||
guard let sharedFile = this.getSharedMediaFile(forVideo: newPath) else {
|
||||
return
|
||||
}
|
||||
this.sharedMedia.append(sharedFile)
|
||||
}
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
this.redirectToHostApp()
|
||||
}
|
||||
|
||||
} else {
|
||||
self?.dismissWithError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleFiles (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||
attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in
|
||||
|
||||
if error == nil, let url = data as? URL, let this = self {
|
||||
|
||||
// Always copy
|
||||
let newName = this.getFileName(from :url)
|
||||
let newPath = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(newName)")
|
||||
let copied = this.copyFile(at: url, to: newPath)
|
||||
if (copied) {
|
||||
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file))
|
||||
}
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
this.redirectToHostApp()
|
||||
}
|
||||
|
||||
} else {
|
||||
self?.dismissWithError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func redirectToHostApp() {
|
||||
let userDefaults = UserDefaults(suiteName: "group.\(hostAppBundleIdentifier)")
|
||||
userDefaults?.set(self.toData(data: self.sharedMedia), forKey: "\(self.sharedKey).media")
|
||||
userDefaults?.set(self.sharedText, forKey: "\(self.sharedKey).text")
|
||||
userDefaults?.set(self.sharedURL, forKey: "\(self.sharedKey).url")
|
||||
userDefaults?.synchronize()
|
||||
|
||||
let url = URL(string: "\(shareProtocol)://dataUrl=\(sharedKey)")
|
||||
var responder = self as UIResponder?
|
||||
let selectorOpenURL = sel_registerName("openURL:")
|
||||
|
||||
while (responder != nil) {
|
||||
if (responder?.responds(to: selectorOpenURL))! {
|
||||
let _ = responder?.perform(selectorOpenURL, with: url)
|
||||
}
|
||||
responder = responder!.next
|
||||
}
|
||||
|
||||
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}
|
||||
|
||||
private func dismissWithError() {
|
||||
print("[ERROR] Error loading data!")
|
||||
let alert = UIAlertController(title: "Error", message: "Error loading data", preferredStyle: .alert)
|
||||
|
||||
let action = UIAlertAction(title: "Error", style: .cancel) { _ in
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
alert.addAction(action)
|
||||
present(alert, animated: true, completion: nil)
|
||||
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||
}
|
||||
|
||||
private func alertLog(message: String) {
|
||||
let alert = UIAlertController(title: "Log", message: message, preferredStyle: .alert)
|
||||
|
||||
let action = UIAlertAction(title: "OK", style: .default)
|
||||
|
||||
alert.addAction(action)
|
||||
present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
enum RedirectType {
|
||||
case media
|
||||
case text
|
||||
case file
|
||||
}
|
||||
|
||||
func getExtension(from url: URL, type: SharedMediaType) -> String {
|
||||
let parts = url.lastPathComponent.components(separatedBy: ".")
|
||||
var ex: String? = nil
|
||||
if (parts.count > 1) {
|
||||
ex = parts.last
|
||||
}
|
||||
|
||||
if (ex == nil) {
|
||||
switch type {
|
||||
case .image:
|
||||
ex = "PNG"
|
||||
case .video:
|
||||
ex = "MP4"
|
||||
case .file:
|
||||
ex = "TXT"
|
||||
}
|
||||
}
|
||||
return ex ?? "Unknown"
|
||||
}
|
||||
|
||||
func getFileName(from url: URL) -> String {
|
||||
var name = url.lastPathComponent
|
||||
|
||||
if (name == "") {
|
||||
name = UUID().uuidString + "." + getExtension(from: url, type: .file)
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func copyFile(at srcURL: URL, to dstURL: URL) -> Bool {
|
||||
do {
|
||||
if FileManager.default.fileExists(atPath: dstURL.path) {
|
||||
try FileManager.default.removeItem(at: dstURL)
|
||||
}
|
||||
try FileManager.default.copyItem(at: srcURL, to: dstURL)
|
||||
} catch (let error) {
|
||||
print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private func getSharedMediaFile(forVideo: URL) -> SharedMediaFile? {
|
||||
let asset = AVAsset(url: forVideo)
|
||||
let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()
|
||||
let thumbnailPath = getThumbnailPath(for: forVideo)
|
||||
|
||||
if FileManager.default.fileExists(atPath: thumbnailPath.path) {
|
||||
return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video)
|
||||
}
|
||||
|
||||
var saved = false
|
||||
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
|
||||
assetImgGenerate.appliesPreferredTrackTransform = true
|
||||
// let scale = UIScreen.main.scale
|
||||
assetImgGenerate.maximumSize = CGSize(width: 360, height: 360)
|
||||
do {
|
||||
let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil)
|
||||
try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath)
|
||||
saved = true
|
||||
} catch {
|
||||
saved = false
|
||||
}
|
||||
|
||||
return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil
|
||||
|
||||
}
|
||||
|
||||
private func getThumbnailPath(for url: URL) -> URL {
|
||||
let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "==", with: "")
|
||||
let path = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(fileName).jpg")
|
||||
return path
|
||||
}
|
||||
|
||||
class SharedMediaFile: Codable {
|
||||
var path: String; // can be image, video or url path. It can also be text content
|
||||
var thumbnail: String?; // video thumbnail
|
||||
var duration: Double?; // video duration in milliseconds
|
||||
var type: SharedMediaType;
|
||||
|
||||
|
||||
init(path: String, thumbnail: String?, duration: Double?, type: SharedMediaType) {
|
||||
self.path = path
|
||||
self.thumbnail = thumbnail
|
||||
self.duration = duration
|
||||
self.type = type
|
||||
}
|
||||
|
||||
// Debug method to print out SharedMediaFile details in the console
|
||||
func toString() {
|
||||
print("[SharedMediaFile] \n\tpath: \(self.path)\n\tthumbnail: \(self.thumbnail)\n\tduration: \(self.duration)\n\ttype: \(self.type)")
|
||||
}
|
||||
}
|
||||
|
||||
enum SharedMediaType: Int, Codable {
|
||||
case image
|
||||
case video
|
||||
case file
|
||||
}
|
||||
|
||||
func toData(data: [SharedMediaFile]) -> Data {
|
||||
let encodedData = try? JSONEncoder().encode(data)
|
||||
return encodedData!
|
||||
}
|
||||
}
|
||||
|
||||
extension Array {
|
||||
subscript (safe index: UInt) -> Element? {
|
||||
return Int(index) < count ? self[Int(index)] : nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTViewManager.h>
|
||||
@@ -8,9 +8,14 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
00E356F31AD99517003FC87E /* StandardNotesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* StandardNotesTests.m */; };
|
||||
07CAB75E2A618128008FE1EF /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CAB75D2A618128008FE1EF /* ShareViewController.swift */; };
|
||||
07CAB7612A618128008FE1EF /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 07CAB75F2A618128008FE1EF /* MainInterface.storyboard */; };
|
||||
07CAB7652A618128008FE1EF /* Share To SN.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 07CAB75B2A618128008FE1EF /* Share To SN.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
07CAB76E2A618353008FE1EF /* ReceiveSharingIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CAB76D2A618353008FE1EF /* ReceiveSharingIntent.swift */; };
|
||||
07CAB7702A618385008FE1EF /* ReceiveSharingIntent.m in Sources */ = {isa = PBXBuildFile; fileRef = 07CAB76F2A618385008FE1EF /* ReceiveSharingIntent.m */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
1C2EEB3B45F4EB07AC795C77 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
|
||||
1C2EEB3B45F4EB07AC795C77 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
|
||||
33BB1B14071EBE5978EBF3A8 /* libPods-StandardNotes-StandardNotesTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04FCB5A3A3387CA3CFC82AA3 /* libPods-StandardNotes-StandardNotesTests.a */; };
|
||||
BC8DEA834BF198E8511F04FF /* libPods-StandardNotesDev.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F2D747BE02C2A1BCFEEFD1 /* libPods-StandardNotesDev.a */; };
|
||||
CD6592A9291EEFCC00C09DC6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6592A8291EEFCC00C09DC6 /* StoreKit.framework */; };
|
||||
@@ -34,13 +39,44 @@
|
||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||
remoteInfo = StandardNotes;
|
||||
};
|
||||
07CAB7632A618128008FE1EF /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 07CAB75A2A618128008FE1EF;
|
||||
remoteInfo = "Share To SN";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
07CAB7662A618128008FE1EF /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
07CAB7652A618128008FE1EF /* Share To SN.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StandardNotesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
00E356F21AD99517003FC87E /* StandardNotesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StandardNotesTests.m; sourceTree = "<group>"; };
|
||||
04FCB5A3A3387CA3CFC82AA3 /* libPods-StandardNotes-StandardNotesTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-StandardNotes-StandardNotesTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0780C8AC2A63ECF400DDE846 /* Share To SNRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Share To SNRelease.entitlements"; sourceTree = "<group>"; };
|
||||
07CAB75B2A618128008FE1EF /* Share To SN.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Share To SN.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
07CAB75D2A618128008FE1EF /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
07CAB7602A618128008FE1EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
07CAB7622A618128008FE1EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
07CAB76A2A6182C7008FE1EF /* StandardNotesDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = StandardNotesDebug.entitlements; path = StandardNotes/StandardNotesDebug.entitlements; sourceTree = "<group>"; };
|
||||
07CAB76B2A6182D6008FE1EF /* Share To SNDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Share To SNDebug.entitlements"; sourceTree = "<group>"; };
|
||||
07CAB76C2A618353008FE1EF /* StandardNotes-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StandardNotes-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
07CAB76D2A618353008FE1EF /* ReceiveSharingIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReceiveSharingIntent.swift; path = ReceiveSharingIntent/ReceiveSharingIntent.swift; sourceTree = "<group>"; };
|
||||
07CAB76F2A618385008FE1EF /* ReceiveSharingIntent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ReceiveSharingIntent.m; path = ReceiveSharingIntent/ReceiveSharingIntent.m; sourceTree = "<group>"; };
|
||||
0BB10C8D896AFACECE748F6D /* Pods-StandardNotesDev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StandardNotesDev.release.xcconfig"; path = "Target Support Files/Pods-StandardNotesDev/Pods-StandardNotesDev.release.xcconfig"; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* StandardNotes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StandardNotes.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = StandardNotes/AppDelegate.h; sourceTree = "<group>"; };
|
||||
@@ -75,12 +111,19 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
07CAB7582A618128008FE1EF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CD6592A9291EEFCC00C09DC6 /* StoreKit.framework in Frameworks */,
|
||||
1C2EEB3B45F4EB07AC795C77 /* BuildFile in Frameworks */,
|
||||
1C2EEB3B45F4EB07AC795C77 /* (null) in Frameworks */,
|
||||
DD3D1CE428EC1C8BA0C49211 /* libPods-StandardNotes.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -113,6 +156,18 @@
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
07CAB75C2A618128008FE1EF /* Share To SN */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0780C8AC2A63ECF400DDE846 /* Share To SNRelease.entitlements */,
|
||||
07CAB76B2A6182D6008FE1EF /* Share To SNDebug.entitlements */,
|
||||
07CAB75D2A618128008FE1EF /* ShareViewController.swift */,
|
||||
07CAB75F2A618128008FE1EF /* MainInterface.storyboard */,
|
||||
07CAB7622A618128008FE1EF /* Info.plist */,
|
||||
);
|
||||
path = "Share To SN";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D0B9F98B8FBE9ED9C11F260 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -129,6 +184,7 @@
|
||||
13B07FAE1A68108700A75B9A /* StandardNotes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
07CAB76A2A6182C7008FE1EF /* StandardNotesDebug.entitlements */,
|
||||
D261494428699DCE00B17102 /* Web.bundle */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
CDC75795292552080019F4AF /* AppDelegate.mm */,
|
||||
@@ -137,6 +193,9 @@
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
CD7D5EC8278005B6005FE1BF /* Info.plist */,
|
||||
CD7D5EC927800608005FE1BF /* LaunchScreen.storyboard */,
|
||||
07CAB76D2A618353008FE1EF /* ReceiveSharingIntent.swift */,
|
||||
07CAB76C2A618353008FE1EF /* StandardNotes-Bridging-Header.h */,
|
||||
07CAB76F2A618385008FE1EF /* ReceiveSharingIntent.m */,
|
||||
);
|
||||
name = StandardNotes;
|
||||
sourceTree = "<group>";
|
||||
@@ -166,6 +225,7 @@
|
||||
13B07FAE1A68108700A75B9A /* StandardNotes */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
00E356EF1AD99517003FC87E /* StandardNotesTests */,
|
||||
07CAB75C2A618128008FE1EF /* Share To SN */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
CD7D5EE127801645005FE1BF /* StandardNotesDev-Info.plist */,
|
||||
@@ -184,6 +244,7 @@
|
||||
13B07F961A680F5B00A75B9A /* StandardNotes.app */,
|
||||
00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */,
|
||||
CD7D5EDF278015D2005FE1BF /* StandardNotesDev.app */,
|
||||
07CAB75B2A618128008FE1EF /* Share To SN.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -212,6 +273,23 @@
|
||||
productReference = 00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
07CAB75A2A618128008FE1EF /* Share To SN */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 07CAB7692A618128008FE1EF /* Build configuration list for PBXNativeTarget "Share To SN" */;
|
||||
buildPhases = (
|
||||
07CAB7572A618128008FE1EF /* Sources */,
|
||||
07CAB7582A618128008FE1EF /* Frameworks */,
|
||||
07CAB7592A618128008FE1EF /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Share To SN";
|
||||
productName = "Share To SN";
|
||||
productReference = 07CAB75B2A618128008FE1EF /* Share To SN.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
13B07F861A680F5B00A75B9A /* StandardNotes */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StandardNotes" */;
|
||||
@@ -224,10 +302,12 @@
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
ADF6EC52208176452ADF4217 /* [CP] Embed Pods Frameworks */,
|
||||
BD9D24718C148A593389F498 /* [CP] Copy Pods Resources */,
|
||||
07CAB7662A618128008FE1EF /* Embed Foundation Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
07CAB7642A618128008FE1EF /* PBXTargetDependency */,
|
||||
);
|
||||
name = StandardNotes;
|
||||
productName = StandardNotes;
|
||||
@@ -262,14 +342,18 @@
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1420;
|
||||
LastUpgradeCheck = 1210;
|
||||
TargetAttributes = {
|
||||
00E356ED1AD99517003FC87E = {
|
||||
CreatedOnToolsVersion = 6.2;
|
||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||
};
|
||||
07CAB75A2A618128008FE1EF = {
|
||||
CreatedOnToolsVersion = 14.2;
|
||||
};
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1120;
|
||||
LastSwiftMigration = 1420;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -289,6 +373,7 @@
|
||||
13B07F861A680F5B00A75B9A /* StandardNotes */,
|
||||
00E356ED1AD99517003FC87E /* StandardNotesTests */,
|
||||
CD7D5ECB278015D2005FE1BF /* StandardNotesDev */,
|
||||
07CAB75A2A618128008FE1EF /* Share To SN */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -301,6 +386,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
07CAB7592A618128008FE1EF /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
07CAB7612A618128008FE1EF /* MainInterface.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -570,11 +663,21 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
07CAB7572A618128008FE1EF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
07CAB75E2A618128008FE1EF /* ShareViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CDC75796292552080019F4AF /* AppDelegate.mm in Sources */,
|
||||
07CAB76E2A618353008FE1EF /* ReceiveSharingIntent.swift in Sources */,
|
||||
07CAB7702A618385008FE1EF /* ReceiveSharingIntent.m in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -596,13 +699,30 @@
|
||||
target = 13B07F861A680F5B00A75B9A /* StandardNotes */;
|
||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
||||
};
|
||||
07CAB7642A618128008FE1EF /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 07CAB75A2A618128008FE1EF /* Share To SN */;
|
||||
targetProxy = 07CAB7632A618128008FE1EF /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
07CAB75F2A618128008FE1EF /* MainInterface.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
07CAB7602A618128008FE1EF /* Base */,
|
||||
);
|
||||
name = MainInterface.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
00E356F61AD99517003FC87E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 5915B3231CE7C52662278306 /* Pods-StandardNotes-StandardNotesTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
@@ -630,6 +750,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 321F15E603CF0AF8B1769447 /* Pods-StandardNotes-StandardNotesTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
INFOPLIST_FILE = StandardNotesTests/Info.plist;
|
||||
@@ -650,13 +771,97 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
07CAB7672A618128008FE1EF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Share To SN/Share To SNDebug.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = HKF9BXSN95;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Share To SN/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Share To SN";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.standardnotes.standardnotes.Share-To-SN";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
07CAB7682A618128008FE1EF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = "Share To SN/Share To SNRelease.entitlements";
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKF9BXSN95;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "Share To SN/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Share To SN";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.standardnotes.standardnotes.Share-To-SN";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.standardnotes.standardnotes.Share-To-SN";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.standardnotes.standardnotes.Share-To-SN";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 66417CEB7622E77D89928FCA /* Pods-StandardNotes.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotes.entitlements;
|
||||
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotesDebug.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -676,6 +881,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.standardnotes.standardnotes;
|
||||
PRODUCT_NAME = StandardNotes;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "StandardNotes-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -687,6 +893,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 948EE90E15EA48C27577820B /* Pods-StandardNotes.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotes.entitlements;
|
||||
@@ -710,6 +917,7 @@
|
||||
PRODUCT_NAME = StandardNotes;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.standardnotes.standardnotes";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.standardnotes.standardnotes";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "StandardNotes-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -916,6 +1124,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
07CAB7692A618128008FE1EF /* Build configuration list for PBXNativeTarget "Share To SN" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
07CAB7672A618128008FE1EF /* Debug */,
|
||||
07CAB7682A618128008FE1EF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StandardNotes" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import <WebKit/WKWebsiteDataStore.h>
|
||||
#import <TrustKit/TrustKit.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||
{
|
||||
return [RCTLinkingManager application:application openURL:url options:options];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
|
||||
[self configurePinning];
|
||||
|
||||
|
||||
[self disableUrlCache];
|
||||
|
||||
|
||||
[self clearWebEditorCache];
|
||||
|
||||
|
||||
NSString *CFBundleIdentifier = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
|
||||
|
||||
|
||||
NSDictionary * initialProperties = @{@"env" : [CFBundleIdentifier isEqualToString:@"com.standardnotes.standardnotes.dev"] ? @"dev" : @"prod"};
|
||||
|
||||
|
||||
self.moduleName = @"StandardNotes";
|
||||
self.initialProps = @{};
|
||||
|
||||
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
@@ -50,12 +58,12 @@
|
||||
if(![currentVersion isEqualToString:lastVersionClear]) {
|
||||
// UIWebView
|
||||
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
||||
|
||||
|
||||
// WebKit
|
||||
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
|
||||
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
|
||||
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{}];
|
||||
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:lastVersionClearKey];
|
||||
}
|
||||
}
|
||||
@@ -65,17 +73,17 @@
|
||||
NSDictionary *trustKitConfig =
|
||||
@{
|
||||
kTSKSwizzleNetworkDelegates: @YES,
|
||||
|
||||
|
||||
// The list of domains we want to pin and their configuration
|
||||
kTSKPinnedDomains: @{
|
||||
@"standardnotes.org" : @{
|
||||
kTSKIncludeSubdomains:@YES,
|
||||
|
||||
|
||||
kTSKEnforcePinning:@YES,
|
||||
|
||||
|
||||
// Send reports for pin validation failures so we can track them
|
||||
kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"],
|
||||
|
||||
|
||||
// The pinned public keys' Subject Public Key Info hashes
|
||||
kTSKPublicKeyHashes : @[
|
||||
@"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=",
|
||||
@@ -90,12 +98,12 @@
|
||||
},
|
||||
@"standardnotes.com" : @{
|
||||
kTSKIncludeSubdomains:@YES,
|
||||
|
||||
|
||||
kTSKEnforcePinning:@YES,
|
||||
|
||||
|
||||
// Send reports for pin validation failures so we can track them
|
||||
kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"],
|
||||
|
||||
|
||||
// The pinned public keys' Subject Public Key Info hashes
|
||||
kTSKPublicKeyHashes : @[
|
||||
@"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=",
|
||||
@@ -110,7 +118,7 @@
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
[TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,18 @@
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict/>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>http</string>
|
||||
|
||||
@@ -8,5 +8,9 @@
|
||||
</array>
|
||||
<key>com.apple.developer.default-data-protection</key>
|
||||
<string>NSFileProtectionComplete</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.standardnotes.standardnotes</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>webcredentials:app.standardnotes.com</string>
|
||||
</array>
|
||||
<key>com.apple.developer.default-data-protection</key>
|
||||
<string>NSFileProtectionComplete</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.standardnotes.standardnotes</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.55.12",
|
||||
"version": "3.56.20",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReactNativeToWebEvent } from '@standardnotes/snjs'
|
||||
import { RefObject } from 'react'
|
||||
import { AppState, NativeEventSubscription, NativeModules, Platform } from 'react-native'
|
||||
import { AppState, Linking, NativeEventSubscription, NativeModules, Platform } from 'react-native'
|
||||
import { readFile } from 'react-native-fs'
|
||||
import WebView from 'react-native-webview'
|
||||
const { ReceiveSharingIntent } = NativeModules
|
||||
@@ -13,13 +13,19 @@ type ReceivedItem = {
|
||||
text?: string | null
|
||||
weblink?: string | null
|
||||
subject?: string | null
|
||||
path?: string | null
|
||||
}
|
||||
|
||||
type ReceivedFile = ReceivedItem & {
|
||||
type ReceivedAndroidFile = ReceivedItem & {
|
||||
contentUri: string
|
||||
mimeType: string
|
||||
}
|
||||
|
||||
type ReceivedIosFile = ReceivedItem & {
|
||||
path: string
|
||||
mimeType: string
|
||||
}
|
||||
|
||||
type ReceivedWeblink = ReceivedItem & {
|
||||
weblink: string
|
||||
}
|
||||
@@ -28,10 +34,14 @@ type ReceivedText = ReceivedItem & {
|
||||
text: string
|
||||
}
|
||||
|
||||
const isReceivedFile = (item: ReceivedItem): item is ReceivedFile => {
|
||||
const isReceivedAndroidFile = (item: ReceivedItem): item is ReceivedAndroidFile => {
|
||||
return !!item.contentUri && !!item.mimeType
|
||||
}
|
||||
|
||||
const isReceivedIosFile = (item: ReceivedItem): item is ReceivedIosFile => {
|
||||
return !!item.path
|
||||
}
|
||||
|
||||
const isReceivedWeblink = (item: ReceivedItem): item is ReceivedWeblink => {
|
||||
return !!item.weblink
|
||||
}
|
||||
@@ -40,15 +50,16 @@ const isReceivedText = (item: ReceivedItem): item is ReceivedText => {
|
||||
return !!item.text
|
||||
}
|
||||
|
||||
const BundleIdentifier = 'com.standardnotes.standardnotes'
|
||||
const IosUrlToCheckFor = `${BundleIdentifier}://dataUrl`
|
||||
|
||||
export class ReceivedSharedItemsHandler {
|
||||
private appStateEventSub: NativeEventSubscription | null = null
|
||||
private eventSub: NativeEventSubscription | null = null
|
||||
private receivedItemsQueue: ReceivedItem[] = []
|
||||
private isApplicationLaunched = false
|
||||
|
||||
constructor(private webViewRef: RefObject<WebView>) {
|
||||
if (Platform.OS === 'android') {
|
||||
this.registerNativeEventSub()
|
||||
}
|
||||
this.registerNativeEventSub()
|
||||
}
|
||||
|
||||
setIsApplicationLaunched = (isApplicationLaunched: boolean) => {
|
||||
@@ -61,23 +72,29 @@ export class ReceivedSharedItemsHandler {
|
||||
|
||||
deinit() {
|
||||
this.receivedItemsQueue = []
|
||||
this.appStateEventSub?.remove()
|
||||
this.eventSub?.remove()
|
||||
}
|
||||
|
||||
private registerNativeEventSub = () => {
|
||||
this.appStateEventSub = AppState.addEventListener('change', (state) => {
|
||||
if (state === 'active') {
|
||||
ReceiveSharingIntent.getFileNames()
|
||||
.then(async (filesObject: Record<string, ReceivedItem>) => {
|
||||
const items = Object.values(filesObject)
|
||||
this.receivedItemsQueue.push(...items)
|
||||
if (Platform.OS === 'ios') {
|
||||
Linking.getInitialURL()
|
||||
.then((url) => {
|
||||
if (url && url.startsWith(IosUrlToCheckFor)) {
|
||||
this.addSharedItemsToQueue(url).catch(console.error)
|
||||
}
|
||||
})
|
||||
.catch(console.error)
|
||||
this.eventSub = Linking.addEventListener('url', ({ url }) => {
|
||||
if (url && url.startsWith(IosUrlToCheckFor)) {
|
||||
this.addSharedItemsToQueue(url).catch(console.error)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isApplicationLaunched) {
|
||||
this.handleItemsQueue().catch(console.error)
|
||||
}
|
||||
})
|
||||
.then(() => ReceiveSharingIntent.clearFileNames())
|
||||
.catch(console.error)
|
||||
this.eventSub = AppState.addEventListener('change', (state) => {
|
||||
if (state === 'active') {
|
||||
this.addSharedItemsToQueue().catch(console.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -92,28 +109,16 @@ export class ReceivedSharedItemsHandler {
|
||||
return
|
||||
}
|
||||
|
||||
if (isReceivedFile(item)) {
|
||||
const data = await readFile(item.contentUri, 'base64')
|
||||
const file = {
|
||||
name: item.fileName || item.contentUri,
|
||||
data,
|
||||
mimeType: item.mimeType,
|
||||
}
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedFile,
|
||||
messageType: 'event',
|
||||
messageData: file,
|
||||
}),
|
||||
)
|
||||
if (isReceivedAndroidFile(item) || isReceivedIosFile(item)) {
|
||||
this.sendFileToWebView(item).catch(console.error)
|
||||
} else if (isReceivedWeblink(item)) {
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedText,
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedLink,
|
||||
messageType: 'event',
|
||||
messageData: {
|
||||
title: item.subject || item.weblink,
|
||||
text: item.weblink,
|
||||
link: item.weblink,
|
||||
},
|
||||
}),
|
||||
)
|
||||
@@ -131,4 +136,65 @@ export class ReceivedSharedItemsHandler {
|
||||
|
||||
this.handleItemsQueue().catch(console.error)
|
||||
}
|
||||
|
||||
sendFileToWebView = async (item: ReceivedAndroidFile | ReceivedIosFile) => {
|
||||
const path = isReceivedAndroidFile(item) ? item.contentUri : item.path
|
||||
const data = await readFile(path, 'base64')
|
||||
const file = {
|
||||
name: item.fileName || item.path,
|
||||
data,
|
||||
mimeType: item.mimeType,
|
||||
}
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedFile,
|
||||
messageType: 'event',
|
||||
messageData: file,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private addSharedItemsToQueue = async (url?: string) => {
|
||||
const received =
|
||||
Platform.OS === 'ios' ? await ReceiveSharingIntent.getFileNames(url) : await ReceiveSharingIntent.getFileNames()
|
||||
ReceiveSharingIntent.clearFileNames()
|
||||
|
||||
if (!received) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
const items = Object.values(received as Record<string, ReceivedItem>)
|
||||
this.receivedItemsQueue.push(...items)
|
||||
} else if (typeof received === 'string') {
|
||||
const parsed: unknown = JSON.parse(received)
|
||||
if (typeof parsed !== 'object') {
|
||||
return
|
||||
}
|
||||
if (!parsed) {
|
||||
return
|
||||
}
|
||||
if ('media' in parsed && Array.isArray(parsed.media)) {
|
||||
this.receivedItemsQueue.push(...parsed.media)
|
||||
}
|
||||
if ('text' in parsed && Array.isArray(parsed.text)) {
|
||||
this.receivedItemsQueue.push(
|
||||
...parsed.text.map((text: string) => ({
|
||||
text: text,
|
||||
})),
|
||||
)
|
||||
}
|
||||
if ('urls' in parsed && Array.isArray(parsed.urls)) {
|
||||
this.receivedItemsQueue.push(
|
||||
...parsed.urls.map((url: string) => ({
|
||||
weblink: url,
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isApplicationLaunched) {
|
||||
this.handleItemsQueue().catch(console.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.46.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [1.46.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [1.46.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.46.10",
|
||||
"version": "1.46.12",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ export class DecryptedItemMutator<
|
||||
this.mutableContent = mutableCopy
|
||||
}
|
||||
|
||||
public override getResult() {
|
||||
public override getResult(): DecryptedPayloadInterface<C> {
|
||||
if (this.type === MutationType.NonDirtying) {
|
||||
return this.immutablePayload.copy({
|
||||
content: this.mutableContent,
|
||||
|
||||
@@ -3,7 +3,6 @@ export enum AsymmetricMessagePayloadType {
|
||||
SharedVaultRootKeyChanged = 'shared-vault-root-key-changed',
|
||||
SenderKeypairChanged = 'sender-keypair-changed',
|
||||
SharedVaultMetadataChanged = 'shared-vault-metadata-changed',
|
||||
|
||||
/**
|
||||
* Shared Vault Invites conform to the asymmetric message protocol, but are sent via the dedicated
|
||||
* SharedVaultInvite model and not the AsymmetricMessage model on the server side.
|
||||
|
||||
+8
-2
@@ -1,13 +1,19 @@
|
||||
import { KeySystemRootKeyContentSpecialized } from '../../../Syncable/KeySystemRootKey/KeySystemRootKeyContent'
|
||||
import { TrustedContactContentSpecialized } from '../../../Syncable/TrustedContact/TrustedContactContent'
|
||||
import { ContactPublicKeySetJsonInterface } from '../../../Syncable/TrustedContact/PublicKeySet/ContactPublicKeySetJsonInterface'
|
||||
import { AsymmetricMessageDataCommon } from '../AsymmetricMessageDataCommon'
|
||||
import { AsymmetricMessagePayloadType } from '../AsymmetricMessagePayloadType'
|
||||
|
||||
export type VaultInviteDelegatedContact = {
|
||||
name?: string
|
||||
contactUuid: string
|
||||
publicKeySet: ContactPublicKeySetJsonInterface
|
||||
}
|
||||
|
||||
export type AsymmetricMessageSharedVaultInvite = {
|
||||
type: AsymmetricMessagePayloadType.SharedVaultInvite
|
||||
data: AsymmetricMessageDataCommon & {
|
||||
rootKey: KeySystemRootKeyContentSpecialized
|
||||
trustedContacts: TrustedContactContentSpecialized[]
|
||||
trustedContacts: VaultInviteDelegatedContact[]
|
||||
metadata: {
|
||||
name: string
|
||||
description?: string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { TrustedContactContentSpecialized } from '../../../Syncable/TrustedContact/TrustedContactContent'
|
||||
import { TrustedContactContentSpecialized } from '../../../Syncable/TrustedContact/Content/TrustedContactContent'
|
||||
import { AsymmetricMessageDataCommon } from '../AsymmetricMessageDataCommon'
|
||||
import { AsymmetricMessagePayloadType } from '../AsymmetricMessagePayloadType'
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ItemContent } from '../../../Abstract/Content/ItemContent'
|
||||
import { ContactPublicKeySetJsonInterface } from '../PublicKeySet/ContactPublicKeySetJsonInterface'
|
||||
|
||||
export type TrustedContactContentSpecialized = {
|
||||
name: string
|
||||
contactUuid: string
|
||||
publicKeySet: ContactPublicKeySetJsonInterface
|
||||
isMe: boolean
|
||||
}
|
||||
|
||||
export type TrustedContactContent = TrustedContactContentSpecialized & ItemContent
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { DecryptedPayload, PayloadTimestampDefaults } from '../../../Abstract/Payload'
|
||||
import { TrustedContact } from '../TrustedContact'
|
||||
import { TrustedContactInterface } from '../TrustedContactInterface'
|
||||
import { TrustedContactMutator } from './TrustedContactMutator'
|
||||
import { ContactPublicKeySet } from '../PublicKeySet/ContactPublicKeySet'
|
||||
import { FillItemContentSpecialized } from '../../../Abstract/Content/ItemContent'
|
||||
import { TrustedContactContentSpecialized } from '../Content/TrustedContactContent'
|
||||
import { MutationType } from '../../../Abstract/Item'
|
||||
import { PortablePublicKeySet } from '../Types/PortablePublicKeySet'
|
||||
import { ContactPublicKeySetInterface } from '../PublicKeySet/ContactPublicKeySetInterface'
|
||||
|
||||
function createMockPublicKeySetChain(): ContactPublicKeySetInterface {
|
||||
const nMinusOne = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key-n-1',
|
||||
signing: 'signing-public-key-n-1',
|
||||
timestamp: new Date(-1),
|
||||
previousKeySet: undefined,
|
||||
})
|
||||
|
||||
const root = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key',
|
||||
signing: 'signing-public-key',
|
||||
timestamp: new Date(),
|
||||
previousKeySet: nMinusOne,
|
||||
})
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
describe('TrustedContactMutator', () => {
|
||||
let contact: TrustedContactInterface
|
||||
let mutator: TrustedContactMutator
|
||||
|
||||
beforeEach(() => {
|
||||
contact = new TrustedContact(
|
||||
new DecryptedPayload({
|
||||
uuid: 'item-uuid',
|
||||
content_type: ContentType.TYPES.TrustedContact,
|
||||
...PayloadTimestampDefaults(),
|
||||
content: FillItemContentSpecialized<TrustedContactContentSpecialized, TrustedContactInterface>({
|
||||
name: 'test',
|
||||
contactUuid: 'contact-uuid',
|
||||
isMe: true,
|
||||
publicKeySet: createMockPublicKeySetChain(),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
|
||||
mutator = new TrustedContactMutator(contact, MutationType.UpdateUserTimestamps)
|
||||
})
|
||||
|
||||
it('should set name', () => {
|
||||
mutator.name = 'new name'
|
||||
|
||||
const result = mutator.getResult()
|
||||
|
||||
expect(result.content.name).toEqual('new name')
|
||||
})
|
||||
|
||||
it('should add public key', () => {
|
||||
const currentKeySet = contact.publicKeySet
|
||||
|
||||
const newKeySet: PortablePublicKeySet = {
|
||||
encryption: 'new-encryption-public-key',
|
||||
signing: 'new-signing-public-key',
|
||||
}
|
||||
|
||||
mutator.addPublicKey(newKeySet)
|
||||
|
||||
const result = new TrustedContact(mutator.getResult())
|
||||
|
||||
expect(result.publicKeySet.encryption).toEqual(newKeySet.encryption)
|
||||
expect(result.publicKeySet.signing).toEqual(newKeySet.signing)
|
||||
expect(result.publicKeySet.previousKeySet).toEqual(currentKeySet)
|
||||
})
|
||||
|
||||
it('should replace public key set', () => {
|
||||
const replacement = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key-replacement',
|
||||
signing: 'signing-public-key-replacement',
|
||||
timestamp: new Date(),
|
||||
previousKeySet: undefined,
|
||||
})
|
||||
|
||||
mutator.replacePublicKeySet(replacement.asJson())
|
||||
|
||||
const result = new TrustedContact(mutator.getResult())
|
||||
|
||||
expect(result.publicKeySet.encryption).toEqual(replacement.encryption)
|
||||
expect(result.publicKeySet.signing).toEqual(replacement.signing)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
import { DecryptedItemMutator } from '../../../Abstract/Item'
|
||||
import { TrustedContactContent } from '../Content/TrustedContactContent'
|
||||
import { TrustedContactInterface } from '../TrustedContactInterface'
|
||||
import { ContactPublicKeySet } from '../PublicKeySet/ContactPublicKeySet'
|
||||
import { PortablePublicKeySet } from '../Types/PortablePublicKeySet'
|
||||
import { ContactPublicKeySetJsonInterface } from '../PublicKeySet/ContactPublicKeySetJsonInterface'
|
||||
|
||||
export class TrustedContactMutator extends DecryptedItemMutator<TrustedContactContent, TrustedContactInterface> {
|
||||
set name(newName: string) {
|
||||
this.mutableContent.name = newName
|
||||
}
|
||||
|
||||
addPublicKey(keySet: PortablePublicKeySet): void {
|
||||
const newKey = new ContactPublicKeySet({
|
||||
encryption: keySet.encryption,
|
||||
signing: keySet.signing,
|
||||
timestamp: new Date(),
|
||||
previousKeySet: this.immutableItem.publicKeySet,
|
||||
})
|
||||
|
||||
this.mutableContent.publicKeySet = newKey
|
||||
}
|
||||
|
||||
replacePublicKeySet(publicKeySet: ContactPublicKeySetJsonInterface): void {
|
||||
this.mutableContent.publicKeySet = publicKeySet
|
||||
}
|
||||
}
|
||||
+35
-36
@@ -5,36 +5,18 @@ export class ContactPublicKeySet implements ContactPublicKeySetInterface {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
isRevoked: boolean
|
||||
previousKeySet?: ContactPublicKeySet
|
||||
previousKeySet?: ContactPublicKeySetInterface
|
||||
|
||||
constructor(
|
||||
encryption: string,
|
||||
signing: string,
|
||||
timestamp: Date,
|
||||
isRevoked: boolean,
|
||||
previousKeySet: ContactPublicKeySet | undefined,
|
||||
) {
|
||||
this.encryption = encryption
|
||||
this.signing = signing
|
||||
this.timestamp = timestamp
|
||||
this.isRevoked = isRevoked
|
||||
this.previousKeySet = previousKeySet
|
||||
}
|
||||
|
||||
public findKeySet(params: {
|
||||
targetEncryptionPublicKey: string
|
||||
targetSigningPublicKey: string
|
||||
}): ContactPublicKeySetInterface | undefined {
|
||||
if (this.encryption === params.targetEncryptionPublicKey && this.signing === params.targetSigningPublicKey) {
|
||||
return this
|
||||
}
|
||||
|
||||
if (this.previousKeySet) {
|
||||
return this.previousKeySet.findKeySet(params)
|
||||
}
|
||||
|
||||
return undefined
|
||||
constructor(dto: {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
previousKeySet: ContactPublicKeySetInterface | undefined
|
||||
}) {
|
||||
this.encryption = dto.encryption
|
||||
this.signing = dto.signing
|
||||
this.timestamp = dto.timestamp
|
||||
this.previousKeySet = dto.previousKeySet
|
||||
}
|
||||
|
||||
public findKeySetWithSigningKey(signingKey: string): ContactPublicKeySetInterface | undefined {
|
||||
@@ -61,13 +43,30 @@ export class ContactPublicKeySet implements ContactPublicKeySetInterface {
|
||||
return undefined
|
||||
}
|
||||
|
||||
asJson(): ContactPublicKeySetJsonInterface {
|
||||
return {
|
||||
encryption: this.encryption,
|
||||
signing: this.signing,
|
||||
timestamp: this.timestamp,
|
||||
previousKeySet: this.previousKeySet ? this.previousKeySet.asJson() : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
mutableCopy(): ContactPublicKeySetInterface {
|
||||
return new ContactPublicKeySet({
|
||||
encryption: this.encryption,
|
||||
signing: this.signing,
|
||||
timestamp: this.timestamp,
|
||||
previousKeySet: this.previousKeySet ? ContactPublicKeySet.FromJson(this.previousKeySet.asJson()) : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
static FromJson(json: ContactPublicKeySetJsonInterface): ContactPublicKeySetInterface {
|
||||
return new ContactPublicKeySet(
|
||||
json.encryption,
|
||||
json.signing,
|
||||
new Date(json.timestamp),
|
||||
json.isRevoked,
|
||||
json.previousKeySet ? ContactPublicKeySet.FromJson(json.previousKeySet) : undefined,
|
||||
)
|
||||
return new ContactPublicKeySet({
|
||||
encryption: json.encryption,
|
||||
signing: json.signing,
|
||||
timestamp: new Date(json.timestamp),
|
||||
previousKeySet: json.previousKeySet ? ContactPublicKeySet.FromJson(json.previousKeySet) : undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,15 +1,14 @@
|
||||
import { ContactPublicKeySetJsonInterface } from './ContactPublicKeySetJsonInterface'
|
||||
|
||||
export interface ContactPublicKeySetInterface {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
isRevoked: boolean
|
||||
previousKeySet?: ContactPublicKeySetInterface
|
||||
|
||||
findKeySet(params: {
|
||||
targetEncryptionPublicKey: string
|
||||
targetSigningPublicKey: string
|
||||
}): ContactPublicKeySetInterface | undefined
|
||||
|
||||
findKeySetWithPublicKey(publicKey: string): ContactPublicKeySetInterface | undefined
|
||||
findKeySetWithSigningKey(signingKey: string): ContactPublicKeySetInterface | undefined
|
||||
|
||||
asJson(): ContactPublicKeySetJsonInterface
|
||||
mutableCopy(): ContactPublicKeySetInterface
|
||||
}
|
||||
|
||||
-1
@@ -2,6 +2,5 @@ export interface ContactPublicKeySetJsonInterface {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
isRevoked: boolean
|
||||
previousKeySet?: ContactPublicKeySetJsonInterface
|
||||
}
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
import { ContactPublicKeySetInterface } from './ContactPublicKeySetInterface'
|
||||
|
||||
export type FindPublicKeySetResult =
|
||||
| {
|
||||
publicKeySet: ContactPublicKeySetInterface
|
||||
current: boolean
|
||||
}
|
||||
| undefined
|
||||
@@ -0,0 +1,109 @@
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { DecryptedPayload, PayloadTimestampDefaults } from '../../Abstract/Payload'
|
||||
import { ContactPublicKeySet } from './PublicKeySet/ContactPublicKeySet'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
import { TrustedContact } from './TrustedContact'
|
||||
import { TrustedContactInterface } from './TrustedContactInterface'
|
||||
import { FillItemContentSpecialized } from '../../Abstract/Content/ItemContent'
|
||||
import { ConflictStrategy } from '../../Abstract/Item'
|
||||
import { TrustedContactContentSpecialized } from './Content/TrustedContactContent'
|
||||
import { PublicKeyTrustStatus } from './Types/PublicKeyTrustStatus'
|
||||
|
||||
function createMockPublicKeySetChain(): ContactPublicKeySetInterface {
|
||||
const nMinusOne = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key-n-1',
|
||||
signing: 'signing-public-key-n-1',
|
||||
timestamp: new Date(-1),
|
||||
previousKeySet: undefined,
|
||||
})
|
||||
|
||||
const root = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key',
|
||||
signing: 'signing-public-key',
|
||||
timestamp: new Date(),
|
||||
previousKeySet: nMinusOne,
|
||||
})
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
function CreateContact(params: {
|
||||
name?: string
|
||||
contactUuid?: string
|
||||
isMe?: boolean
|
||||
publicKeySet?: ContactPublicKeySetInterface
|
||||
}): TrustedContactInterface {
|
||||
return new TrustedContact(
|
||||
new DecryptedPayload({
|
||||
uuid: 'item-uuid',
|
||||
content_type: ContentType.TYPES.TrustedContact,
|
||||
...PayloadTimestampDefaults(),
|
||||
content: FillItemContentSpecialized<TrustedContactContentSpecialized, TrustedContactInterface>({
|
||||
name: params.name ?? 'test',
|
||||
contactUuid: params.contactUuid ?? 'contact-uuid',
|
||||
isMe: params.isMe ?? false,
|
||||
publicKeySet: params.publicKeySet ?? createMockPublicKeySetChain(),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
describe('isSingleton', () => {
|
||||
it('should be true', () => {
|
||||
const contact = CreateContact({})
|
||||
|
||||
expect(contact.isSingleton).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('strategyWhenConflictingWithItem', () => {
|
||||
it('should be KeepBase', () => {
|
||||
const contact = CreateContact({})
|
||||
|
||||
expect(contact.strategyWhenConflictingWithItem({} as unknown as TrustedContactInterface)).toEqual(
|
||||
ConflictStrategy.KeepBase,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('TrustedContact', () => {
|
||||
describe('getTrustStatusForPublicKey', () => {
|
||||
it('should be trusted if key set is root', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForPublicKey('encryption-public-key')).toEqual(PublicKeyTrustStatus.Trusted)
|
||||
})
|
||||
|
||||
it('should be semi-trusted if key set is previous', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForPublicKey('encryption-public-key-n-1')).toEqual(PublicKeyTrustStatus.Previous)
|
||||
})
|
||||
|
||||
it('should return not trusted if public key is not found', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForPublicKey('not-found-public-key')).toEqual(PublicKeyTrustStatus.NotTrusted)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getTrustStatusForSigningPublicKey', () => {
|
||||
it('should be trusted if key set is root', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForSigningPublicKey('signing-public-key')).toEqual(PublicKeyTrustStatus.Trusted)
|
||||
})
|
||||
|
||||
it('should be semi-trusted if key set is previous', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForSigningPublicKey('signing-public-key-n-1')).toEqual(PublicKeyTrustStatus.Previous)
|
||||
})
|
||||
|
||||
it('should return not trusted if public key is not found', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForSigningPublicKey('not-found-public-key')).toEqual(PublicKeyTrustStatus.NotTrusted)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ConflictStrategy, DecryptedItem, DecryptedItemInterface } from '../../Abstract/Item'
|
||||
import { DecryptedPayloadInterface } from '../../Abstract/Payload'
|
||||
import { HistoryEntryInterface } from '../../Runtime/History'
|
||||
import { TrustedContactContent } from './TrustedContactContent'
|
||||
import { TrustedContactContent } from './Content/TrustedContactContent'
|
||||
import { TrustedContactInterface } from './TrustedContactInterface'
|
||||
import { FindPublicKeySetResult } from './PublicKeySet/FindPublicKeySetResult'
|
||||
import { ContactPublicKeySet } from './PublicKeySet/ContactPublicKeySet'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
import { Predicate } from '../../Runtime/Predicate/Predicate'
|
||||
import { PublicKeyTrustStatus } from './Types/PublicKeyTrustStatus'
|
||||
|
||||
export class TrustedContact extends DecryptedItem<TrustedContactContent> implements TrustedContactInterface {
|
||||
static singletonPredicate = new Predicate<TrustedContact>('isMe', '=', true)
|
||||
@@ -33,39 +33,36 @@ export class TrustedContact extends DecryptedItem<TrustedContactContent> impleme
|
||||
return TrustedContact.singletonPredicate
|
||||
}
|
||||
|
||||
public findKeySet(params: {
|
||||
targetEncryptionPublicKey: string
|
||||
targetSigningPublicKey: string
|
||||
}): FindPublicKeySetResult {
|
||||
const set = this.publicKeySet.findKeySet(params)
|
||||
if (!set) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
publicKeySet: set,
|
||||
current: set === this.publicKeySet,
|
||||
}
|
||||
hasCurrentOrPreviousSigningPublicKey(signingPublicKey: string): boolean {
|
||||
return this.publicKeySet.findKeySetWithSigningKey(signingPublicKey) !== undefined
|
||||
}
|
||||
|
||||
isPublicKeyTrusted(encryptionPublicKey: string): boolean {
|
||||
const keySet = this.publicKeySet.findKeySetWithPublicKey(encryptionPublicKey)
|
||||
|
||||
if (keySet && !keySet.isRevoked) {
|
||||
return true
|
||||
getTrustStatusForPublicKey(encryptionPublicKey: string): PublicKeyTrustStatus {
|
||||
if (this.publicKeySet.encryption === encryptionPublicKey) {
|
||||
return PublicKeyTrustStatus.Trusted
|
||||
}
|
||||
|
||||
return false
|
||||
const previous = this.publicKeySet.findKeySetWithPublicKey(encryptionPublicKey)
|
||||
|
||||
if (previous) {
|
||||
return PublicKeyTrustStatus.Previous
|
||||
}
|
||||
|
||||
return PublicKeyTrustStatus.NotTrusted
|
||||
}
|
||||
|
||||
isSigningKeyTrusted(signingKey: string): boolean {
|
||||
const keySet = this.publicKeySet.findKeySetWithSigningKey(signingKey)
|
||||
|
||||
if (keySet && !keySet.isRevoked) {
|
||||
return true
|
||||
getTrustStatusForSigningPublicKey(signingPublicKey: string): PublicKeyTrustStatus {
|
||||
if (this.publicKeySet.signing === signingPublicKey) {
|
||||
return PublicKeyTrustStatus.Trusted
|
||||
}
|
||||
|
||||
return false
|
||||
const previous = this.publicKeySet.findKeySetWithSigningKey(signingPublicKey)
|
||||
|
||||
if (previous) {
|
||||
return PublicKeyTrustStatus.Previous
|
||||
}
|
||||
|
||||
return PublicKeyTrustStatus.NotTrusted
|
||||
}
|
||||
|
||||
override strategyWhenConflictingWithItem(
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { ItemContent } from '../../Abstract/Content/ItemContent'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
|
||||
export type TrustedContactContentSpecialized = {
|
||||
name: string
|
||||
contactUuid: string
|
||||
publicKeySet: ContactPublicKeySetInterface
|
||||
isMe: boolean
|
||||
}
|
||||
|
||||
export type TrustedContactContent = TrustedContactContentSpecialized & ItemContent
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DecryptedItemInterface } from '../../Abstract/Item/Interfaces/DecryptedItem'
|
||||
import { FindPublicKeySetResult } from './PublicKeySet/FindPublicKeySetResult'
|
||||
import { TrustedContactContent } from './TrustedContactContent'
|
||||
import { TrustedContactContent } from './Content/TrustedContactContent'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
import { PublicKeyTrustStatus } from './Types/PublicKeyTrustStatus'
|
||||
|
||||
export interface TrustedContactInterface extends DecryptedItemInterface<TrustedContactContent> {
|
||||
name: string
|
||||
@@ -9,8 +9,7 @@ export interface TrustedContactInterface extends DecryptedItemInterface<TrustedC
|
||||
publicKeySet: ContactPublicKeySetInterface
|
||||
isMe: boolean
|
||||
|
||||
findKeySet(params: { targetEncryptionPublicKey: string; targetSigningPublicKey: string }): FindPublicKeySetResult
|
||||
|
||||
isPublicKeyTrusted(encryptionPublicKey: string): boolean
|
||||
isSigningKeyTrusted(signingKey: string): boolean
|
||||
getTrustStatusForPublicKey(encryptionPublicKey: string): PublicKeyTrustStatus
|
||||
getTrustStatusForSigningPublicKey(signingPublicKey: string): PublicKeyTrustStatus
|
||||
hasCurrentOrPreviousSigningPublicKey(signingPublicKey: string): boolean
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { DecryptedItemMutator } from '../../Abstract/Item'
|
||||
import { TrustedContactContent } from './TrustedContactContent'
|
||||
import { TrustedContactInterface } from './TrustedContactInterface'
|
||||
import { ContactPublicKeySet } from './PublicKeySet/ContactPublicKeySet'
|
||||
|
||||
export class TrustedContactMutator extends DecryptedItemMutator<TrustedContactContent, TrustedContactInterface> {
|
||||
set name(newName: string) {
|
||||
this.mutableContent.name = newName
|
||||
}
|
||||
|
||||
addPublicKey(params: { encryption: string; signing: string }): void {
|
||||
const newKey = new ContactPublicKeySet(
|
||||
params.encryption,
|
||||
params.signing,
|
||||
new Date(),
|
||||
false,
|
||||
this.immutableItem.publicKeySet,
|
||||
)
|
||||
|
||||
this.mutableContent.publicKeySet = newKey
|
||||
}
|
||||
|
||||
replacePublicKeySet(publicKeySet: ContactPublicKeySet): void {
|
||||
this.mutableContent.publicKeySet = publicKeySet
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export type PublicKeySet = {
|
||||
export type PortablePublicKeySet = {
|
||||
encryption: string
|
||||
signing: string
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum PublicKeyTrustStatus {
|
||||
Trusted = 'Trusted',
|
||||
Previous = 'Previous',
|
||||
NotTrusted = 'NotTrusted',
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user