Compare commits

...
Author SHA1 Message Date
StandardNotes CI e698b1c990 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].3
2023-07-21 10:54:22 +00:00
Aman Harwara ab2e88a710 fix: Shared image links are now correctly handled on iOS 2023-07-21 15:53:36 +05:30
StandardNotes CI 68db05581b chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected].2
2023-07-20 15:26:51 +00:00
Aman Harwara 7acf23033d chore: fix blank screen issue on iOS web app 2023-07-20 20:23:31 +05:30
StandardNotes CI 78b87672de chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2023-07-19 14:36:29 +00:00
Aman Harwara 2b0df30ca2 fix: Fixed issue where trying to create new folder on mobile would close navigation pane 2023-07-19 19:35:10 +05:30
StandardNotes CI 58cf0e542f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2023-07-19 07:34:30 +00:00
Aman Harwara edead7e251 feat: Added Undo/Redo buttons to Super mobile toolbar 2023-07-19 12:37:02 +05:30
StandardNotes CI 166b4f3a5d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-07-18 14:56:13 +00:00
Aman Harwara 1566c862a5 chore: fix issue when trying to use offline subscription code on desktop 2023-07-18 19:57:08 +05:30
StandardNotes CI 7df9873871 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-07-18 12:51:18 +00:00
Mo 071b345e93 chore: fix snapcraft ci 2023-07-18 07:24:02 -05:00
StandardNotes CI 072e9f803b chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-07-18 09:26:49 +00:00
Aman Harwara 8ceb6938b3 chore: fix super export option position in menu 2023-07-18 14:14:03 +05:30
StandardNotes CI e7d805b91a chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
2023-07-17 14:57:47 +00:00
Mo eba1289ab9 fix: fixes issue where some accounts were not correctly loading subscription info (#2361) 2023-07-17 09:25:00 -05:00
Mo 7fb33c400c chore: refetch subscription when prefs menu opens (#2360) 2023-07-17 09:04:22 -05:00
Mo a56e1f5398 chore: fix release notes [no ci] 2023-07-17 07:55:51 -05:00
StandardNotes CI bf115b5eca chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
2023-07-16 18:43:53 +00:00
Aman Harwara 5932eb814c fix: Fixed issue where some editors would not open on Android 2023-07-16 23:44:53 +05:30
StandardNotes CI e1f1be96cb chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-07-16 10:44:50 +00:00
Aman Harwara e86dff4e58 chore: release 2023-07-16 15:43:12 +05:30
StandardNotes CI bac665fe1e chore(release): publish
- @standardnotes/[email protected].5
 - @standardnotes/[email protected]
2023-07-16 09:49:29 +00:00
Aman Harwara 10cddeee25 chore: ios project 2023-07-16 14:47:12 +05:30
StandardNotes CI bd7b5a2b99 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-07-15 18:52:03 +00:00
Aman Harwara f64a9b66ca chore: release 2023-07-15 23:50:44 +05:30
Aman Harwara 2662f5abbb chore: ios share provisioning profile 2023-07-15 23:21:13 +05:30
StandardNotes CI 526c70f623 chore(release): publish
- @standardnotes/[email protected].3
 - @standardnotes/[email protected]
2023-07-15 16:16:28 +00:00
Aman Harwara af8aca612a chore: ios ci 2023-07-15 21:17:10 +05:30
StandardNotes CI a890f44cbe chore(release): publish
- @standardnotes/[email protected].2
 - @standardnotes/[email protected]
2023-07-15 14:12:50 +00:00
Aman Harwara 415757cc30 chore: share extension provisioning profile 2023-07-15 19:13:17 +05:30
StandardNotes CI 2a91dd0636 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-07-15 12:04:32 +00:00
Aman Harwara 953870beaf chore: ios ci 2023-07-15 17:03:23 +05:30
StandardNotes CI 2091e4c6aa chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-07-15 10:35:02 +00:00
Aman Harwara 1eb3e35f99 chore: fix android 2023-07-15 15:33:59 +05:30
Aman Harwara cb7f9d33ff chore: ci 2023-07-15 15:26:46 +05:30
Aman Harwara 88ef5887a9 feat: You can now share files & text from other apps into SN on iOS (#2358) 2023-07-15 15:05:49 +05:30
StandardNotes CI 61043aee65 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
2023-07-14 17:10:08 +00:00
Aman Harwara 887da2e452 chore: fix android ci 2023-07-14 22:06:14 +05:30
Mo 1c7a215519 chore: feature status in context of item (#2359) 2023-07-14 11:32:28 -05:00
95 changed files with 1964 additions and 337 deletions
+3 -3
View File
@@ -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
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.26.28",
"version": "1.26.29",
"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>> {
+28 -13
View File
@@ -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
-1
View File
@@ -18,4 +18,3 @@ export * from './User/UserRegistrationRequestParams'
export * from './User/UserUpdateRequestParams'
export * from './UserRequest/UserRequestRequestParams'
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
export * from './SharedVault/SharedVaultMoveType'
+56
View File
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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.1.91](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/clipper
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/clipper",
"description": "Web clipper browser extension for Standard Notes",
"version": "1.1.91",
"version": "1.1.105",
"private": true,
"scripts": {
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
+56
View File
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [3.108.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.108.22",
"version": "3.108.36",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.21.51",
"version": "1.21.52",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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 -2
View File
@@ -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": {
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.28.60",
"version": "1.28.61",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.16.6",
"version": "1.16.7",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+1
View File
@@ -0,0 +1 @@
2.7.6
+72
View File
@@ -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.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
## [3.55.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -5,7 +5,7 @@
source "https://rubygems.org"
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby ">= 2.6.10"
ruby "2.7.6"
gem 'cocoapods', '~> 1.12'
gem 'fastlane'
+1 -1
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.55.11",
"version": "3.56.15",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+102 -36
View File
@@ -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)
}
}
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.46.10",
"version": "1.46.11",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+68
View File
@@ -3,6 +3,74 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.387](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.386](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.385](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.384](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.383](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.382](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.381](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.380](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.379](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.378](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.377](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.376](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.375](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.374](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.373](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.372](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.371](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.370](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.370",
"version": "1.4.387",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.13.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
**Note:** Version bump only for package @standardnotes/responses
## [1.13.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
**Note:** Version bump only for package @standardnotes/responses
+1 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.13.29",
"version": "1.13.30",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -35,7 +35,6 @@
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/features": "workspace:*",
"@standardnotes/security": "^1.7.6",
"reflect-metadata": "^0.1.13"
}
}
@@ -1,4 +1,4 @@
import { ValetTokenOperation } from './ValetTokenOperation'
import { ValetTokenOperation } from '../Temp/ValetTokenOperation'
export type CreateValetTokenPayload = {
operation: ValetTokenOperation
@@ -1 +0,0 @@
export type ValetTokenOperation = 'read' | 'write' | 'delete' | 'move'
@@ -1,4 +1,4 @@
import { Role } from '@standardnotes/security'
import { Role } from '../Temp/Role'
export type DeprecatedResponseMeta = {
auth: {
@@ -1,4 +1,4 @@
import { Role } from '@standardnotes/security'
import { Role } from '../Temp/Role'
export type HttpResponseMeta = {
auth: {
@@ -0,0 +1,4 @@
export type Role = {
uuid: string
name: string
}
@@ -0,0 +1,7 @@
export type Subscription = {
planName: string
endsAt: number
createdAt: number
updatedAt: number
cancelled: boolean
}
@@ -0,0 +1,6 @@
export enum ValetTokenOperation {
Read = 'read',
Write = 'write',
Delete = 'delete',
Move = 'move',
}
@@ -1,4 +1,4 @@
import { Subscription } from '@standardnotes/security'
import { Subscription } from '../Temp/Subscription'
export type GetSubscriptionResponse = {
subscription?: Subscription
+6 -1
View File
@@ -14,6 +14,12 @@ export * from './Auth/SignInResponse'
export * from './Auth/SignOutResponse'
export * from './Auth/User'
/** Temps are awaiting final publish state on server repo */
export * from './Temp/SharedVaultMoveType'
export * from './Temp/Subscription'
export * from './Temp/ValetTokenOperation'
export * from './Temp/Role'
export * from './Error/ClientDisplayableError'
export * from './Files/CloseUploadSessionResponse'
@@ -24,7 +30,6 @@ export * from './Files/DownloadFileChunkResponse'
export * from './Files/StartUploadSessionResponse'
export * from './Files/UploadFileChunkResponse'
export * from './Files/MoveFileResponse'
export * from './Files/ValetTokenOperation'
export * from './Http'
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.63.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
**Note:** Version bump only for package @standardnotes/services
## [1.63.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/services
## [1.63.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
**Note:** Version bump only for package @standardnotes/services
+1 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.63.3",
"version": "1.63.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -24,7 +24,6 @@
"@standardnotes/files": "workspace:^",
"@standardnotes/models": "workspace:^",
"@standardnotes/responses": "workspace:*",
"@standardnotes/security": "^1.7.5",
"@standardnotes/sncrypto-common": "workspace:^",
"@standardnotes/utils": "workspace:*",
"reflect-metadata": "^0.1.13"
@@ -1,4 +1,4 @@
import { Role } from '@standardnotes/security'
import { Role } from '@standardnotes/responses'
export type MetaReceivedData = {
userUuid: string
@@ -10,7 +10,7 @@ import {
isNote,
NoteContent,
} from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses'
import { ClientDisplayableError, ValetTokenOperation } from '@standardnotes/responses'
import {
FilesApiInterface,
FileBackupMetadataFile,
@@ -520,7 +520,7 @@ export class FilesBackupService extends AbstractService implements BackupService
},
})
const token = await this.api.createUserFileValetToken(file.remoteIdentifier, 'read')
const token = await this.api.createUserFileValetToken(file.remoteIdentifier, ValetTokenOperation.Read)
if (token instanceof ClientDisplayableError) {
this.status.removeMessage(messageId)
@@ -1,11 +1,11 @@
import { FeatureIdentifier } from '@standardnotes/features'
import { ComponentInterface } from '@standardnotes/models'
import { ComponentInterface, DecryptedItemInterface } from '@standardnotes/models'
import { FeatureStatus } from './FeatureStatus'
import { SetOfflineFeaturesFunctionResponse } from './SetOfflineFeaturesFunctionResponse'
export interface FeaturesClientInterface {
getFeatureStatus(featureId: FeatureIdentifier): FeatureStatus
getFeatureStatus(featureId: FeatureIdentifier, options?: { inContextOfItem?: DecryptedItemInterface }): FeatureStatus
hasMinimumRole(role: string): boolean
hasFirstPartyOfflineSubscription(): boolean
@@ -1,9 +1,10 @@
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
import {
ClientDisplayableError,
ValetTokenOperation,
isClientDisplayableError,
isErrorResponse,
SharedVaultMoveType,
ValetTokenOperation,
} from '@standardnotes/responses'
import {
FileItem,
@@ -47,12 +48,7 @@ import { AbstractService } from '../Service/AbstractService'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { DecryptItemsKeyWithUserFallback } from '../Encryption/Functions'
import { log, LoggingDomain } from '../Logging'
import {
SharedVaultMoveType,
SharedVaultServer,
SharedVaultServerInterface,
HttpServiceInterface,
} from '@standardnotes/api'
import { SharedVaultServer, SharedVaultServerInterface, HttpServiceInterface } from '@standardnotes/api'
import { ContentType } from '@standardnotes/domain-core'
const OneHundredMb = 100 * 1_000_000
@@ -111,7 +107,7 @@ export class FileService extends AbstractService implements FilesClientInterface
moveOperationType?: SharedVaultMoveType
sharedVaultToSharedVaultMoveTargetUuid?: string
}): Promise<string | ClientDisplayableError> {
if (params.operation !== 'write' && !params.fileUuidRequiredForExistingFiles) {
if (params.operation !== ValetTokenOperation.Write && !params.fileUuidRequiredForExistingFiles) {
throw new Error('File UUID is required for for non-write operations')
}
@@ -139,7 +135,7 @@ export class FileService extends AbstractService implements FilesClientInterface
const valetTokenResult = await this.createSharedVaultValetToken({
sharedVaultUuid: file.shared_vault_uuid ? file.shared_vault_uuid : sharedVault.sharing.sharedVaultUuid,
remoteIdentifier: file.remoteIdentifier,
operation: 'move',
operation: ValetTokenOperation.Move,
fileUuidRequiredForExistingFiles: file.uuid,
moveOperationType: file.shared_vault_uuid ? 'shared-vault-to-shared-vault' : 'user-to-shared-vault',
sharedVaultToSharedVaultMoveTargetUuid: file.shared_vault_uuid ? sharedVault.sharing.sharedVaultUuid : undefined,
@@ -164,7 +160,7 @@ export class FileService extends AbstractService implements FilesClientInterface
const valetTokenResult = await this.createSharedVaultValetToken({
sharedVaultUuid: file.shared_vault_uuid,
remoteIdentifier: file.remoteIdentifier,
operation: 'move',
operation: ValetTokenOperation.Move,
fileUuidRequiredForExistingFiles: file.uuid,
moveOperationType: 'shared-vault-to-user',
})
@@ -190,10 +186,10 @@ export class FileService extends AbstractService implements FilesClientInterface
? await this.createSharedVaultValetToken({
sharedVaultUuid: vault.sharing.sharedVaultUuid,
remoteIdentifier,
operation: 'write',
operation: ValetTokenOperation.Write,
unencryptedFileSizeForUpload: sizeInBytes,
})
: await this.createUserValetToken(remoteIdentifier, 'write', sizeInBytes)
: await this.createUserValetToken(remoteIdentifier, ValetTokenOperation.Write, sizeInBytes)
if (valetTokenResult instanceof ClientDisplayableError) {
return valetTokenResult
@@ -342,10 +338,10 @@ export class FileService extends AbstractService implements FilesClientInterface
? await this.createSharedVaultValetToken({
sharedVaultUuid: file.shared_vault_uuid,
remoteIdentifier: file.remoteIdentifier,
operation: 'read',
operation: ValetTokenOperation.Read,
fileUuidRequiredForExistingFiles: file.uuid,
})
: await this.createUserValetToken(file.remoteIdentifier, 'read')
: await this.createUserValetToken(file.remoteIdentifier, ValetTokenOperation.Read)
if (tokenResult instanceof ClientDisplayableError) {
return tokenResult
@@ -375,10 +371,10 @@ export class FileService extends AbstractService implements FilesClientInterface
? await this.createSharedVaultValetToken({
sharedVaultUuid: file.shared_vault_uuid,
remoteIdentifier: file.remoteIdentifier,
operation: 'delete',
operation: ValetTokenOperation.Delete,
fileUuidRequiredForExistingFiles: file.uuid,
})
: await this.createUserValetToken(file.remoteIdentifier, 'delete')
: await this.createUserValetToken(file.remoteIdentifier, ValetTokenOperation.Delete)
if (tokenResult instanceof ClientDisplayableError) {
return tokenResult
@@ -13,8 +13,12 @@ import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface
import { AbstractService } from '../Service/AbstractService'
import { SubscriptionManagerInterface } from './SubscriptionManagerInterface'
import { AppleIAPReceipt } from './AppleIAPReceipt'
import { AvailableSubscriptions, getErrorFromErrorResponse, isErrorResponse } from '@standardnotes/responses'
import { Subscription } from '@standardnotes/security'
import {
AvailableSubscriptions,
getErrorFromErrorResponse,
isErrorResponse,
Subscription,
} from '@standardnotes/responses'
import { SubscriptionManagerEvent } from './SubscriptionManagerEvent'
export class SubscriptionManager
@@ -170,7 +174,7 @@ export class SubscriptionManager
}
}
private async fetchOnlineSubscription(): Promise<void> {
public async fetchOnlineSubscription(): Promise<void> {
if (!this.sessions.isSignedIn()) {
return
}
@@ -1,8 +1,7 @@
import { ApplicationServiceInterface } from './../Service/ApplicationServiceInterface'
import { Invitation } from '@standardnotes/models'
import { AppleIAPReceipt } from './AppleIAPReceipt'
import { AvailableSubscriptions } from '@standardnotes/responses'
import { Subscription } from '@standardnotes/security'
import { AvailableSubscriptions, Subscription } from '@standardnotes/responses'
import { SubscriptionManagerEvent } from './SubscriptionManagerEvent'
export interface SubscriptionManagerInterface extends ApplicationServiceInterface<SubscriptionManagerEvent, unknown> {
@@ -15,6 +14,7 @@ export interface SubscriptionManagerInterface extends ApplicationServiceInterfac
get isUserSubscriptionExpired(): boolean
get isUserSubscriptionCanceled(): boolean
fetchOnlineSubscription(): Promise<void>
listSubscriptionInvitations(): Promise<Invitation[]>
inviteToSubscription(inviteeEmail: string): Promise<boolean>
cancelInvitation(inviteUuid: string): Promise<boolean>
+26
View File
@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.202.9](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))
## [2.202.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.7](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))
## [2.202.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
### Bug Fixes
* Fixed issue where some editors would not open on Android ([5932eb8](https://github.com/standardnotes/app/commit/5932eb814cbf81568d435afe134b99441d71f930))
## [2.202.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/snjs
+1 -1
View File
@@ -8,7 +8,7 @@ export function platformFromString(string: string) {
'linux-desktop': Platform.LinuxDesktop,
'windows-web': Platform.WindowsWeb,
'windows-desktop': Platform.WindowsDesktop,
ios: Platform.Ios,
'ios-web': Platform.Ios,
android: Platform.Android,
}
return map[string]
@@ -10,5 +10,6 @@ export enum ReactNativeToWebEvent {
KeyboardWillShow = 'KeyboardWillShow',
KeyboardWillHide = 'KeyboardWillHide',
ReceivedFile = 'ReceivedFile',
ReceivedLink = 'ReceivedLink',
ReceivedText = 'ReceivedText',
}
+1 -3
View File
@@ -66,15 +66,13 @@ import {
HttpErrorResponse,
HttpSuccessResponse,
isErrorResponse,
ValetTokenOperation,
MoveFileResponse,
ValetTokenOperation,
} from '@standardnotes/responses'
import { LegacySession, MapperInterface, Session, SessionToken } from '@standardnotes/domain-core'
import { HttpServiceInterface } from '@standardnotes/api'
import { SNRootKeyParams } from '@standardnotes/encryption'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { isUrlFirstParty, TRUSTED_FEATURE_HOSTS } from '@Lib/Hosts'
import { Paths } from './Paths'
import { DiskStorageService } from '../Storage/DiskStorageService'
@@ -126,24 +126,6 @@ export class SNComponentManager
window.addEventListener('message', this.onWindowMessage, true)
}
get isDesktop(): boolean {
return this.environment === Environment.Desktop
}
get isMobile(): boolean {
return this.environment === Environment.Mobile
}
get thirdPartyComponents(): ComponentInterface[] {
return this.items.getDisplayableComponents()
}
thirdPartyComponentsForArea(area: ComponentArea): ComponentInterface[] {
return this.thirdPartyComponents.filter((component) => {
return component.area === area
})
}
override deinit(): void {
super.deinit()
@@ -172,11 +154,17 @@ export class SNComponentManager
;(this.onWindowMessage as unknown) = undefined
}
setPermissionDialogUIHandler(handler: (dialog: PermissionDialog) => void): void {
public setPermissionDialogUIHandler(handler: (dialog: PermissionDialog) => void): void {
this.permissionDialogUIHandler = handler
this.runWithPermissionsUseCase.setPermissionDialogUIHandler(handler)
}
public thirdPartyComponentsForArea(area: ComponentArea): ComponentInterface[] {
return this.items.getDisplayableComponents().filter((component) => {
return component.area === area
})
}
public createComponentViewer(
component: UIFeature<IframeComponentFeatureDescription>,
item: ComponentViewerItem,
@@ -217,7 +205,7 @@ export class SNComponentManager
removeFromArray(this.viewers, viewer)
}
setDesktopManager(desktopManager: DesktopManagerInterface): void {
public setDesktopManager(desktopManager: DesktopManagerInterface): void {
this.desktopManager = desktopManager
this.configureForDesktop()
}
@@ -234,13 +222,14 @@ export class SNComponentManager
return
}
if (this.isDesktop) {
if (this.desktopManager) {
const thirdPartyComponents = components.filter((component) => {
const nativeFeature = FindNativeFeature(component.identifier)
return nativeFeature ? false : true
})
if (thirdPartyComponents.length > 0) {
this.desktopManager?.syncComponentsInstallation(thirdPartyComponents)
this.desktopManager.syncComponentsInstallation(thirdPartyComponents)
}
}
@@ -304,7 +293,8 @@ export class SNComponentManager
}
detectFocusChange = (): void => {
const activeIframes = this.allComponentIframes()
const activeIframes = Array.from(document.getElementsByTagName('iframe'))
for (const iframe of activeIframes) {
if (document.activeElement === iframe) {
setTimeout(() => {
@@ -322,18 +312,19 @@ export class SNComponentManager
}
onWindowMessage = (event: MessageEvent): void => {
/** Make sure this message is for us */
const data = event.data as ComponentMessage
if (data.sessionKey) {
this.log('Component manager received message', data)
this.componentViewerForSessionKey(data.sessionKey)?.handleMessage(data)
}
}
configureForDesktop(): void {
this.desktopManager?.registerUpdateObserver((component: ComponentInterface) => {
/* Reload theme if active */
private configureForDesktop(): void {
if (!this.desktopManager) {
throw new Error('Desktop manager is not defined')
}
this.desktopManager.registerUpdateObserver((component: ComponentInterface) => {
const activeComponents = this.getActiveComponents()
const isComponentActive = activeComponents.find((candidate) => candidate.uuid === component.uuid)
if (isComponentActive && component.isTheme()) {
@@ -342,18 +333,18 @@ export class SNComponentManager
})
}
postActiveThemesToAllViewers(): void {
private postActiveThemesToAllViewers(): void {
for (const viewer of this.viewers) {
viewer.postActiveThemes()
}
}
urlForFeature(uiFeature: UIFeature<ComponentFeatureDescription>): string | undefined {
public urlForFeature(uiFeature: UIFeature<ComponentFeatureDescription>): string | undefined {
const usecase = new GetFeatureUrl(this.desktopManager, this.environment, this.platform)
return usecase.execute(uiFeature)
}
urlsForActiveThemes(): string[] {
public urlsForActiveThemes(): string[] {
const themes = this.getActiveThemes()
const urls = []
for (const theme of themes) {
@@ -373,7 +364,7 @@ export class SNComponentManager
return this.viewers.find((viewer) => viewer.sessionKey === key)
}
async toggleTheme(uiFeature: UIFeature<ThemeFeatureDescription>): Promise<void> {
public async toggleTheme(uiFeature: UIFeature<ThemeFeatureDescription>): Promise<void> {
this.log('Toggling theme', uiFeature.uniqueIdentifier)
if (this.isThemeActive(uiFeature)) {
@@ -406,7 +397,7 @@ export class SNComponentManager
}
}
getActiveThemes(): UIFeature<ThemeFeatureDescription>[] {
public getActiveThemes(): UIFeature<ThemeFeatureDescription>[] {
const activeThemesIdentifiers = this.getActiveThemesIdentifiers()
const thirdPartyThemes = this.items.findItems<ThemeInterface>(activeThemesIdentifiers).map((item) => {
@@ -427,11 +418,11 @@ export class SNComponentManager
return entitledThemes
}
getActiveThemesIdentifiers(): string[] {
public getActiveThemesIdentifiers(): string[] {
return this.preferences.getValue(PrefKey.ActiveThemes, undefined) ?? []
}
async toggleComponent(component: ComponentInterface): Promise<void> {
public async toggleComponent(component: ComponentInterface): Promise<void> {
this.log('Toggling component', component.uuid)
if (this.isComponentActive(component)) {
@@ -441,14 +432,6 @@ export class SNComponentManager
}
}
allComponentIframes(): HTMLIFrameElement[] {
return Array.from(document.getElementsByTagName('iframe'))
}
iframeForComponentViewer(viewer: ComponentViewer): HTMLIFrameElement | undefined {
return viewer.getIframe()
}
editorForNote(note: SNNote): UIFeature<EditorFeatureDescription | IframeComponentFeatureDescription> {
const usecase = new EditorForNoteUseCase(this.items)
return usecase.execute(note)
@@ -82,13 +82,12 @@ export class ComponentViewer implements ComponentViewerInterface {
private loggingEnabled = false
public identifier = nonSecureRandomIdentifier()
private actionObservers: ActionObserver[] = []
private featureStatus: FeatureStatus
private removeFeaturesObserver: () => void
private eventObservers: ComponentEventObserver[] = []
private dealloced = false
private window?: Window
private hidden = false
private readonly = false
public lockReadonly = false
public sessionKey?: string
@@ -132,20 +131,14 @@ export class ComponentViewer implements ComponentViewerInterface {
this.actionObservers.push(options.actionObserver)
}
this.featureStatus = services.features.getFeatureStatus(componentOrFeature.featureIdentifier)
this.removeFeaturesObserver = services.features.addEventObserver((event) => {
if (this.dealloced) {
return
}
if (event === FeaturesEvent.FeaturesAvailabilityChanged) {
const featureStatus = services.features.getFeatureStatus(componentOrFeature.featureIdentifier)
if (featureStatus !== this.featureStatus) {
this.featureStatus = featureStatus
this.postActiveThemes()
this.notifyEventObservers(ComponentViewerEvent.FeatureStatusUpdated)
}
this.postActiveThemes()
this.notifyEventObservers(ComponentViewerEvent.FeatureStatusUpdated)
}
})
@@ -226,7 +219,19 @@ export class ComponentViewer implements ComponentViewerInterface {
}
public getFeatureStatus(): FeatureStatus {
return this.featureStatus
return this.services.features.getFeatureStatus(this.componentOrFeature.featureIdentifier, {
inContextOfItem: this.getContextItem(),
})
}
private getContextItem(): DecryptedItemInterface | undefined {
if (isComponentViewerItemReadonlyItem(this.options.item)) {
return this.options.item.readonlyItem
}
const matchingItem = this.services.items.findItem(this.options.item.uuid)
return matchingItem
}
private isOfflineRestricted(): boolean {
@@ -274,7 +279,7 @@ export class ComponentViewer implements ComponentViewerInterface {
this.componentOrFeature = item
}
handleChangesInItems(
private handleChangesInItems(
items: (DecryptedItemInterface | DeletedItemInterface | EncryptedItemInterface)[],
source: PayloadEmitSource,
sourceKey?: string,
@@ -312,7 +317,7 @@ export class ComponentViewer implements ComponentViewerInterface {
}
}
sendManyItemsThroughBridge(items: (DecryptedItemInterface | DeletedItemInterface)[]): void {
private sendManyItemsThroughBridge(items: (DecryptedItemInterface | DeletedItemInterface)[]): void {
const requiredPermissions: ComponentPermission[] = [
{
name: ComponentAction.StreamItems,
@@ -329,7 +334,7 @@ export class ComponentViewer implements ComponentViewerInterface {
)
}
sendContextItemThroughBridge(item: DecryptedItemInterface, source?: PayloadEmitSource): void {
private sendContextItemThroughBridge(item: DecryptedItemInterface, source?: PayloadEmitSource): void {
const requiredContextPermissions = [
{
name: ComponentAction.StreamContextItem,
@@ -443,14 +448,7 @@ export class ComponentViewer implements ComponentViewerInterface {
* @param essential If the message is non-essential, no alert will be shown
* if we can no longer find the window.
*/
sendMessage(message: ComponentMessage | MessageReply, essential = true): void {
const permissibleActionsWhileHidden = [ComponentAction.ComponentRegistered, ComponentAction.ActivateThemes]
if (this.hidden && !permissibleActionsWhileHidden.includes(message.action)) {
this.log('Component disabled for current item, ignoring messages.', this.componentOrFeature.displayName)
return
}
private sendMessage(message: ComponentMessage | MessageReply, essential = true): void {
if (!this.window && message.action === ComponentAction.Reply) {
this.log('Component has been deallocated in between message send and reply', this.componentOrFeature, message)
return
@@ -514,10 +512,6 @@ export class ComponentViewer implements ComponentViewerInterface {
})
}
public getWindow(): Window | undefined {
return this.window
}
/** Called by client when the iframe is ready */
public setWindow(window: Window): void {
if (this.window) {
@@ -548,7 +542,7 @@ export class ComponentViewer implements ComponentViewerInterface {
this.postActiveThemes()
}
postActiveThemes(): void {
public postActiveThemes(): void {
const urls = this.config.componentManagerFunctions.urlsForActiveThemes()
const data: MessageData = {
themes: urls,
@@ -562,24 +556,6 @@ export class ComponentViewer implements ComponentViewerInterface {
this.sendMessage(message, false)
}
/* A hidden component will not receive messages. However, when a component is unhidden,
* we need to send it any items it may have registered streaming for. */
public setHidden(hidden: boolean): void {
if (hidden) {
this.hidden = true
} else if (this.hidden) {
this.hidden = false
if (this.streamContextItemOriginalMessage) {
this.handleStreamContextItemMessage(this.streamContextItemOriginalMessage)
}
if (this.streamItems) {
this.handleStreamItemsMessage(this.streamItemsOriginalMessage as ComponentMessage)
}
}
}
handleMessage(message: ComponentMessage): void {
this.log('Handle message', message, this)
if (!this.componentOrFeature) {
@@ -616,7 +592,7 @@ export class ComponentViewer implements ComponentViewerInterface {
}
}
handleStreamItemsMessage(message: ComponentMessage): void {
private handleStreamItemsMessage(message: ComponentMessage): void {
const data = message.data as StreamItemsMessageData
const types = data.content_types.filter((type) => AllowedBatchContentTypes.includes(type)).sort()
const requiredPermissions = [
@@ -643,7 +619,7 @@ export class ComponentViewer implements ComponentViewerInterface {
)
}
handleStreamContextItemMessage(message: ComponentMessage): void {
private handleStreamContextItemMessage(message: ComponentMessage): void {
const requiredPermissions: ComponentPermission[] = [
{
name: ComponentAction.StreamContextItem,
@@ -671,7 +647,7 @@ export class ComponentViewer implements ComponentViewerInterface {
* Save items is capable of saving existing items, and also creating new ones
* if they don't exist.
*/
handleSaveItemsMessage(message: ComponentMessage): void {
private handleSaveItemsMessage(message: ComponentMessage): void {
let responsePayloads = message.data.items as IncomingComponentItemPayload[]
const requiredPermissions = []
@@ -814,7 +790,7 @@ export class ComponentViewer implements ComponentViewerInterface {
)
}
handleCreateItemsMessage(message: ComponentMessage): void {
private handleCreateItemsMessage(message: ComponentMessage): void {
let responseItems = (message.data.item ? [message.data.item] : message.data.items) as IncomingComponentItemPayload[]
const uniqueContentTypes = uniqueArray(
@@ -884,7 +860,7 @@ export class ComponentViewer implements ComponentViewerInterface {
)
}
handleDeleteItemsMessage(message: ComponentMessage): void {
private handleDeleteItemsMessage(message: ComponentMessage): void {
const data = message.data as DeleteItemsMessageData
const items = data.items.filter((item) => AllowedBatchContentTypes.includes(item.content_type))
@@ -932,7 +908,7 @@ export class ComponentViewer implements ComponentViewerInterface {
)
}
handleSetComponentPreferencesMessage(message: ComponentMessage): void {
private handleSetComponentPreferencesMessage(message: ComponentMessage): void {
const noPermissionsRequired: ComponentPermission[] = []
this.config.componentManagerFunctions.runWithPermissionsUseCase.execute(
this.componentUniqueIdentifier,
@@ -949,7 +925,7 @@ export class ComponentViewer implements ComponentViewerInterface {
)
}
handleSetSizeEvent(message: ComponentMessage): void {
private handleSetSizeEvent(message: ComponentMessage): void {
if (this.componentOrFeature.area !== ComponentArea.EditorStack) {
return
}
@@ -967,7 +943,7 @@ export class ComponentViewer implements ComponentViewerInterface {
}
}
getIframe(): HTMLIFrameElement | undefined {
private getIframe(): HTMLIFrameElement | undefined {
return Array.from(document.getElementsByTagName('iframe')).find(
(iframe) => iframe.dataset.componentViewerId === this.identifier,
)
@@ -20,7 +20,10 @@ export class GetFeatureUrl {
}
if (uiFeature.isFeatureDescription) {
return this.urlForNativeComponent(uiFeature.asFeatureDescription)
return this.urlForNativeComponent(
uiFeature.asFeatureDescription.identifier,
uiFeature.asFeatureDescription.index_path,
)
}
if (uiFeature.asComponent.offlineOnly) {
@@ -29,6 +32,13 @@ export class GetFeatureUrl {
const url = uiFeature.asComponent.hosted_url || uiFeature.asComponent.legacy_url
if (!url) {
if (uiFeature.asComponent.package_info.identifier && uiFeature.asComponent.package_info.index_path) {
return this.urlForNativeComponent(
uiFeature.asComponent.package_info.identifier,
uiFeature.asComponent.package_info.index_path,
)
}
return undefined
}
@@ -58,13 +68,16 @@ export class GetFeatureUrl {
}
}
private urlForNativeComponent(feature: ComponentFeatureDescription): string {
private urlForNativeComponent(
identifier: ComponentFeatureDescription['identifier'],
index_path: ComponentFeatureDescription['index_path'],
): string {
if (this.isMobile) {
const baseUrlRequiredForThemesInsideEditors = window.location.href.split('/index.html')[0]
return `${baseUrlRequiredForThemesInsideEditors}/web-src/components/assets/${feature.identifier}/${feature.index_path}`
return `${baseUrlRequiredForThemesInsideEditors}/web-src/components/assets/${identifier}/${index_path}`
} else {
const baseUrlRequiredForThemesInsideEditors = window.location.origin
return `${baseUrlRequiredForThemesInsideEditors}/components/assets/${feature.identifier}/${feature.index_path}`
return `${baseUrlRequiredForThemesInsideEditors}/components/assets/${identifier}/${index_path}`
}
}
@@ -16,6 +16,7 @@ import {
PayloadEmitSource,
ComponentInterface,
ThemeInterface,
DecryptedItemInterface,
} from '@standardnotes/models'
import {
AbstractService,
@@ -389,7 +390,10 @@ export class SNFeaturesService
return indexOfRoleToCheck <= highestUserRoleIndex
}
public getFeatureStatus(featureId: FeatureIdentifier): FeatureStatus {
public getFeatureStatus(
featureId: FeatureIdentifier,
options: { inContextOfItem?: DecryptedItemInterface } = {},
): FeatureStatus {
return this.getFeatureStatusUseCase.execute({
featureId,
firstPartyRoles: this.hasFirstPartyOnlineSubscription()
@@ -401,6 +405,7 @@ export class SNFeaturesService
firstPartyOnlineSubscription: this.hasFirstPartyOnlineSubscription()
? this.subscriptions.getOnlineSubscription()
: undefined,
inContextOfItem: options.inContextOfItem,
})
}
@@ -1,7 +1,7 @@
import { FeatureIdentifier } from '@standardnotes/features'
import { FeatureStatus, ItemManagerInterface } from '@standardnotes/services'
import { GetFeatureStatusUseCase } from './GetFeatureStatus'
import { ComponentInterface } from '@standardnotes/models'
import { ComponentInterface, DecryptedItemInterface } from '@standardnotes/models'
jest.mock('@standardnotes/features', () => ({
FeatureIdentifier: {
@@ -11,7 +11,7 @@ jest.mock('@standardnotes/features', () => ({
}))
import { FindNativeFeature } from '@standardnotes/features'
import { Subscription } from '@standardnotes/security'
import { Subscription } from '@standardnotes/responses'
describe('GetFeatureStatusUseCase', () => {
let items: jest.Mocked<ItemManagerInterface>
@@ -71,6 +71,34 @@ describe('GetFeatureStatusUseCase', () => {
})
describe('native features', () => {
it('should return Entitled if the context item belongs to a shared vault and user does not have subscription', () => {
;(FindNativeFeature as jest.Mock).mockReturnValue({ deprecated: false })
expect(
usecase.execute({
featureId: 'nativeFeature',
firstPartyOnlineSubscription: undefined,
firstPartyRoles: undefined,
hasPaidAnyPartyOnlineOrOfflineSubscription: false,
inContextOfItem: { shared_vault_uuid: 'sharedVaultUuid' } as jest.Mocked<DecryptedItemInterface>,
}),
).toEqual(FeatureStatus.Entitled)
})
it('should return NoUserSubscription if the context item does not belong to a shared vault and user does not have subscription', () => {
;(FindNativeFeature as jest.Mock).mockReturnValue({ deprecated: false })
expect(
usecase.execute({
featureId: 'nativeFeature',
firstPartyOnlineSubscription: undefined,
firstPartyRoles: undefined,
hasPaidAnyPartyOnlineOrOfflineSubscription: false,
inContextOfItem: { shared_vault_uuid: undefined } as jest.Mocked<DecryptedItemInterface>,
}),
).toEqual(FeatureStatus.NoUserSubscription)
})
it('should return NoUserSubscription for native features without subscription and roles', () => {
;(FindNativeFeature as jest.Mock).mockReturnValue({ deprecated: false })
@@ -1,5 +1,6 @@
import { AnyFeatureDescription, FeatureIdentifier, FindNativeFeature } from '@standardnotes/features'
import { Subscription } from '@standardnotes/security'
import { DecryptedItemInterface } from '@standardnotes/models'
import { Subscription } from '@standardnotes/responses'
import { FeatureStatus, ItemManagerInterface } from '@standardnotes/services'
import { convertTimestampToMilliseconds } from '@standardnotes/utils'
@@ -11,6 +12,7 @@ export class GetFeatureStatusUseCase {
firstPartyOnlineSubscription: Subscription | undefined
firstPartyRoles: { online: string[] } | { offline: string[] } | undefined
hasPaidAnyPartyOnlineOrOfflineSubscription: boolean
inContextOfItem?: DecryptedItemInterface
}): FeatureStatus {
if (this.isFreeFeature(dto.featureId as FeatureIdentifier)) {
return FeatureStatus.Entitled
@@ -33,6 +35,7 @@ export class GetFeatureStatusUseCase {
nativeFeature,
firstPartyOnlineSubscription: dto.firstPartyOnlineSubscription,
firstPartyRoles: dto.firstPartyRoles,
inContextOfItem: dto.inContextOfItem,
})
}
@@ -51,7 +54,15 @@ export class GetFeatureStatusUseCase {
nativeFeature: AnyFeatureDescription
firstPartyOnlineSubscription: Subscription | undefined
firstPartyRoles: { online: string[] } | { offline: string[] } | undefined
inContextOfItem?: DecryptedItemInterface
}): FeatureStatus {
if (dto.inContextOfItem) {
const isSharedVaultItem = dto.inContextOfItem.shared_vault_uuid !== undefined
if (isSharedVaultItem) {
return FeatureStatus.Entitled
}
}
if (!dto.firstPartyOnlineSubscription && !dto.firstPartyRoles) {
return FeatureStatus.NoUserSubscription
}
@@ -169,9 +169,7 @@ export class SNSessionManager
private setSession(session: Session | LegacySession, persist = true): void {
this.session = session
if (session instanceof Session) {
this.httpService.setSession(session)
}
this.httpService.setSession(session)
this.apiService.setSession(session, persist)
+17 -5
View File
@@ -51,7 +51,7 @@ describe('files', function () {
await setup({ fakeCrypto: true, subscription: true })
const remoteIdentifier = Utils.generateUuid()
const token = await application.apiService.createUserFileValetToken(remoteIdentifier, 'write')
const token = await application.apiService.createUserFileValetToken(remoteIdentifier, ValetTokenOperation.Write)
expect(token.length).to.be.above(0)
})
@@ -60,7 +60,10 @@ describe('files', function () {
await setup({ fakeCrypto: true, subscription: false })
const remoteIdentifier = Utils.generateUuid()
const tokenOrError = await application.apiService.createUserFileValetToken(remoteIdentifier, 'write')
const tokenOrError = await application.apiService.createUserFileValetToken(
remoteIdentifier,
ValetTokenOperation.Write,
)
expect(isClientDisplayableError(tokenOrError)).to.equal(true)
})
@@ -76,7 +79,10 @@ describe('files', function () {
})
const remoteIdentifier = Utils.generateUuid()
const tokenOrError = await application.apiService.createUserFileValetToken(remoteIdentifier, 'write')
const tokenOrError = await application.apiService.createUserFileValetToken(
remoteIdentifier,
ValetTokenOperation.Write,
)
expect(isClientDisplayableError(tokenOrError)).to.equal(true)
})
@@ -84,12 +90,18 @@ describe('files', function () {
it('creating two upload sessions successively should succeed', async function () {
await setup({ fakeCrypto: true, subscription: true })
const firstToken = await application.apiService.createUserFileValetToken(Utils.generateUuid(), 'write')
const firstToken = await application.apiService.createUserFileValetToken(
Utils.generateUuid(),
ValetTokenOperation.Write,
)
const firstSession = await application.apiService.startUploadSession(firstToken, 'user')
expect(firstSession.uploadId).to.be.ok
const secondToken = await application.apiService.createUserFileValetToken(Utils.generateUuid(), 'write')
const secondToken = await application.apiService.createUserFileValetToken(
Utils.generateUuid(),
ValetTokenOperation.Write,
)
const secondSession = await application.apiService.startUploadSession(secondToken, 'user')
expect(secondSession.uploadId).to.be.ok
+1 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.202.4",
"version": "2.202.9",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -44,7 +44,6 @@
"@standardnotes/files": "workspace:*",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/security": "^1.7.6",
"@standardnotes/services": "workspace:*",
"@standardnotes/settings": "^1.20.0",
"@standardnotes/sncrypto-common": "workspace:*",
+4 -4
View File
@@ -1,6 +1,6 @@
const path = require('path');
const webpack = require('webpack');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const path = require('path')
const webpack = require('webpack')
const CircularDependencyPlugin = require('circular-dependency-plugin')
module.exports = {
entry: {
@@ -57,4 +57,4 @@ module.exports = {
cwd: process.cwd(),
}),
],
};
}
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.7](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))
## [1.28.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.28.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.28.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-13)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.28.4",
"version": "1.28.7",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -18,6 +18,7 @@ export interface WebApplicationInterface extends ApplicationInterface {
handleMobileKeyboardDidChangeFrameEvent(frame: { height: number; contentHeight: number }): void
handleReceivedFileEvent(file: { name: string; mimeType: string; data: string }): void
handleReceivedTextEvent(item: { text: string; title?: string }): Promise<void>
handleReceivedLinkEvent(item: { link: string; title: string }): Promise<void>
isNativeMobileWeb(): boolean
mobileDevice(): MobileDeviceInterface
handleAndroidBackButtonPressed(): void
+64 -2
View File
@@ -3,6 +3,70 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.167.3](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.167.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
**Note:** Version bump only for package @standardnotes/web
## [3.167.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
### Bug Fixes
* Fixed issue where trying to create new folder on mobile would close navigation pane ([2b0df30](https://github.com/standardnotes/app/commit/2b0df30ca25bd15d7e4c279fa0e586ddec08b821))
# [3.167.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
### Features
* Added Undo/Redo buttons to Super mobile toolbar ([edead7e](https://github.com/standardnotes/app/commit/edead7e2511b6c59843bb5b0336ef89fd3240f77))
## [3.166.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
**Note:** Version bump only for package @standardnotes/web
## [3.166.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
**Note:** Version bump only for package @standardnotes/web
## [3.166.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)
**Note:** Version bump only for package @standardnotes/web
## [3.166.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
**Note:** Version bump only for package @standardnotes/web
## [3.166.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
### Bug Fixes
* Fixed issue where some editors would not open on Android ([5932eb8](https://github.com/standardnotes/app/commit/5932eb814cbf81568d435afe134b99441d71f930))
## [3.166.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)
**Note:** Version bump only for package @standardnotes/web
## [3.166.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/web
## [3.166.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/web
## [3.166.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)
**Note:** Version bump only for package @standardnotes/web
## [3.166.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/web
## [3.166.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)
**Note:** Version bump only for package @standardnotes/web
@@ -113,8 +177,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
# [3.164.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-03)
### Features
**Note:** Version bump only for package @standardnotes/web
## [3.163.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-02)
+167 -4
View File
@@ -1,5 +1,171 @@
{
"versions": [
{
"version": "3.167.3",
"title": "[3.167.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)",
"date": null,
"body": "### Bug Fixes\n\n* Shared image links are now correctly handled on iOS ([ab2e88a](https://github.com/standardnotes/app/commit/ab2e88a710c65771312bd9d02cec0b400d352a0e))",
"parsed": {
"_": [
"Shared image links are now correctly handled on iOS (ab2e88a)"
],
"Bug Fixes": [
"Shared image links are now correctly handled on iOS (ab2e88a)"
]
}
},
{
"version": "3.167.2",
"title": "[3.167.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.167.1",
"title": "[3.167.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)",
"date": null,
"body": "### Bug Fixes\n\n* Fixed issue where trying to create new folder on mobile would close navigation pane ([2b0df30](https://github.com/standardnotes/app/commit/2b0df30ca25bd15d7e4c279fa0e586ddec08b821))",
"parsed": {
"_": [
"Fixed issue where trying to create new folder on mobile would close navigation pane (2b0df30)"
],
"Bug Fixes": [
"Fixed issue where trying to create new folder on mobile would close navigation pane (2b0df30)"
]
}
},
{
"version": "3.167.0",
"title": "[3.167.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)",
"date": null,
"body": "### Features\n\n* Added Undo/Redo buttons to Super mobile toolbar ([edead7e](https://github.com/standardnotes/app/commit/edead7e2511b6c59843bb5b0336ef89fd3240f77))",
"parsed": {
"_": [
"Added Undo/Redo buttons to Super mobile toolbar (edead7e)"
],
"Features": [
"Added Undo/Redo buttons to Super mobile toolbar (edead7e)"
]
}
},
{
"version": "3.166.19",
"title": "[3.166.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.18",
"title": "[3.166.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.17",
"title": "[3.166.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-18)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.16",
"title": "[3.166.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.15",
"title": "[3.166.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)",
"date": null,
"body": "### Bug Fixes\n\n* Fixed issue where some editors would not open on Android ([5932eb8](https://github.com/standardnotes/app/commit/5932eb814cbf81568d435afe134b99441d71f930))",
"parsed": {
"_": [
"Fixed issue where some editors would not open on Android (5932eb8)"
],
"Bug Fixes": [
"Fixed issue where some editors would not open on Android (5932eb8)"
]
}
},
{
"version": "3.166.14",
"title": "[3.166.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-16)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.13",
"title": "[3.166.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.12",
"title": "[3.166.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.11",
"title": "[3.166.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-15)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.10",
"title": "[3.166.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.166.9",
"title": "[3.166.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-14)",
@@ -296,13 +462,10 @@
"version": "3.164.0",
"title": "[3.164.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-03)",
"date": null,
"body": "### Features\n\n**Note:** Version bump only for package @standardnotes/web",
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
],
"Features": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.166.9",
"version": "3.167.3",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -54,7 +54,7 @@ import { ItemGroupController } from '@/Components/NoteView/Controller/ItemGroupC
import { VisibilityObserver } from './VisibilityObserver'
import { MomentsService } from '@/Controllers/Moments/MomentsService'
import { DevMode } from './DevMode'
import { ToastType, addToast } from '@standardnotes/toast'
import { ToastType, addToast, dismissToast } from '@standardnotes/toast'
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
@@ -395,7 +395,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
const filesController = this.controllers.filesController
const blob = getBlobFromBase64(file.data, file.mimeType)
const mappedFile = new File([blob], file.name, { type: file.mimeType })
void filesController.uploadNewFile(mappedFile, true)
filesController.uploadNewFile(mappedFile, true).catch(console.error)
}
async handleReceivedTextEvent({ text, title }: { text: string; title?: string | undefined }) {
@@ -417,6 +417,41 @@ export class WebApplication extends SNApplication implements WebApplicationInter
})
}
async handleReceivedLinkEvent({ link, title }: { link: string; title: string | undefined }) {
const url = new URL(link)
const paths = url.pathname.split('/')
const finalPath = paths[paths.length - 1]
const isImagePath = !!finalPath && /\.(png|svg|webp|jpe?g)/.test(finalPath)
if (isImagePath) {
const fetchToastUuid = addToast({
type: ToastType.Loading,
message: 'Fetching image from link...',
})
try {
const imgResponse = await fetch(link)
if (!imgResponse.ok) {
throw new Error(`${imgResponse.status}: Could not fetch image`)
}
const imgBlob = await imgResponse.blob()
const file = new File([imgBlob], finalPath, {
type: imgBlob.type,
})
this.controllers.filesController.uploadNewFile(file, true).catch(console.error)
} catch (error) {
console.error(error)
} finally {
dismissToast(fetchToastUuid)
}
return
}
this.handleReceivedTextEvent({
title: title,
text: link,
}).catch(console.error)
}
private async lockApplicationAfterMobileEventIfApplicable(): Promise<void> {
const isLocked = await this.isLocked()
if (isLocked) {
@@ -111,6 +111,7 @@ export const IconNameToSvgMapping = {
password: icons.PasswordIcon,
pencil: icons.PencilIcon,
pin: icons.PinIcon,
redo: icons.RedoIcon,
restore: icons.RestoreIcon,
save: icons.SaveIcon,
search: icons.SearchIcon,
@@ -135,6 +136,7 @@ export const IconNameToSvgMapping = {
tune: icons.TuneIcon,
unarchive: icons.UnarchiveIcon,
underline: icons.UnderlineIcon,
undo: icons.UndoIcon,
unpin: icons.UnpinIcon,
upload: icons.UploadIcon,
user: icons.UserIcon,
@@ -1,6 +1,6 @@
import Icon from '@/Components/Icon/Icon'
import { observer } from 'mobx-react-lite'
import { useState, useEffect, useMemo, useCallback } from 'react'
import { useState, useEffect, useMemo, useCallback, useRef } from 'react'
import { NoteType, Platform, SNNote } from '@standardnotes/snjs'
import {
CHANGE_EDITOR_WIDTH_COMMAND,
@@ -8,6 +8,9 @@ import {
PIN_NOTE_COMMAND,
SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND,
STAR_NOTE_COMMAND,
SUPER_EXPORT_HTML,
SUPER_EXPORT_JSON,
SUPER_EXPORT_MARKDOWN,
SUPER_SHOW_MARKDOWN_PREVIEW,
} from '@standardnotes/ui-services'
import ChangeEditorOption from './ChangeEditorOption'
@@ -39,6 +42,8 @@ import { useApplication } from '../ApplicationProvider'
import { MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
import AddToVaultMenuOption from '../Vaults/AddToVaultMenuOption'
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
import Menu from '../Menu/Menu'
import Popover from '../Popover/Popover'
const iconSize = MenuItemIconSize
const iconClassDanger = `text-danger mr-2 ${iconSize}`
@@ -181,6 +186,9 @@ const NotesOptions = ({
[application],
)
const superExportButtonRef = useRef<HTMLButtonElement>(null)
const [isSuperExportMenuOpen, setIsSuperExportMenuOpen] = useState(false)
const unauthorized = notes.some((note) => !application.isAuthorizedToRenderItem(note))
if (unauthorized) {
return <ProtectedUnauthorizedLabel />
@@ -286,7 +294,48 @@ const NotesOptions = ({
{pinShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={pinShortcut} />}
</MenuItem>
)}
{!isOnlySuperNoteSelected && (
{isOnlySuperNoteSelected ? (
<>
<MenuItem
ref={superExportButtonRef}
onClick={() => {
setIsSuperExportMenuOpen((open) => !open)
}}
>
<div className="flex items-center">
<Icon type="download" className={iconClass} />
Export
</div>
<Icon type="chevron-right" className="ml-auto text-neutral" />
</MenuItem>
<Popover
title="Export note"
side="left"
align="start"
open={isSuperExportMenuOpen}
anchorElement={superExportButtonRef.current}
togglePopover={() => {
setIsSuperExportMenuOpen(!isSuperExportMenuOpen)
}}
className="py-1"
>
<Menu a11yLabel={'Super note export menu'} isOpen={isSuperExportMenuOpen}>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_JSON, notes[0].title)}>
<Icon type="code" className={iconClass} />
Export as JSON
</MenuItem>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_MARKDOWN, notes[0].title)}>
<Icon type="markdown" className={iconClass} />
Export as Markdown
</MenuItem>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_HTML, notes[0].title)}>
<Icon type="rich-text" className={iconClass} />
Export as HTML
</MenuItem>
</Menu>
</Popover>
</>
) : (
<>
<MenuItem
onClick={() => {
@@ -1,17 +1,8 @@
import { SNNote } from '@standardnotes/snjs'
import {
PlatformedKeyboardShortcut,
SUPER_EXPORT_HTML,
SUPER_EXPORT_JSON,
SUPER_EXPORT_MARKDOWN,
} from '@standardnotes/ui-services'
import { useRef, useState } from 'react'
import { useCommandService } from '../CommandProvider'
import { PlatformedKeyboardShortcut } from '@standardnotes/ui-services'
import Icon from '../Icon/Icon'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
import Menu from '../Menu/Menu'
import MenuItem from '../Menu/MenuItem'
import Popover from '../Popover/Popover'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { iconClass } from './ClassNames'
@@ -21,12 +12,7 @@ type Props = {
enableSuperMarkdownPreview: () => void
}
const SuperNoteOptions = ({ note, markdownShortcut, enableSuperMarkdownPreview }: Props) => {
const commandService = useCommandService()
const exportButtonRef = useRef<HTMLButtonElement>(null)
const [isExportMenuOpen, setIsExportMenuOpen] = useState(false)
const SuperNoteOptions = ({ markdownShortcut, enableSuperMarkdownPreview }: Props) => {
return (
<>
<HorizontalSeparator classes="my-2" />
@@ -38,45 +24,6 @@ const SuperNoteOptions = ({ note, markdownShortcut, enableSuperMarkdownPreview }
Show Markdown
{markdownShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={markdownShortcut} />}
</MenuItem>
<MenuItem
ref={exportButtonRef}
onClick={() => {
setIsExportMenuOpen((open) => !open)
}}
>
<div className="flex items-center">
<Icon type="download" className={iconClass} />
Export
</div>
<Icon type="chevron-right" className="ml-auto text-neutral" />
</MenuItem>
<Popover
title="Export note"
side="left"
align="start"
open={isExportMenuOpen}
anchorElement={exportButtonRef.current}
togglePopover={() => {
setIsExportMenuOpen(!isExportMenuOpen)
}}
className="py-1"
>
<Menu a11yLabel={'Super note export menu'} isOpen={isExportMenuOpen}>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_JSON, note.title)}>
<Icon type="code" className={iconClass} />
Export as JSON
</MenuItem>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_MARKDOWN, note.title)}>
<Icon type="markdown" className={iconClass} />
Export as Markdown
</MenuItem>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_HTML, note.title)}>
<Icon type="rich-text" className={iconClass} />
Export as HTML
</MenuItem>
</Menu>
</Popover>
</>
)
}
@@ -110,6 +110,7 @@ export const usePaneSwipeGesture = (
const touch = event.touches[0]
startX = touch.clientX
clientX = touch.clientX
const isStartOutOfThreshold =
(direction === 'right' && startX > TouchStartThreshold) ||
@@ -2,15 +2,30 @@ import { Title } from '@/Components/Preferences/PreferencesComponents/Content'
import SubscriptionInformation from './SubscriptionInformation'
import NoSubscription from './NoSubscription'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'react'
import { FunctionComponent, useEffect, useState } from 'react'
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
import { useApplication } from '@/Components/ApplicationProvider'
import { SubscriptionManagerEvent, Subscription } from '@standardnotes/snjs'
const Subscription: FunctionComponent = () => {
const application = useApplication()
const onlineSubscription = application.controllers.subscriptionController.onlineSubscription
const [onlineSubscription, setOnlineSubscription] = useState<Subscription | undefined>(
application.controllers.subscriptionController.onlineSubscription,
)
useEffect(() => {
return application.subscriptions.addEventObserver((event) => {
if (event === SubscriptionManagerEvent.DidFetchSubscription) {
setOnlineSubscription(application.controllers.subscriptionController.onlineSubscription)
}
})
}, [application.subscriptions, application.controllers.subscriptionController])
useEffect(() => {
void application.subscriptions.fetchOnlineSubscription()
}, [application.subscriptions])
return (
<PreferencesGroup>
@@ -36,7 +36,7 @@ const OfflineSubscription: FunctionComponent<Props> = ({ application, onSuccess
const homeServer = application.homeServer
const homeServerEnabled = homeServer && homeServer.isHomeServerEnabled()
const homeServerEnabled = homeServer && (await homeServer.isHomeServerEnabled())
const homeServerIsRunning = homeServerEnabled && (await homeServer.isHomeServerRunning())
if (homeServerEnabled) {
@@ -4,7 +4,17 @@ import useModal from '../../Lexical/Hooks/useModal'
import { InsertTableDialog } from '../../Plugins/TablePlugin'
import { getSelectedNode } from '../../Lexical/Utils/getSelectedNode'
import { sanitizeUrl } from '../../Lexical/Utils/sanitizeUrl'
import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
import {
$getSelection,
$isRangeSelection,
CAN_REDO_COMMAND,
CAN_UNDO_COMMAND,
COMMAND_PRIORITY_CRITICAL,
FORMAT_TEXT_COMMAND,
REDO_COMMAND,
UNDO_COMMAND,
} from 'lexical'
import { mergeRegister } from '@lexical/utils'
import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { GetAlignmentBlocks } from '../Blocks/Alignment'
@@ -64,13 +74,53 @@ const MobileToolbarPlugin = () => {
}
}, [editor])
const [canUndo, setCanUndo] = useState(false)
const [canRedo, setCanRedo] = useState(false)
useEffect(() => {
return mergeRegister(
editor.registerCommand<boolean>(
CAN_UNDO_COMMAND,
(payload) => {
setCanUndo(payload)
return false
},
COMMAND_PRIORITY_CRITICAL,
),
editor.registerCommand<boolean>(
CAN_REDO_COMMAND,
(payload) => {
setCanRedo(payload)
return false
},
COMMAND_PRIORITY_CRITICAL,
),
)
}, [editor])
const items = useMemo(
(): {
name: string
iconName: string
keywords?: string[]
disabled?: boolean
onSelect: () => void
}[] => [
{
name: 'Undo',
iconName: 'undo',
disabled: !canUndo,
onSelect: () => {
editor.dispatchCommand(UNDO_COMMAND, undefined)
},
},
{
name: 'Redo',
iconName: 'redo',
disabled: !canRedo,
onSelect: () => {
editor.dispatchCommand(REDO_COMMAND, undefined)
},
},
{
name: 'Bold',
iconName: 'bold',
@@ -150,7 +200,7 @@ const MobileToolbarPlugin = () => {
GetCollapsibleBlock(editor),
...GetEmbedsBlocks(editor),
],
[application.keyboardService, editor, insertLink, showModal],
[application.keyboardService, canRedo, canUndo, editor, insertLink, showModal],
)
useEffect(() => {
@@ -222,10 +272,11 @@ const MobileToolbarPlugin = () => {
{items.map((item) => {
return (
<button
className="flex items-center justify-center rounded px-3 py-3"
className="flex items-center justify-center rounded px-3 py-3 disabled:opacity-50"
aria-label={item.name}
onClick={item.onSelect}
key={item.name}
disabled={item.disabled}
>
<Icon type={item.iconName} size="medium" className="!text-current [&>path]:!text-current" />
</button>
@@ -75,7 +75,9 @@ export const SuperEditor: FunctionComponent<Props> = ({
const [featureStatus, setFeatureStatus] = useState<FeatureStatus>(FeatureStatus.Entitled)
useEffect(() => {
setFeatureStatus(application.features.getFeatureStatus(FeatureIdentifier.SuperEditor))
setFeatureStatus(
application.features.getFeatureStatus(FeatureIdentifier.SuperEditor, { inContextOfItem: note.current }),
)
}, [application.features])
const commandService = useCommandService()
@@ -1,4 +1,4 @@
import { Subscription } from '@standardnotes/security'
import { Subscription } from '@standardnotes/responses'
import { destroyAllObjectProperties } from '@/Utils'
import {
ApplicationEvent,
@@ -93,7 +93,10 @@ export class MobileWebReceiver {
)
break
case ReactNativeToWebEvent.ReceivedText:
void this.application.handleReceivedTextEvent(messageData as { text: string; title?: string })
void this.application.handleReceivedTextEvent(messageData as { text: string })
break
case ReactNativeToWebEvent.ReceivedLink:
void this.application.handleReceivedLinkEvent(messageData as { link: string; title: string })
break
default:
+1 -6
View File
@@ -4221,7 +4221,6 @@ __metadata:
"@standardnotes/domain-core": ^1.22.0
"@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*"
"@standardnotes/security": ^1.7.6
"@standardnotes/utils": "workspace:*"
"@types/jest": ^29.2.3
"@typescript-eslint/eslint-plugin": "*"
@@ -4518,7 +4517,6 @@ __metadata:
dependencies:
"@standardnotes/common": ^1.50.0
"@standardnotes/domain-core": ^1.22.0
"@standardnotes/security": ^1.7.6
"@types/jest": ^29.2.3
"@typescript-eslint/eslint-plugin": "*"
eslint: "*"
@@ -4774,7 +4772,6 @@ __metadata:
dependencies:
"@standardnotes/common": ^1.50.0
"@standardnotes/features": "workspace:*"
"@standardnotes/security": ^1.7.6
"@types/jest": ^29.2.3
"@typescript-eslint/eslint-plugin": "*"
eslint: "*"
@@ -4795,7 +4792,7 @@ __metadata:
languageName: node
linkType: hard
"@standardnotes/security@npm:1.7.6, @standardnotes/security@npm:^1.2.0, @standardnotes/security@npm:^1.7.5, @standardnotes/security@npm:^1.7.6":
"@standardnotes/security@npm:1.7.6, @standardnotes/security@npm:^1.2.0":
version: 1.7.6
resolution: "@standardnotes/security@npm:1.7.6"
dependencies:
@@ -4817,7 +4814,6 @@ __metadata:
"@standardnotes/files": "workspace:^"
"@standardnotes/models": "workspace:^"
"@standardnotes/responses": "workspace:*"
"@standardnotes/security": ^1.7.5
"@standardnotes/sncrypto-common": "workspace:^"
"@standardnotes/utils": "workspace:*"
"@types/jest": ^29.2.3
@@ -4918,7 +4914,6 @@ __metadata:
"@standardnotes/files": "workspace:*"
"@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*"
"@standardnotes/security": ^1.7.6
"@standardnotes/services": "workspace:*"
"@standardnotes/settings": ^1.20.0
"@standardnotes/sncrypto-common": "workspace:*"