mirror of
https://github.com/standardnotes/app
synced 2026-09-19 09:13:53 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f02465346f | ||
|
|
11dd39c126 | ||
|
|
d7a90c4d91 | ||
|
|
00075616e6 | ||
|
|
5ffffbff20 | ||
|
|
ecd8d56171 | ||
|
|
33226000f5 | ||
|
|
c841ac99d2 | ||
|
|
b0e708186c | ||
|
|
20e420820d | ||
|
|
c3d6a91730 | ||
|
|
0ed692ff9b | ||
|
|
c74fa272fb | ||
|
|
3f9d3ceffa | ||
|
|
f6d0aa0f0a | ||
|
|
eafa65e934 | ||
|
|
a83003ee69 | ||
|
|
554f59a3fe | ||
|
|
c0af596f2d | ||
|
|
6e10911d02 | ||
|
|
9502a7fa32 | ||
|
|
727d78cfe2 | ||
|
|
44bb4b7ca3 | ||
|
|
981d8a7497 | ||
|
|
04245dfeeb | ||
|
|
5db47e83e8 | ||
|
|
c08602b7b8 | ||
|
|
ec52e5684d | ||
|
|
f3f7f9d362 | ||
|
|
b9a07d8b5a | ||
|
|
31213bf268 | ||
|
|
70e57e32b3 | ||
|
|
720215eea7 | ||
|
|
6d5ebdeaa1 | ||
|
|
c99693876f | ||
|
|
f8f6a5212b | ||
|
|
58cd919267 | ||
|
|
507823dc4f | ||
|
|
6ea1def0cf | ||
|
|
fe2ce9f1e8 | ||
|
|
4985787c5d | ||
|
|
8614a638a4 | ||
|
|
15957c71cb | ||
|
|
59e814bcec | ||
|
|
4cf9ebf52a | ||
|
|
a3611c802f | ||
|
|
9be315730d | ||
|
|
789a78bda3 | ||
|
|
73e170dedd | ||
|
|
e4b1b64119 | ||
|
|
c2a1918843 | ||
|
|
d3621d70b1 | ||
|
|
bf286f4854 | ||
|
|
97835e64ac | ||
|
|
285df04a59 | ||
|
|
4d5429cc89 | ||
|
|
da6622dc95 | ||
|
|
a548883863 | ||
|
|
1330201d2e | ||
|
|
0af720146c | ||
|
|
4d6992cf83 | ||
|
|
5de6f459c1 | ||
|
|
3c0d4fac76 | ||
|
|
4cdfcbfd95 | ||
|
|
51e50433d3 | ||
|
|
696327ff03 | ||
|
|
de64f1bfce | ||
|
|
48d484675b | ||
|
|
86251b49d3 | ||
|
|
5961bc491d | ||
|
|
e50f4eb92e | ||
|
|
4f8e75f36a | ||
|
|
6824dd2d5f | ||
|
|
76ec8b0f90 | ||
|
|
3eebdbb77d | ||
|
|
ce252def33 | ||
|
|
cb7af03ce8 | ||
|
|
1fe833bc24 | ||
|
|
ac1f59cde7 | ||
|
|
db4ceca10e | ||
|
|
0176f47fe7 | ||
|
|
f281041221 | ||
|
|
ebd56cd5aa | ||
|
|
8fa566336c | ||
|
|
9dfeac7489 | ||
|
|
d1b71861ec | ||
|
|
766548f12d | ||
|
|
581a08cd09 | ||
|
|
ee0d44405d | ||
|
|
1ae64dcac4 | ||
|
|
2be4fb4f83 | ||
|
|
271a8d1d70 | ||
|
|
e809328fa8 |
Binary file not shown.
BIN
Binary file not shown.
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.8.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.8.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.8.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.8.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.8.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.8.1",
|
||||
"version": "1.8.5",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -24,7 +24,12 @@ describe('UserApiService', () => {
|
||||
})
|
||||
|
||||
it('should register a user', async () => {
|
||||
const response = await createService().register('[email protected]', 'testpasswd', keyParams, false)
|
||||
const response = await createService().register({
|
||||
email: '[email protected]',
|
||||
serverPassword: 'testpasswd',
|
||||
keyParams,
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
@@ -52,7 +57,7 @@ describe('UserApiService', () => {
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.register('[email protected]', 'testpasswd', keyParams, false)
|
||||
await service.register({ email: '[email protected]', serverPassword: 'testpasswd', keyParams, ephemeral: false })
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
@@ -67,7 +72,12 @@ describe('UserApiService', () => {
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().register('[email protected]', 'testpasswd', keyParams, false)
|
||||
await createService().register({
|
||||
email: '[email protected]',
|
||||
serverPassword: 'testpasswd',
|
||||
keyParams,
|
||||
ephemeral: false,
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ export class UserApiService implements UserApiServiceInterface {
|
||||
this.registering = false
|
||||
}
|
||||
|
||||
async register(
|
||||
email: string,
|
||||
serverPassword: string,
|
||||
keyParams: RootKeyParamsInterface,
|
||||
ephemeral: boolean,
|
||||
): Promise<UserRegistrationResponse> {
|
||||
async register(registerDTO: {
|
||||
email: string
|
||||
serverPassword: string
|
||||
keyParams: RootKeyParamsInterface
|
||||
ephemeral: boolean
|
||||
}): Promise<UserRegistrationResponse> {
|
||||
if (this.registering) {
|
||||
throw new ApiCallError(ErrorMessage.RegistrationInProgress)
|
||||
}
|
||||
@@ -29,10 +29,10 @@ export class UserApiService implements UserApiServiceInterface {
|
||||
try {
|
||||
const response = await this.userServer.register({
|
||||
[ApiEndpointParam.ApiVersion]: ApiVersion.v0,
|
||||
password: serverPassword,
|
||||
email,
|
||||
ephemeral,
|
||||
...keyParams.getPortableValue(),
|
||||
password: registerDTO.serverPassword,
|
||||
email: registerDTO.email,
|
||||
ephemeral: registerDTO.ephemeral,
|
||||
...registerDTO.keyParams.getPortableValue(),
|
||||
})
|
||||
|
||||
this.registering = false
|
||||
|
||||
@@ -2,10 +2,10 @@ import { RootKeyParamsInterface } from '@standardnotes/models'
|
||||
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
|
||||
|
||||
export interface UserApiServiceInterface {
|
||||
register(
|
||||
email: string,
|
||||
serverPassword: string,
|
||||
keyParams: RootKeyParamsInterface,
|
||||
ephemeral: boolean,
|
||||
): Promise<UserRegistrationResponse>
|
||||
register(registerDTO: {
|
||||
email: string
|
||||
serverPassword: string
|
||||
keyParams: RootKeyParamsInterface
|
||||
ephemeral: boolean
|
||||
}): Promise<UserRegistrationResponse>
|
||||
}
|
||||
|
||||
@@ -8,4 +8,6 @@ export type UserRegistrationRequestParams = AnyKeyParamsContent & {
|
||||
email: string
|
||||
ephemeral: boolean
|
||||
[additionalParam: string]: unknown
|
||||
pkcPublicKey?: string
|
||||
pkcEncryptedPrivateKey?: string
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.7.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
## [2.7.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-16)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/components-meta",
|
||||
"version": "2.7.13",
|
||||
"version": "2.7.14",
|
||||
"private": true,
|
||||
"author": "Standard Notes.",
|
||||
"main": "dist",
|
||||
|
||||
@@ -3,6 +3,162 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.23.175](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.174](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.173](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.172](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.171](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.170](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.169](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.168](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.167](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.166](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.165](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.164](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.163](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.162](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.161](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.160](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.159](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.158](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.157](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.156](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.155](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.154](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.153](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.152](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.151](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.150](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.149](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.148](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.147](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.146](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.145](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.144](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.143](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.142](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.141](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.140](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.139](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.138](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.137](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.136](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.23.136",
|
||||
"version": "3.23.175",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.15.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.15.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.15.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.15.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.15.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.15.3",
|
||||
"version": "1.15.7",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.52.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** adjust status bar color to match current theme ([#1624](https://github.com/standardnotes/app/issues/1624)) ([4d5429c](https://github.com/standardnotes/app/commit/4d5429cc89c15f870825f20e97844825102c6aec))
|
||||
|
||||
# [1.52.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-13)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/features",
|
||||
"version": "1.52.0",
|
||||
"version": "1.52.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -72,6 +72,7 @@ export type ThemeFeatureDescription = ComponentFeatureDescription & {
|
||||
/** Some themes can be layered on top of other themes */
|
||||
layerable?: boolean
|
||||
dock_icon?: ThemeDockIcon
|
||||
isDark?: boolean
|
||||
}
|
||||
|
||||
export type FeatureDescription = BaseFeatureDescription &
|
||||
|
||||
@@ -12,6 +12,7 @@ export function themes(): ThemeFeatureDescription[] {
|
||||
permission_name: PermissionName.MidnightTheme,
|
||||
description: 'Elegant utilitarianism.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/midnight-with-mobile.jpg',
|
||||
isDark: true,
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#086DD6',
|
||||
@@ -27,6 +28,7 @@ export function themes(): ThemeFeatureDescription[] {
|
||||
permission_name: PermissionName.FuturaTheme,
|
||||
description: 'Calm and relaxed. Take some time off.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/futura-with-mobile.jpg',
|
||||
isDark: true,
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#fca429',
|
||||
@@ -42,6 +44,7 @@ export function themes(): ThemeFeatureDescription[] {
|
||||
permission_name: PermissionName.SolarizedDarkTheme,
|
||||
description: 'The perfect theme for any time.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/solarized-dark.jpg',
|
||||
isDark: true,
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#2AA198',
|
||||
@@ -72,6 +75,7 @@ export function themes(): ThemeFeatureDescription[] {
|
||||
permission_name: PermissionName.FocusedTheme,
|
||||
description: 'For when you need to go in.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/focus-with-mobile.jpg',
|
||||
isDark: true,
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#a464c2',
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.23.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.23.3",
|
||||
"version": "1.23.7",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.10.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.10.3",
|
||||
"version": "1.10.7",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,186 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.37.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.37.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.37.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* handle android back button on android ([#1656](https://github.com/standardnotes/app/issues/1656)) ([981d8a7](https://github.com/standardnotes/app/commit/981d8a7497ea2600e1535ad9170cb5559550ca4d))
|
||||
|
||||
## [3.36.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* export/sharing notes on mobile webview ([#1644](https://github.com/standardnotes/app/issues/1644)) ([6d5ebde](https://github.com/standardnotes/app/commit/6d5ebdeaa1777f31878b6e7d4dfc33fe83abc9fc))
|
||||
|
||||
## [3.36.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** hide drawer on new mobile experience ([#1642](https://github.com/standardnotes/app/issues/1642)) ([8614a63](https://github.com/standardnotes/app/commit/8614a638a4bfc8fdf97020bf1fc147c62998cf3b))
|
||||
|
||||
## [3.36.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** accurate version number ([789a78b](https://github.com/standardnotes/app/commit/789a78bda33f12d214c12a63ae27d2ba0f39feab))
|
||||
|
||||
## [3.36.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** adjust status bar color to match current theme ([#1624](https://github.com/standardnotes/app/issues/1624)) ([4d5429c](https://github.com/standardnotes/app/commit/4d5429cc89c15f870825f20e97844825102c6aec))
|
||||
|
||||
## [3.36.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.36.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.36.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Features
|
||||
|
||||
* sync page theme color metadata with active theme bg ([#1623](https://github.com/standardnotes/app/issues/1623)) ([5de6f45](https://github.com/standardnotes/app/commit/5de6f459c106127f294693389f62fa1c3ed57798))
|
||||
|
||||
## [3.35.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* iOS safe area using CSS ([#1610](https://github.com/standardnotes/app/issues/1610)) ([8fa5663](https://github.com/standardnotes/app/commit/8fa566336ceef8a3d7b559b1aaf342f7c51f21cd))
|
||||
|
||||
## [3.35.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* iOS webview window size and padding ([#1605](https://github.com/standardnotes/app/issues/1605)) ([1ae64dc](https://github.com/standardnotes/app/commit/1ae64dcac4cc714f8931b2301fce1cb802570862))
|
||||
|
||||
## [3.35.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.35.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||
<meta content="viewport-fit=cover, width=device-width, initial-scale=1" name="viewport" />
|
||||
<meta content="#ffffff" name="theme-color" />
|
||||
<link rel="stylesheet" href="web-src/app.css" />
|
||||
<script>
|
||||
window.defaultSyncServer = "https://api.standardnotes.com";
|
||||
@@ -22,4 +23,4 @@
|
||||
<body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.35.4",
|
||||
"version": "3.37.9",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
@@ -69,7 +69,7 @@
|
||||
"react-native-screens": "3.13.1",
|
||||
"react-native-search-bar": "standardnotes/react-native-search-bar#7d2139daf9b7663b570403f21f520deceba9bb09",
|
||||
"react-native-search-box": "standardnotes/react-native-search-box#8c46369912cba78dca718588aca9c16926953ff7",
|
||||
"react-native-share": "^7.3.7",
|
||||
"react-native-share": "^7.9.0",
|
||||
"react-native-sodium-jsi": "1.2.0",
|
||||
"react-native-static-server": "standardnotes/react-native-static-server#28ef0175dbee3db9aadfab57498497067556a836",
|
||||
"react-native-store-review": "^0.2.0",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { AbstractService, InternalEventBus, ReactNativeToWebEvent } from '@standardnotes/snjs'
|
||||
import { BackHandler, NativeEventSubscription } from 'react-native'
|
||||
|
||||
export class AndroidBackHandlerService extends AbstractService<ReactNativeToWebEvent> {
|
||||
private removeListener: NativeEventSubscription
|
||||
|
||||
constructor() {
|
||||
const internalEventBus = new InternalEventBus()
|
||||
super(internalEventBus)
|
||||
|
||||
this.removeListener = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
void this.notifyEvent(ReactNativeToWebEvent.AndroidBackButtonPressed)
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
deinit() {
|
||||
this.removeListener.remove()
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import { ICON_MENU } from '@Style/Icons'
|
||||
import { ThemeService } from '@Style/ThemeService'
|
||||
import { getDefaultDrawerWidth } from '@Style/Utils'
|
||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { Dimensions, Keyboard, ScaledSize, StatusBar } from 'react-native'
|
||||
import { Dimensions, Keyboard, ScaledSize } from 'react-native'
|
||||
import DrawerLayout, { DrawerState } from 'react-native-gesture-handler/DrawerLayout'
|
||||
import { HeaderButtons, Item } from 'react-navigation-header-buttons'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
@@ -121,7 +121,9 @@ export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) =
|
||||
[application],
|
||||
)
|
||||
|
||||
if (IsMobileWeb) {
|
||||
const shouldOpenWebApp = application?.getValue(AlwaysOpenWebAppOnLaunchKey, StorageValueModes.Nonwrapped) as boolean
|
||||
|
||||
if (IsMobileWeb || shouldOpenWebApp) {
|
||||
return (
|
||||
<AppStack.Navigator
|
||||
screenOptions={() => ({
|
||||
@@ -134,12 +136,6 @@ export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) =
|
||||
)
|
||||
}
|
||||
|
||||
if (!application) {
|
||||
return null
|
||||
}
|
||||
|
||||
const shouldOpenWebApp = application.getValue(AlwaysOpenWebAppOnLaunchKey, StorageValueModes.Nonwrapped) as boolean
|
||||
|
||||
return (
|
||||
<DrawerLayout
|
||||
ref={drawerRef}
|
||||
@@ -150,7 +146,6 @@ export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) =
|
||||
onDrawerStateChanged={handleDrawerStateChange}
|
||||
renderNavigationView={() => !isLocked && <MainSideMenu drawerRef={drawerRef.current} />}
|
||||
>
|
||||
<StatusBar translucent={!shouldOpenWebApp} />
|
||||
<DrawerLayout
|
||||
ref={noteDrawerRef}
|
||||
drawerWidth={getDefaultDrawerWidth(dimensions)}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
platformFromString,
|
||||
SNApplication,
|
||||
SNComponentManager,
|
||||
StorageValueModes,
|
||||
} from '@standardnotes/snjs'
|
||||
import { Platform } from 'react-native'
|
||||
|
||||
@@ -21,6 +22,7 @@ import { MobileAlertService } from './AlertService'
|
||||
import { ApplicationState } from './ApplicationState'
|
||||
import { BackupsService } from './BackupsService'
|
||||
import { ComponentManager } from './ComponentManager'
|
||||
import { AlwaysOpenWebAppOnLaunchKey } from './constants'
|
||||
import { FilesService } from './FilesService'
|
||||
import { InstallationService } from './InstallationService'
|
||||
import { MobileDevice } from './Interface'
|
||||
@@ -135,7 +137,8 @@ export class MobileApplication extends SNApplication {
|
||||
}
|
||||
|
||||
promptForChallenge(challenge: Challenge) {
|
||||
if (IsMobileWeb) {
|
||||
const optedIntoWebPreview = this.getValue(AlwaysOpenWebAppOnLaunchKey, StorageValueModes.Nonwrapped) as boolean
|
||||
if (IsMobileWeb || optedIntoWebPreview) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AsyncStorage from '@react-native-community/async-storage'
|
||||
import { AndroidBackHandlerService } from '@Root/AndroidBackHandlerService'
|
||||
import SNReactNative from '@standardnotes/react-native-utils'
|
||||
import {
|
||||
ApplicationIdentifier,
|
||||
@@ -7,16 +8,27 @@ import {
|
||||
LegacyRawKeychainValue,
|
||||
MobileDeviceInterface,
|
||||
NamespacedRootKeyInKeychain,
|
||||
Platform as SNPlatform,
|
||||
RawKeychainValue,
|
||||
removeFromArray,
|
||||
TransferPayload,
|
||||
} from '@standardnotes/snjs'
|
||||
import { Alert, Linking, Platform } from 'react-native'
|
||||
import { Alert, Linking, PermissionsAndroid, Platform, StatusBar } from 'react-native'
|
||||
import FingerprintScanner from 'react-native-fingerprint-scanner'
|
||||
import FlagSecure from 'react-native-flag-secure-android'
|
||||
import {
|
||||
CachesDirectoryPath,
|
||||
DocumentDirectoryPath,
|
||||
DownloadDirectoryPath,
|
||||
exists,
|
||||
unlink,
|
||||
writeFile,
|
||||
} from 'react-native-fs'
|
||||
import { hide, show } from 'react-native-privacy-snapshot'
|
||||
import Share from 'react-native-share'
|
||||
import { AppStateObserverService } from './../AppStateObserverService'
|
||||
import Keychain from './Keychain'
|
||||
import { SNReactNativeCrypto } from './ReactNativeCrypto'
|
||||
import { IsMobileWeb } from './Utils'
|
||||
|
||||
export type BiometricsType = 'Fingerprint' | 'Face ID' | 'Biometrics' | 'Touch ID'
|
||||
@@ -64,13 +76,23 @@ const showLoadFailForItemIds = (failedItemIds: string[]) => {
|
||||
|
||||
export class MobileDevice implements MobileDeviceInterface {
|
||||
environment: Environment.Mobile = Environment.Mobile
|
||||
platform: SNPlatform.Ios | SNPlatform.Android = Platform.OS === 'ios' ? SNPlatform.Ios : SNPlatform.Android
|
||||
private eventObservers: MobileDeviceEventHandler[] = []
|
||||
public isDarkMode = false
|
||||
private crypto: SNReactNativeCrypto
|
||||
|
||||
constructor(private stateObserverService?: AppStateObserverService) {}
|
||||
constructor(
|
||||
private stateObserverService?: AppStateObserverService,
|
||||
private androidBackHandlerService?: AndroidBackHandlerService,
|
||||
) {
|
||||
this.crypto = new SNReactNativeCrypto()
|
||||
}
|
||||
|
||||
deinit() {
|
||||
this.stateObserverService?.deinit()
|
||||
;(this.stateObserverService as unknown) = undefined
|
||||
this.androidBackHandlerService?.deinit()
|
||||
;(this.androidBackHandlerService as unknown) = undefined
|
||||
}
|
||||
|
||||
consoleLog(...args: any[]): void {
|
||||
@@ -432,6 +454,16 @@ export class MobileDevice implements MobileDeviceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
handleThemeSchemeChange(isDark: boolean): void {
|
||||
this.isDarkMode = isDark
|
||||
|
||||
this.reloadStatusBarStyle()
|
||||
}
|
||||
|
||||
reloadStatusBarStyle(animated = true) {
|
||||
StatusBar.setBarStyle(this.isDarkMode ? 'light-content' : 'dark-content', animated)
|
||||
}
|
||||
|
||||
private notifyEvent(event: MobileDeviceEvent): void {
|
||||
for (const handler of this.eventObservers) {
|
||||
handler(event)
|
||||
@@ -444,4 +476,101 @@ export class MobileDevice implements MobileDeviceInterface {
|
||||
isDeviceDestroyed() {
|
||||
return false
|
||||
}
|
||||
|
||||
async deleteFileAtPathIfExists(path: string) {
|
||||
if (await exists(path)) {
|
||||
await unlink(path)
|
||||
}
|
||||
}
|
||||
|
||||
async shareBase64AsFile(base64: string, filename: string) {
|
||||
let downloadedTempFilePath: string | undefined
|
||||
try {
|
||||
downloadedTempFilePath = await this.downloadBase64AsFile(base64, filename, true)
|
||||
if (!downloadedTempFilePath) {
|
||||
return
|
||||
}
|
||||
await Share.open({
|
||||
url: `file://${downloadedTempFilePath}`,
|
||||
failOnCancel: false,
|
||||
})
|
||||
} catch (error) {
|
||||
this.consoleLog(`${error}`)
|
||||
} finally {
|
||||
if (downloadedTempFilePath) {
|
||||
void this.deleteFileAtPathIfExists(downloadedTempFilePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getFileDestinationPath(filename: string, saveInTempLocation: boolean): string {
|
||||
let directory = DocumentDirectoryPath
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
directory = saveInTempLocation ? CachesDirectoryPath : DownloadDirectoryPath
|
||||
}
|
||||
|
||||
return `${directory}/${filename}`
|
||||
}
|
||||
|
||||
async hasStoragePermissionOnAndroid(): Promise<boolean> {
|
||||
if (Platform.OS !== 'android') {
|
||||
return true
|
||||
}
|
||||
const grantedStatus = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE)
|
||||
if (grantedStatus === PermissionsAndroid.RESULTS.GRANTED) {
|
||||
return true
|
||||
}
|
||||
Alert.alert(
|
||||
'Storage permissions are required in order to download files. Please accept the permissions prompt and try again.',
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
async downloadBase64AsFile(
|
||||
base64: string,
|
||||
filename: string,
|
||||
saveInTempLocation = false,
|
||||
): Promise<string | undefined> {
|
||||
const isGrantedStoragePermissionOnAndroid = await this.hasStoragePermissionOnAndroid()
|
||||
|
||||
if (!isGrantedStoragePermissionOnAndroid) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const path = this.getFileDestinationPath(filename, saveInTempLocation)
|
||||
void this.deleteFileAtPathIfExists(path)
|
||||
const decodedContents = this.crypto.base64Decode(base64.replace(/data.*base64,/, ''))
|
||||
await writeFile(path, decodedContents)
|
||||
return path
|
||||
} catch (error) {
|
||||
this.consoleLog(`${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
confirmAndExit() {
|
||||
Alert.alert(
|
||||
'Close app',
|
||||
'Do you want to close the app?',
|
||||
[
|
||||
{
|
||||
text: 'Cancel',
|
||||
style: 'cancel',
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onPress: async () => {},
|
||||
},
|
||||
{
|
||||
text: 'Close',
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
SNReactNative.exitApp()
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
cancelable: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { MobileDevice, MobileDeviceEvent } from '@Lib/Interface'
|
||||
import { IsDev } from '@Lib/Utils'
|
||||
import { ReactNativeToWebEvent } from '@standardnotes/snjs'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Platform } from 'react-native'
|
||||
import { Keyboard, Platform } from 'react-native'
|
||||
import VersionInfo from 'react-native-version-info'
|
||||
import { WebView, WebViewMessageEvent } from 'react-native-webview'
|
||||
import pjson from '../package.json'
|
||||
import { AndroidBackHandlerService } from './AndroidBackHandlerService'
|
||||
import { AppStateObserverService } from './AppStateObserverService'
|
||||
|
||||
const LoggingEnabled = false
|
||||
const LoggingEnabled = IsDev
|
||||
|
||||
export const MobileWebAppContainer = () => {
|
||||
const [identifier, setIdentifier] = useState(Math.random())
|
||||
@@ -21,17 +25,38 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
||||
const webViewRef = useRef<WebView>(null)
|
||||
const sourceUri = (Platform.OS === 'android' ? 'file:///android_asset/' : '') + 'Web.bundle/src/index.html'
|
||||
const stateService = useMemo(() => new AppStateObserverService(), [])
|
||||
const device = useMemo(() => new MobileDevice(stateService), [stateService])
|
||||
const androidBackHandlerService = useMemo(() => new AndroidBackHandlerService(), [])
|
||||
const device = useMemo(
|
||||
() => new MobileDevice(stateService, androidBackHandlerService),
|
||||
[androidBackHandlerService, stateService],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const removeListener = stateService.addEventObserver((event: ReactNativeToWebEvent) => {
|
||||
const removeStateServiceListener = stateService.addEventObserver((event: ReactNativeToWebEvent) => {
|
||||
webViewRef.current?.postMessage(JSON.stringify({ reactNativeEvent: event, messageType: 'event' }))
|
||||
})
|
||||
|
||||
const removeBackHandlerServiceListener = androidBackHandlerService.addEventObserver(
|
||||
(event: ReactNativeToWebEvent) => {
|
||||
webViewRef.current?.postMessage(JSON.stringify({ reactNativeEvent: event, messageType: 'event' }))
|
||||
},
|
||||
)
|
||||
|
||||
const keyboardShowListener = Keyboard.addListener('keyboardWillShow', () => {
|
||||
device.reloadStatusBarStyle(false)
|
||||
})
|
||||
|
||||
const keyboardHideListener = Keyboard.addListener('keyboardDidHide', () => {
|
||||
device.reloadStatusBarStyle(false)
|
||||
})
|
||||
|
||||
return () => {
|
||||
removeListener()
|
||||
removeStateServiceListener()
|
||||
removeBackHandlerServiceListener()
|
||||
keyboardShowListener.remove()
|
||||
keyboardHideListener.remove()
|
||||
}
|
||||
}, [webViewRef, stateService])
|
||||
}, [webViewRef, stateService, device, androidBackHandlerService])
|
||||
|
||||
useEffect(() => {
|
||||
const observer = device.addMobileWebEventReceiver((event) => {
|
||||
@@ -73,8 +98,9 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
||||
const WebProcessDeviceInterface = `
|
||||
class WebProcessDeviceInterface {
|
||||
constructor(messageSender) {
|
||||
this.appVersion = '1.2.3'
|
||||
this.appVersion = '${pjson.version} (${VersionInfo.buildVersion})'
|
||||
this.environment = 4
|
||||
this.platform = ${device.platform}
|
||||
this.databases = []
|
||||
this.messageSender = messageSender
|
||||
}
|
||||
@@ -175,7 +201,6 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
||||
ref={webViewRef}
|
||||
source={{ uri: sourceUri }}
|
||||
originWhitelist={['*']}
|
||||
contentInset={{ top: 30, bottom: 10 }}
|
||||
onLoad={() => {}}
|
||||
onError={(err) => console.error('An error has occurred', err)}
|
||||
onHttpError={() => console.error('An HTTP error occurred')}
|
||||
@@ -183,6 +208,7 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
|
||||
allowFileAccess={true}
|
||||
allowUniversalAccessFromFileURLs={true}
|
||||
injectedJavaScriptBeforeContentLoaded={injectedJS}
|
||||
bounces={false}
|
||||
/>
|
||||
)
|
||||
/* eslint-enable @typescript-eslint/no-empty-function */
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { AlwaysOpenWebAppOnLaunchKey } from '@Lib/constants'
|
||||
import { ApplicationContext } from '@Root/ApplicationContext'
|
||||
import { ButtonCell } from '@Root/Components/ButtonCell'
|
||||
import { SectionHeader } from '@Root/Components/SectionHeader'
|
||||
import { TableSection } from '@Root/Components/TableSection'
|
||||
import { ButtonType, StorageValueModes } from '@standardnotes/snjs'
|
||||
import React, { useContext } from 'react'
|
||||
import styled from 'styled-components/native'
|
||||
import { Label } from './CompanySection.styled'
|
||||
import { BaseView, StyledSectionedTableCell } from './EncryptionSection.styled'
|
||||
|
||||
const Title = styled.Text`
|
||||
color: ${({ theme }) => theme.stylekitForegroundColor};
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
`
|
||||
|
||||
export const NewMobileSection = () => {
|
||||
const application = useContext(ApplicationContext)
|
||||
|
||||
if (!application) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const optIn = async () => {
|
||||
const confirmationText =
|
||||
'This will close the app and switch to the new mobile experience next time you open it. You will be able to switch back to the soon-to-be removed classic experience from the settings.'
|
||||
|
||||
if (
|
||||
await application.alertService.confirm(
|
||||
confirmationText,
|
||||
'Switch To New Mobile Experience?',
|
||||
'Switch',
|
||||
ButtonType.Info,
|
||||
)
|
||||
) {
|
||||
application.setValue(AlwaysOpenWebAppOnLaunchKey, true, StorageValueModes.Nonwrapped)
|
||||
setTimeout(() => application.deviceInterface.performSoftReset(), 1000)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TableSection>
|
||||
<SectionHeader title={'New Mobile Experience'} />
|
||||
|
||||
<StyledSectionedTableCell last={true} first={true}>
|
||||
<BaseView>
|
||||
<Title>🎉 New Mobile Experience Preview</Title>
|
||||
<Label>
|
||||
The new mobile experience (MX) brings the desktop experience you've come to love right here on your mobile
|
||||
device. This mode will replace the existing classic mobile experience soon. You can opt in below to begin
|
||||
using the new MX now.
|
||||
</Label>
|
||||
</BaseView>
|
||||
</StyledSectionedTableCell>
|
||||
|
||||
<ButtonCell leftAligned={true} title="Opt In to New Mobile Experience" onPress={optIn}></ButtonCell>
|
||||
<ButtonCell
|
||||
leftAligned={true}
|
||||
title="Read Blog Post"
|
||||
onPress={() => {
|
||||
application?.deviceInterface!.openUrl(
|
||||
'https://blog.standardnotes.com/38384/our-new-mobile-experience-is-launching-soon',
|
||||
)
|
||||
}}
|
||||
></ButtonCell>
|
||||
</TableSection>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { AlwaysOpenWebAppOnLaunchKey } from '@Lib/constants'
|
||||
import { useSignedIn } from '@Lib/SnjsHelperHooks'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { ButtonCell } from '@Root/Components/ButtonCell'
|
||||
@@ -9,7 +8,7 @@ import { TableSection } from '@Root/Components/TableSection'
|
||||
import { useSafeApplicationContext } from '@Root/Hooks/useSafeApplicationContext'
|
||||
import { ModalStackNavigationProp } from '@Root/ModalStack'
|
||||
import { SCREEN_MANAGE_SESSIONS, SCREEN_SETTINGS } from '@Root/Screens/screens'
|
||||
import { ButtonType, PrefKey, StorageValueModes } from '@standardnotes/snjs'
|
||||
import { ButtonType, PrefKey } from '@standardnotes/snjs'
|
||||
import moment from 'moment'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Platform } from 'react-native'
|
||||
@@ -222,21 +221,6 @@ export const OptionsSection = ({ title, encryptionAvailable }: Props) => {
|
||||
onPress={onExportPress}
|
||||
/>
|
||||
|
||||
<ButtonCell
|
||||
onPress={async () => {
|
||||
const confirmationText =
|
||||
'This will close the app and fully switch to the web view next time you open it. You will be able to switch back from the settings.'
|
||||
|
||||
if (
|
||||
await application.alertService.confirm(confirmationText, 'Switch To Web View?', 'Switch', ButtonType.Info)
|
||||
) {
|
||||
application.setValue(AlwaysOpenWebAppOnLaunchKey, true, StorageValueModes.Nonwrapped)
|
||||
setTimeout(() => application.deviceInterface.performSoftReset(), 1000)
|
||||
}
|
||||
}}
|
||||
title="Switch to Web View"
|
||||
/>
|
||||
|
||||
{!signedIn && (
|
||||
<SectionedAccessoryTableCell
|
||||
testID="lastExportDate"
|
||||
|
||||
@@ -9,6 +9,7 @@ import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { AuthSection } from './Sections/AuthSection'
|
||||
import { CompanySection } from './Sections/CompanySection'
|
||||
import { EncryptionSection } from './Sections/EncryptionSection'
|
||||
import { NewMobileSection } from './Sections/NewMobilePreview'
|
||||
import { OptionsSection } from './Sections/OptionsSection'
|
||||
import { PreferencesSection } from './Sections/PreferencesSection'
|
||||
import { ProtectionsSection } from './Sections/ProtectionsSection'
|
||||
@@ -54,6 +55,7 @@ export const Settings = (props: Props) => {
|
||||
<Container keyboardShouldPersistTaps={'always'} keyboardDismissMode={'interactive'}>
|
||||
<AuthSection title="Account" signedIn={signedIn} />
|
||||
<OptionsSection encryptionAvailable={!!encryptionAvailable} title="Options" />
|
||||
<NewMobileSection />
|
||||
<WorkspacesSection />
|
||||
<PreferencesSection />
|
||||
{application.hasAccount() && isEntitledToFiles && <FilesSection />}
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.20.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
### Features
|
||||
|
||||
* add custom note title format pref ([#1678](https://github.com/standardnotes/app/issues/1678)) ([11dd39c](https://github.com/standardnotes/app/commit/11dd39c126019c4295c03fb59b05ea5aa3adcd27))
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
### Features
|
||||
|
||||
* new note title format w/ prefs ([#1629](https://github.com/standardnotes/app/issues/1629)) ([d3621d7](https://github.com/standardnotes/app/commit/d3621d70b1a34537417b06830275467055b753cb))
|
||||
|
||||
## [1.18.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** adjust status bar color to match current theme ([#1624](https://github.com/standardnotes/app/issues/1624)) ([4d5429c](https://github.com/standardnotes/app/commit/4d5429cc89c15f870825f20e97844825102c6aec))
|
||||
|
||||
## [1.18.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.18.3",
|
||||
"version": "1.20.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FeatureDescription } from '@standardnotes/features'
|
||||
import { FeatureDescription, ThemeFeatureDescription } from '@standardnotes/features'
|
||||
|
||||
type ThirdPartyPackageInfo = {
|
||||
version: string
|
||||
@@ -6,3 +6,4 @@ type ThirdPartyPackageInfo = {
|
||||
}
|
||||
|
||||
export type ComponentPackageInfo = FeatureDescription & Partial<ThirdPartyPackageInfo>
|
||||
export type ThemePackageInfo = FeatureDescription & Partial<ThirdPartyPackageInfo> & ThemeFeatureDescription
|
||||
|
||||
@@ -6,11 +6,13 @@ import { HistoryEntryInterface } from '../../Runtime/History'
|
||||
import { DecryptedItemInterface, ItemInterface } from '../../Abstract/Item'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { useBoolean } from '@standardnotes/utils'
|
||||
import { ThemePackageInfo } from '../Component/PackageInfo'
|
||||
|
||||
export const isTheme = (x: ItemInterface): x is SNTheme => x.content_type === ContentType.Theme
|
||||
|
||||
export class SNTheme extends SNComponent {
|
||||
public override area: ComponentArea = ComponentArea.Themes
|
||||
public override readonly package_info!: ThemePackageInfo
|
||||
|
||||
isLayerable(): boolean {
|
||||
return useBoolean(this.package_info && this.package_info.layerable, false)
|
||||
|
||||
@@ -32,6 +32,15 @@ export enum PrefKey {
|
||||
MobileDoNotShowAgainUnsupportedEditors = 'mobileDoNotShowAgainUnsupportedEditors',
|
||||
MobileSelectedTagUuid = 'mobileSelectedTagUuid',
|
||||
MobileNotesHideEditorIcon = 'mobileHideEditorIcon',
|
||||
NewNoteTitleFormat = 'newNoteTitleFormat',
|
||||
CustomNoteTitleFormat = 'customNoteTitleFormat',
|
||||
}
|
||||
|
||||
export enum NewNoteTitleFormat {
|
||||
CurrentDateAndTime = 'CurrentDateAndTime',
|
||||
CurrentNoteCount = 'CurrentNoteCount',
|
||||
CustomFormat = 'CustomFormat',
|
||||
Empty = 'Empty',
|
||||
}
|
||||
|
||||
export type PrefValue = {
|
||||
@@ -65,4 +74,6 @@ export type PrefValue = {
|
||||
[PrefKey.MobileDoNotShowAgainUnsupportedEditors]: boolean
|
||||
[PrefKey.MobileSelectedTagUuid]: string | undefined
|
||||
[PrefKey.MobileNotesHideEditorIcon]: boolean
|
||||
[PrefKey.NewNoteTitleFormat]: NewNoteTitleFormat
|
||||
[PrefKey.CustomNoteTitleFormat]: string
|
||||
}
|
||||
|
||||
@@ -3,6 +3,170 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.100](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.99](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.98](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.97](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.96](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.95](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.94](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.93](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.92](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.91](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.90](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.89](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.88](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.87](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.86](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.85](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.84](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.83](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.82](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.81](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.80](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.79](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.78](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.75](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.74](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.73](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.72](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.71](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.70](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.69](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.68](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.67](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.66](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.65](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.64](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.63](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.62](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.61](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.60](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.59](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.3.59",
|
||||
"version": "1.3.100",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -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.10.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/responses
|
||||
|
||||
## [1.10.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/responses",
|
||||
"version": "1.10.2",
|
||||
"version": "1.10.3",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.22.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
# [1.22.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* handle android back button on android ([#1656](https://github.com/standardnotes/app/issues/1656)) ([981d8a7](https://github.com/standardnotes/app/commit/981d8a7497ea2600e1535ad9170cb5559550ca4d))
|
||||
|
||||
## [1.21.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* export/sharing notes on mobile webview ([#1644](https://github.com/standardnotes/app/issues/1644)) ([6d5ebde](https://github.com/standardnotes/app/commit/6d5ebdeaa1777f31878b6e7d4dfc33fe83abc9fc))
|
||||
|
||||
## [1.21.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.21.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** adjust status bar color to match current theme ([#1624](https://github.com/standardnotes/app/issues/1624)) ([4d5429c](https://github.com/standardnotes/app/commit/4d5429cc89c15f870825f20e97844825102c6aec))
|
||||
|
||||
## [1.21.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.21.2",
|
||||
"version": "1.22.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MobileDeviceInterface } from './../Device/MobileDeviceInterface'
|
||||
import { DesktopManagerInterface } from '../Device/DesktopManagerInterface'
|
||||
import { WebAppEvent } from '../Event/WebAppEvent'
|
||||
import { ApplicationInterface } from './ApplicationInterface'
|
||||
@@ -9,4 +10,8 @@ export interface WebApplicationInterface extends ApplicationInterface {
|
||||
handleMobileGainingFocusEvent(): Promise<void>
|
||||
handleMobileLosingFocusEvent(): Promise<void>
|
||||
handleMobileResumingFromBackgroundEvent(): Promise<void>
|
||||
isNativeMobileWeb(): boolean
|
||||
get mobileDevice(): MobileDeviceInterface
|
||||
handleAndroidBackButtonPressed(): void
|
||||
addAndroidBackHandlerEventListener(listener: () => boolean): (() => void) | undefined
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { DeviceInterface } from './DeviceInterface'
|
||||
import { Environment, RawKeychainValue } from '@standardnotes/models'
|
||||
import { Environment, Platform, RawKeychainValue } from '@standardnotes/models'
|
||||
|
||||
export interface MobileDeviceInterface extends DeviceInterface {
|
||||
environment: Environment.Mobile
|
||||
platform: Platform.Ios | Platform.Android
|
||||
|
||||
getRawKeychainValue(): Promise<RawKeychainValue | undefined>
|
||||
getDeviceBiometricsAvailability(): Promise<boolean>
|
||||
@@ -11,4 +12,8 @@ export interface MobileDeviceInterface extends DeviceInterface {
|
||||
hideMobileInterfaceFromScreenshots(): void
|
||||
stopHidingMobileInterfaceFromScreenshots(): void
|
||||
consoleLog(...args: any[]): void
|
||||
handleThemeSchemeChange(isDark: boolean): void
|
||||
shareBase64AsFile(base64: string, filename: string): Promise<void>
|
||||
downloadBase64AsFile(base64: string, filename: string, saveInTempLocation?: boolean): Promise<string | undefined>
|
||||
confirmAndExit(): void
|
||||
}
|
||||
|
||||
@@ -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.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* **crypto:** pkc algos for key generation, encrypt, and decrypt ([#1663](https://github.com/standardnotes/app/issues/1663)) ([a83003e](https://github.com/standardnotes/app/commit/a83003ee69d144c8e80eaee20bc7be2533d92c63))
|
||||
|
||||
## [1.11.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/sncrypto-common
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/sncrypto-common",
|
||||
"version": "1.11.1",
|
||||
"version": "1.12.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -26,7 +26,8 @@
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
||||
"@typescript-eslint/parser": "^5.12.1",
|
||||
"eslint-plugin-prettier": "^4.2.1"
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"reflect-metadata": "^0.1.13"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { HexString } from './HexString'
|
||||
|
||||
export type PkcKeyPair = {
|
||||
keyType: 'curve25519' | 'ed25519' | 'x25519'
|
||||
privateKey: HexString
|
||||
publicKey: HexString
|
||||
}
|
||||
@@ -8,3 +8,4 @@ export * from './StreamDecryptorResult'
|
||||
export * from './StreamEncryptor'
|
||||
export * from './Unencrypted'
|
||||
export * from './Utf8String'
|
||||
export * from './PkcKeyPair'
|
||||
|
||||
@@ -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.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* **crypto:** pkc algos for key generation, encrypt, and decrypt ([#1663](https://github.com/standardnotes/app/issues/1663)) ([a83003e](https://github.com/standardnotes/app/commit/a83003ee69d144c8e80eaee20bc7be2533d92c63))
|
||||
|
||||
## [1.12.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/sncrypto-web
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/sncrypto-web",
|
||||
"version": "1.12.3",
|
||||
"version": "1.13.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -39,11 +39,13 @@
|
||||
"babel-loader": "^8.2.3",
|
||||
"chai": "^4.3.6",
|
||||
"connect": "^3.7.0",
|
||||
"eslint": "*",
|
||||
"eslint-plugin-prettier": "*",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"serve-static": "^1.14.2",
|
||||
"ts-loader": "^9.2.6",
|
||||
"typedarray-to-buffer": "^4.0.0",
|
||||
"typescript": "*",
|
||||
"uuid": "^8.3.2",
|
||||
"webpack": "*",
|
||||
"webpack-cli": "*"
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
PureCryptoInterface,
|
||||
Utf8String,
|
||||
timingSafeEqual,
|
||||
PkcKeyPair,
|
||||
} from '@standardnotes/sncrypto-common'
|
||||
import * as Utils from './utils'
|
||||
import * as sodium from './libsodium'
|
||||
@@ -83,7 +84,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
iterations: number,
|
||||
length: number,
|
||||
): Promise<HexString | null> {
|
||||
const keyData = await Utils.stringToArrayBuffer(password)
|
||||
const keyData = Utils.stringToArrayBuffer(password)
|
||||
const key = await this.webCryptoImportKey(keyData, WebCryptoAlgs.Pbkdf2, [WebCryptoActions.DeriveBits])
|
||||
if (!key) {
|
||||
console.error('Key is null, unable to continue')
|
||||
@@ -99,21 +100,21 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
}
|
||||
|
||||
public async aes256CbcEncrypt(plaintext: Utf8String, iv: HexString, key: HexString): Promise<Base64String> {
|
||||
const keyData = await Utils.hexStringToArrayBuffer(key)
|
||||
const ivData = await Utils.hexStringToArrayBuffer(iv)
|
||||
const keyData = Utils.hexStringToArrayBuffer(key)
|
||||
const ivData = Utils.hexStringToArrayBuffer(iv)
|
||||
const alg = { name: WebCryptoAlgs.AesCbc, iv: ivData }
|
||||
const importedKeyData = await this.webCryptoImportKey(keyData, alg.name, [WebCryptoActions.Encrypt])
|
||||
const textData = await Utils.stringToArrayBuffer(plaintext)
|
||||
const textData = Utils.stringToArrayBuffer(plaintext)
|
||||
const result = await crypto.subtle.encrypt(alg, importedKeyData, textData)
|
||||
return Utils.arrayBufferToBase64(result)
|
||||
}
|
||||
|
||||
public async aes256CbcDecrypt(ciphertext: Base64String, iv: HexString, key: HexString): Promise<Utf8String | null> {
|
||||
const keyData = await Utils.hexStringToArrayBuffer(key)
|
||||
const ivData = await Utils.hexStringToArrayBuffer(iv)
|
||||
const keyData = Utils.hexStringToArrayBuffer(key)
|
||||
const ivData = Utils.hexStringToArrayBuffer(iv)
|
||||
const alg = { name: WebCryptoAlgs.AesCbc, iv: ivData }
|
||||
const importedKeyData = await this.webCryptoImportKey(keyData, alg.name, [WebCryptoActions.Decrypt])
|
||||
const textData = await Utils.base64ToArrayBuffer(ciphertext)
|
||||
const textData = Utils.base64ToArrayBuffer(ciphertext)
|
||||
|
||||
try {
|
||||
const result = await crypto.subtle.decrypt(alg, importedKeyData, textData)
|
||||
@@ -150,11 +151,11 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
}
|
||||
|
||||
public async hmac1(message: Utf8String, key: HexString): Promise<HexString | null> {
|
||||
const keyHexData = await Utils.hexStringToArrayBuffer(key)
|
||||
const keyHexData = Utils.hexStringToArrayBuffer(key)
|
||||
const keyData = await this.webCryptoImportKey(keyHexData, WebCryptoAlgs.Hmac, [WebCryptoActions.Sign], {
|
||||
name: WebCryptoAlgs.Sha1,
|
||||
})
|
||||
const messageData = await Utils.stringToArrayBuffer(message)
|
||||
const messageData = Utils.stringToArrayBuffer(message)
|
||||
const funcParams = { name: WebCryptoAlgs.Hmac }
|
||||
|
||||
try {
|
||||
@@ -169,15 +170,14 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
}
|
||||
|
||||
public async unsafeSha1(text: string): Promise<string> {
|
||||
const textData = await Utils.stringToArrayBuffer(text)
|
||||
const textData = Utils.stringToArrayBuffer(text)
|
||||
const digest = await crypto.subtle.digest(WebCryptoAlgs.Sha1, textData)
|
||||
return Utils.arrayBufferToHexString(digest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a raw string key to a WebCrypto CryptoKey object.
|
||||
* @param rawKey
|
||||
* A plain utf8 string or an array buffer
|
||||
* @param keyData
|
||||
* @param alg
|
||||
* The name of the algorithm this key will be used for (i.e 'AES-CBC' or 'HMAC')
|
||||
* @param actions
|
||||
@@ -220,7 +220,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
): Promise<HexString> {
|
||||
const params = {
|
||||
name: WebCryptoAlgs.Pbkdf2,
|
||||
salt: await Utils.stringToArrayBuffer(salt),
|
||||
salt: Utils.stringToArrayBuffer(salt),
|
||||
iterations: iterations,
|
||||
hash: { name: WebCryptoAlgs.Sha512 },
|
||||
}
|
||||
@@ -298,13 +298,13 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
public xchacha20StreamEncryptorPush(
|
||||
encryptor: StreamEncryptor,
|
||||
plainBuffer: Uint8Array,
|
||||
assocData: Utf8String,
|
||||
assocData?: Utf8String,
|
||||
tag: SodiumConstant = SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH,
|
||||
): Uint8Array {
|
||||
const encryptedBuffer = sodium.crypto_secretstream_xchacha20poly1305_push(
|
||||
encryptor.state as sodium.StateAddress,
|
||||
plainBuffer,
|
||||
assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null,
|
||||
assocData && assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null,
|
||||
tag,
|
||||
)
|
||||
return encryptedBuffer
|
||||
@@ -325,7 +325,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
public xchacha20StreamDecryptorPush(
|
||||
decryptor: StreamDecryptor,
|
||||
encryptedBuffer: Uint8Array,
|
||||
assocData: Utf8String,
|
||||
assocData?: Utf8String,
|
||||
): StreamDecryptorResult | false {
|
||||
if (encryptedBuffer.length < SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES) {
|
||||
throw new Error('Invalid ciphertext size')
|
||||
@@ -334,7 +334,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
const result = sodium.crypto_secretstream_xchacha20poly1305_pull(
|
||||
decryptor.state as sodium.StateAddress,
|
||||
encryptedBuffer,
|
||||
assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null,
|
||||
assocData && assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null,
|
||||
)
|
||||
|
||||
if ((result as unknown) === false) {
|
||||
@@ -344,6 +344,51 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* https://doc.libsodium.org/public-key_cryptography/authenticated_encryption
|
||||
*/
|
||||
public sodiumCryptoBoxEasyEncrypt(
|
||||
message: Utf8String,
|
||||
nonce: HexString,
|
||||
senderSecretKey: HexString,
|
||||
recipientPublicKey: HexString,
|
||||
): Base64String {
|
||||
const result = sodium.crypto_box_easy(
|
||||
message,
|
||||
Utils.hexStringToArrayBuffer(nonce),
|
||||
Utils.hexStringToArrayBuffer(recipientPublicKey),
|
||||
Utils.hexStringToArrayBuffer(senderSecretKey),
|
||||
)
|
||||
|
||||
return Utils.arrayBufferToBase64(result)
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxEasyDecrypt(
|
||||
ciphertext: Base64String,
|
||||
nonce: HexString,
|
||||
senderPublicKey: HexString,
|
||||
recipientSecretKey: HexString,
|
||||
): Base64String {
|
||||
const result = sodium.crypto_box_open_easy(
|
||||
Utils.base64ToArrayBuffer(ciphertext),
|
||||
Utils.hexStringToArrayBuffer(nonce),
|
||||
Utils.hexStringToArrayBuffer(senderPublicKey),
|
||||
Utils.hexStringToArrayBuffer(recipientSecretKey),
|
||||
'text',
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxGenerateKeypair(): PkcKeyPair {
|
||||
const result = sodium.crypto_box_keypair()
|
||||
|
||||
const publicKey = Utils.arrayBufferToHexString(result.publicKey)
|
||||
const privateKey = Utils.arrayBufferToHexString(result.privateKey)
|
||||
|
||||
return { publicKey, privateKey, keyType: result.keyType }
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random secret for TOTP authentication
|
||||
*
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
export { SNWebCrypto } from './crypto'
|
||||
export {
|
||||
arrayBufferToBase64,
|
||||
arrayBufferToHexString,
|
||||
arrayBufferToString,
|
||||
base64Decode,
|
||||
base64Encode,
|
||||
base64ToArrayBuffer,
|
||||
base64ToHex,
|
||||
hexStringToArrayBuffer,
|
||||
hexToBase64,
|
||||
isWebCryptoAvailable,
|
||||
stringToArrayBuffer,
|
||||
base32Decode,
|
||||
base32Encode,
|
||||
} from './utils'
|
||||
export * from './crypto'
|
||||
export * from './utils'
|
||||
export { SodiumConstant } from '@standardnotes/sncrypto-common'
|
||||
|
||||
@@ -3,12 +3,15 @@ export {
|
||||
base64_variants,
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt,
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt,
|
||||
crypto_secretstream_xchacha20poly1305_push,
|
||||
crypto_secretstream_xchacha20poly1305_pull,
|
||||
crypto_secretstream_xchacha20poly1305_init_push,
|
||||
crypto_secretstream_xchacha20poly1305_init_pull,
|
||||
crypto_box_easy,
|
||||
crypto_box_keypair,
|
||||
crypto_box_open_easy,
|
||||
crypto_pwhash_ALG_DEFAULT,
|
||||
crypto_pwhash,
|
||||
crypto_secretstream_xchacha20poly1305_init_pull,
|
||||
crypto_secretstream_xchacha20poly1305_init_push,
|
||||
crypto_secretstream_xchacha20poly1305_pull,
|
||||
crypto_secretstream_xchacha20poly1305_push,
|
||||
from_base64,
|
||||
from_hex,
|
||||
from_string,
|
||||
|
||||
@@ -50,31 +50,18 @@ describe('crypto operations', async function () {
|
||||
|
||||
it('compares strings with timing safe comparison', async function () {
|
||||
const crypto = new SNWebCrypto()
|
||||
expect(crypto.timingSafeEqual('hello world 🌍', 'hello world 🌍')).to.equal(
|
||||
true,
|
||||
)
|
||||
expect(crypto.timingSafeEqual('helo world 🌍', 'hello world 🌍')).to.equal(
|
||||
false,
|
||||
)
|
||||
expect(crypto.timingSafeEqual('hello world 🌍', 'hello world 🌍')).to.equal(true)
|
||||
expect(crypto.timingSafeEqual('helo world 🌍', 'hello world 🌍')).to.equal(false)
|
||||
expect(crypto.timingSafeEqual('', 'a')).to.equal(false)
|
||||
expect(crypto.timingSafeEqual('', '')).to.equal(true)
|
||||
expect(
|
||||
crypto.timingSafeEqual(
|
||||
'2e1ee7920bb188a88f94bb912153befd83cc55cd',
|
||||
'2e1ee7920bb188a88f94bb912153befd83cc55cd',
|
||||
),
|
||||
crypto.timingSafeEqual('2e1ee7920bb188a88f94bb912153befd83cc55cd', '2e1ee7920bb188a88f94bb912153befd83cc55cd'),
|
||||
).to.equal(true)
|
||||
expect(
|
||||
crypto.timingSafeEqual(
|
||||
'1e1ee7920bb188a88f94bb912153befd83cc55cd',
|
||||
'2e1ee7920bb188a88f94bb912153befd83cc55cd',
|
||||
),
|
||||
crypto.timingSafeEqual('1e1ee7920bb188a88f94bb912153befd83cc55cd', '2e1ee7920bb188a88f94bb912153befd83cc55cd'),
|
||||
).to.equal(false)
|
||||
expect(
|
||||
crypto.timingSafeEqual(
|
||||
'2e1ee7920bb188a88f94bb912153befd83cc55cc',
|
||||
'2e1ee7920bb188a88f94bb912153befd83cc55cd',
|
||||
),
|
||||
crypto.timingSafeEqual('2e1ee7920bb188a88f94bb912153befd83cc55cc', '2e1ee7920bb188a88f94bb912153befd83cc55cd'),
|
||||
).to.equal(false)
|
||||
})
|
||||
|
||||
@@ -85,22 +72,16 @@ describe('crypto operations', async function () {
|
||||
|
||||
it('pbkdf2 1', async function () {
|
||||
const password = 'very_secure🔒'
|
||||
const salt =
|
||||
'c3feb78823adce65c4ab024dab9c5cdcda5a04cdbd98f65eac0311dfa432d67b'
|
||||
const expected =
|
||||
'bbb3d3af19dd1cbb901c958003faa55f193aad6a57fff30e51a62591bdc054d8'
|
||||
const salt = 'c3feb78823adce65c4ab024dab9c5cdcda5a04cdbd98f65eac0311dfa432d67b'
|
||||
const expected = 'bbb3d3af19dd1cbb901c958003faa55f193aad6a57fff30e51a62591bdc054d8'
|
||||
const result = await webCrypto.pbkdf2(password, salt, 100000, 256)
|
||||
expect(result).to.equal(expected)
|
||||
})
|
||||
|
||||
it('pbkdf2 2', async function () {
|
||||
const password = 'correct horse battery staple ✅'
|
||||
const salt = Buffer.from(
|
||||
'808182838485868788898a8b8c8d8e8f',
|
||||
'hex',
|
||||
).toString('utf8')
|
||||
const expected =
|
||||
'795d83b18e55d860d3799f85a20f66ee17eb9dcf041df1d7a13fac30af7103d9'
|
||||
const salt = Buffer.from('808182838485868788898a8b8c8d8e8f', 'hex').toString('utf8')
|
||||
const expected = '795d83b18e55d860d3799f85a20f66ee17eb9dcf041df1d7a13fac30af7103d9'
|
||||
const result = await webCrypto.pbkdf2(password, salt, 100000, 256)
|
||||
expect(result).to.equal(expected)
|
||||
})
|
||||
@@ -116,19 +97,16 @@ describe('crypto operations', async function () {
|
||||
|
||||
it('hmac 256', async function () {
|
||||
const text = 'hello world 🌍'
|
||||
const key =
|
||||
'e802dc953f3f1f7b5db62409b74ac848559d4711c4e0047ecc5e312ad8ab8397'
|
||||
const key = 'e802dc953f3f1f7b5db62409b74ac848559d4711c4e0047ecc5e312ad8ab8397'
|
||||
const hash = await webCrypto.hmac256(text, key)
|
||||
const expected =
|
||||
'b63f94ee33a067ffac3ee97c7987dd3171dcdc747a322bb3f3ab890201c8e6f9'
|
||||
const expected = 'b63f94ee33a067ffac3ee97c7987dd3171dcdc747a322bb3f3ab890201c8e6f9'
|
||||
expect(hash).to.equal(expected)
|
||||
})
|
||||
|
||||
it('sha256', async function () {
|
||||
const text = 'hello world 🌍'
|
||||
const hash = await webCrypto.sha256(text)
|
||||
const expected =
|
||||
'1e71fe32476da1ff115b44dfd74aed5c90d68a1d80a2033065e30cff4335211a'
|
||||
const expected = '1e71fe32476da1ff115b44dfd74aed5c90d68a1d80a2033065e30cff4335211a'
|
||||
expect(hash).to.equal(expected)
|
||||
})
|
||||
|
||||
@@ -154,13 +132,7 @@ describe('crypto operations', async function () {
|
||||
const bytes = 67108864
|
||||
const length = 16
|
||||
const iterations = 2
|
||||
const result = await webCrypto.argon2(
|
||||
password,
|
||||
salt,
|
||||
iterations,
|
||||
bytes,
|
||||
length,
|
||||
)
|
||||
const result = await webCrypto.argon2(password, salt, iterations, bytes, length)
|
||||
const expectedResult = '18dfbc268f251701652c8e38b5273f73'
|
||||
expect(result).to.equal(expectedResult)
|
||||
})
|
||||
@@ -172,15 +144,8 @@ describe('crypto operations', async function () {
|
||||
const bytes = 67108864
|
||||
const length = 32
|
||||
const iterations = 5
|
||||
const result = await webCrypto.argon2(
|
||||
password,
|
||||
truncatedSalt,
|
||||
iterations,
|
||||
bytes,
|
||||
length,
|
||||
)
|
||||
const expected =
|
||||
'bb6ec440708c271ce34decd7f997e2444d309b1105992779ccdb47f78a5fda6f'
|
||||
const result = await webCrypto.argon2(password, truncatedSalt, iterations, bytes, length)
|
||||
const expected = 'bb6ec440708c271ce34decd7f997e2444d309b1105992779ccdb47f78a5fda6f'
|
||||
expect(result).to.equal(expected)
|
||||
})
|
||||
|
||||
@@ -189,69 +154,38 @@ describe('crypto operations', async function () {
|
||||
const nonce = await webCrypto.generateRandomKey(192)
|
||||
const plaintext = 'hello world 🌍'
|
||||
const aad = JSON.stringify({ uuid: '123🎤' })
|
||||
const ciphertext = await webCrypto.xchacha20Encrypt(
|
||||
plaintext,
|
||||
nonce,
|
||||
key,
|
||||
aad,
|
||||
)
|
||||
const decrypted = await webCrypto.xchacha20Decrypt(
|
||||
ciphertext,
|
||||
nonce,
|
||||
key,
|
||||
aad,
|
||||
)
|
||||
const ciphertext = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, aad)
|
||||
const decrypted = await webCrypto.xchacha20Decrypt(ciphertext, nonce, key, aad)
|
||||
expect(decrypted).to.equal(plaintext)
|
||||
})
|
||||
|
||||
it('xchacha20 streaming encrypt/decrypt', async function () {
|
||||
const key = await webCrypto.generateRandomKey(256)
|
||||
const bigFile = await fetch(
|
||||
'http://localhost:9003/test/resources/big_file.md',
|
||||
)
|
||||
const bigFile = await fetch('http://localhost:9003/test/resources/big_file.md')
|
||||
const bigText = await bigFile.text()
|
||||
const plaintext = bigText
|
||||
const plainBuffer = stringToArrayBuffer(plaintext)
|
||||
const encryptor = webCrypto.xchacha20StreamEncryptInitEncryptor(key)
|
||||
const header = base64StringToArrayBuffer(encryptor.header)
|
||||
const encryptor = webCrypto.xchacha20StreamInitEncryptor(key)
|
||||
|
||||
let encryptedBuffer = Buffer.concat([header])
|
||||
const headerBase64 = encryptor.header
|
||||
const headerBuffer = base64ToArrayBuffer(encryptor.header)
|
||||
|
||||
let encryptedBuffer = Buffer.concat([headerBuffer])
|
||||
const pushChunkSize = plainBuffer.length / 200
|
||||
const pullChunkSize =
|
||||
pushChunkSize +
|
||||
SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES
|
||||
const pullChunkSize = pushChunkSize + SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES
|
||||
|
||||
for (let i = 0; i < plainBuffer.length; i += pushChunkSize) {
|
||||
const readUntil =
|
||||
i + pushChunkSize > plainBuffer.length
|
||||
? plainBuffer.length
|
||||
: i + pushChunkSize
|
||||
const chunk = webCrypto.xchacha20StreamEncryptorPush(
|
||||
encryptor,
|
||||
plainBuffer.slice(i, readUntil),
|
||||
)
|
||||
const readUntil = i + pushChunkSize > plainBuffer.length ? plainBuffer.length : i + pushChunkSize
|
||||
const chunk = webCrypto.xchacha20StreamEncryptorPush(encryptor, plainBuffer.slice(i, readUntil))
|
||||
encryptedBuffer = Buffer.concat([encryptedBuffer, chunk])
|
||||
}
|
||||
|
||||
const decryptor = webCrypto.xchacha20StreamEncryptInitDecryptor(
|
||||
header,
|
||||
key,
|
||||
)
|
||||
const decryptor = webCrypto.xchacha20StreamInitDecryptor(headerBase64, key)
|
||||
|
||||
let decryptedBuffer = Buffer.alloc(0)
|
||||
for (
|
||||
let i = header.length;
|
||||
i < encryptedBuffer.length;
|
||||
i += pullChunkSize
|
||||
) {
|
||||
const readUntil =
|
||||
i + pullChunkSize > encryptedBuffer.length
|
||||
? encryptedBuffer.length
|
||||
: i + pullChunkSize
|
||||
const chunk = webCrypto.xchacha20StreamDecryptorPush(
|
||||
decryptor,
|
||||
encryptedBuffer.slice(i, readUntil),
|
||||
)
|
||||
for (let i = headerBuffer.length; i < encryptedBuffer.length; i += pullChunkSize) {
|
||||
const readUntil = i + pullChunkSize > encryptedBuffer.length ? encryptedBuffer.length : i + pullChunkSize
|
||||
const chunk = webCrypto.xchacha20StreamDecryptorPush(decryptor, encryptedBuffer.slice(i, readUntil))
|
||||
decryptedBuffer = Buffer.concat([decryptedBuffer, chunk.message])
|
||||
}
|
||||
|
||||
@@ -263,18 +197,8 @@ describe('crypto operations', async function () {
|
||||
const key = await webCrypto.generateRandomKey(256)
|
||||
const nonce = await webCrypto.generateRandomKey(192)
|
||||
const plaintext = 'hello world 🌍'
|
||||
const ciphertext = await webCrypto.xchacha20Encrypt(
|
||||
plaintext,
|
||||
nonce,
|
||||
key,
|
||||
JSON.stringify({ uuid: 'foo🎲' }),
|
||||
)
|
||||
const result = await webCrypto.xchacha20Decrypt(
|
||||
ciphertext,
|
||||
nonce,
|
||||
key,
|
||||
JSON.stringify({ uuid: 'bar🎲' }),
|
||||
)
|
||||
const ciphertext = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, JSON.stringify({ uuid: 'foo🎲' }))
|
||||
const result = await webCrypto.xchacha20Decrypt(ciphertext, nonce, key, JSON.stringify({ uuid: 'bar🎲' }))
|
||||
expect(result).to.not.be.ok
|
||||
})
|
||||
|
||||
@@ -284,23 +208,12 @@ describe('crypto operations', async function () {
|
||||
"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.🌞"
|
||||
const assocData = await hexStringToArrayBuffer('50515253c0c1c2c3c4c5c6c7')
|
||||
const nonce = '404142434445464748494a4b4c4d4e4f5051525354555657'
|
||||
const key =
|
||||
'808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'
|
||||
const ciphertext = await webCrypto.xchacha20Encrypt(
|
||||
plaintext,
|
||||
nonce,
|
||||
key,
|
||||
assocData,
|
||||
)
|
||||
const key = '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'
|
||||
const ciphertext = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, assocData)
|
||||
const expected =
|
||||
'vW0XnT6D1DuVdleUk8DpOVcqFwAlK/rMvtKQLCE5bLtzHH8bC0qmRAvzqC9O2n45rmTGcIxUwhbLlrcuEhO0Ui+Mm6QNtdlFsRtpuYLBu54/P6wrw2lIj3ayODVl0//5IflmTJdjfal2iBL2FcaLE7UuOqw6kdl7HV6PKzn0pIOeHH3rkwQ='
|
||||
expect(ciphertext).to.equal(expected)
|
||||
const decrypted = await webCrypto.xchacha20Decrypt(
|
||||
ciphertext,
|
||||
nonce,
|
||||
key,
|
||||
assocData,
|
||||
)
|
||||
const decrypted = await webCrypto.xchacha20Decrypt(ciphertext, nonce, key, assocData)
|
||||
expect(decrypted).to.equal(plaintext)
|
||||
})
|
||||
|
||||
@@ -315,16 +228,10 @@ describe('crypto operations', async function () {
|
||||
).toString('utf8')
|
||||
const assocData = Buffer.from('50515253c0c1c2c3c4c5c6c7', 'hex')
|
||||
const nonce = '404142434445464748494a4b4c4d4e4f5051525354555657'
|
||||
const key =
|
||||
'808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'
|
||||
const key = '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'
|
||||
|
||||
/** Encrypt */
|
||||
const ciphertextBase64 = await webCrypto.xchacha20Encrypt(
|
||||
plaintext,
|
||||
nonce,
|
||||
key,
|
||||
assocData,
|
||||
)
|
||||
const ciphertextBase64 = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, assocData)
|
||||
const ciphertextHex = await base64ToHex(ciphertextBase64)
|
||||
const expected =
|
||||
'bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb' +
|
||||
@@ -335,12 +242,7 @@ describe('crypto operations', async function () {
|
||||
expect(ciphertextHex).to.equal(expected)
|
||||
|
||||
/** Decrypt */
|
||||
const decrypted = await webCrypto.xchacha20Decrypt(
|
||||
ciphertextBase64,
|
||||
nonce,
|
||||
key,
|
||||
assocData,
|
||||
)
|
||||
const decrypted = await webCrypto.xchacha20Decrypt(ciphertextBase64, nonce, key, assocData)
|
||||
expect(decrypted).to.equal(plaintext)
|
||||
})
|
||||
|
||||
@@ -351,17 +253,44 @@ describe('crypto operations', async function () {
|
||||
const bytes = 67108864
|
||||
const length = 16
|
||||
const iterations = 2
|
||||
const result = await webCrypto.argon2(
|
||||
password,
|
||||
salt,
|
||||
iterations,
|
||||
bytes,
|
||||
length,
|
||||
)
|
||||
const result = await webCrypto.argon2(password, salt, iterations, bytes, length)
|
||||
const expectedResult = '720f95400220748a811bca9b8cff5d6e'
|
||||
expect(result).to.equal(expectedResult)
|
||||
})
|
||||
|
||||
it('pkc crypto_box_easy keypair generation', async function () {
|
||||
const keypair = await webCrypto.sodiumCryptoBoxGenerateKeypair()
|
||||
expect(keypair.keyType).to.equal('x25519')
|
||||
expect(keypair.publicKey.length).to.equal(64)
|
||||
expect(keypair.privateKey.length).to.equal(64)
|
||||
})
|
||||
|
||||
it('pkc crypto_box_easy encrypt/decrypt', async function () {
|
||||
const senderKeypair = await webCrypto.sodiumCryptoBoxGenerateKeypair()
|
||||
const recipientKeypair = await webCrypto.sodiumCryptoBoxGenerateKeypair()
|
||||
|
||||
const nonce = await webCrypto.generateRandomKey(192)
|
||||
const plaintext = 'hello world 🌍'
|
||||
|
||||
const ciphertext = await webCrypto.sodiumCryptoBoxEasyEncrypt(
|
||||
plaintext,
|
||||
nonce,
|
||||
senderKeypair.privateKey,
|
||||
recipientKeypair.publicKey,
|
||||
)
|
||||
|
||||
expect(ciphertext.length).to.equal(44)
|
||||
|
||||
const decrypted = await webCrypto.sodiumCryptoBoxEasyDecrypt(
|
||||
ciphertext,
|
||||
nonce,
|
||||
senderKeypair.publicKey,
|
||||
recipientKeypair.privateKey,
|
||||
)
|
||||
|
||||
expect(decrypted).to.equal(plaintext)
|
||||
})
|
||||
|
||||
it('generates random OTP secret 160 bits long', async function () {
|
||||
const secret = await webCrypto.generateOtpSecret()
|
||||
expect(secret).to.have.length(32)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
||||
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||
<script src="../../../node_modules/regenerator-runtime/runtime.js"></script>
|
||||
<script src="https://unpkg.com/regenerator-runtime@0.13.9/runtime.js"></script>
|
||||
<script src="../dist/sncrypto-web.js"></script>
|
||||
<script>
|
||||
Object.assign(window, SNCrypto);
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('utils', async function () {
|
||||
|
||||
it('base64URLEncode', async function () {
|
||||
const str = 'hello world'
|
||||
const b64 = await base64Encode(str)
|
||||
const b64 = await base64URLEncode(str)
|
||||
expect(b64).to.equal('aGVsbG8gd29ybGQ')
|
||||
})
|
||||
|
||||
|
||||
@@ -3,6 +3,75 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.134.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
# [2.134.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
### Features
|
||||
|
||||
* **snjs:** add e2e test for subsequent subscriptions settings persistance ([5ffffbf](https://github.com/standardnotes/app/commit/5ffffbff202ab037b22a72c64fe34673fe49ecaf))
|
||||
|
||||
## [2.133.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **snjs:** increase wait time in retrieving settings test for processing replaceable settings ([3322600](https://github.com/standardnotes/app/commit/33226000f58f5b7f97882479e62de3b24a6b217d))
|
||||
* **snjs:** instructions on running e2e locally ([c841ac9](https://github.com/standardnotes/app/commit/c841ac99d2e51ecca05f4a349efa1a0acb09a86d))
|
||||
|
||||
## [2.133.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
# [2.133.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* handle android back button on android ([#1656](https://github.com/standardnotes/app/issues/1656)) ([981d8a7](https://github.com/standardnotes/app/commit/981d8a7497ea2600e1535ad9170cb5559550ca4d))
|
||||
|
||||
## [2.132.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.132.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.132.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.132.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* biometrics timing loading ([#1622](https://github.com/standardnotes/app/issues/1622)) ([3c0d4fa](https://github.com/standardnotes/app/commit/3c0d4fac766c3a31bdbebe4f7e88f83ba1ea58da))
|
||||
|
||||
## [2.132.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "Revert "feat(web): move emails notifications to one pane and enable opening settings via url (#1614)" (#1617)" (#1619) ([e50f4eb](https://github.com/standardnotes/app/commit/e50f4eb92efd65b0b8fa3a5001b8dfb611f60180)), closes [#1614](https://github.com/standardnotes/app/issues/1614) [#1617](https://github.com/standardnotes/app/issues/1617) [#1619](https://github.com/standardnotes/app/issues/1619)
|
||||
|
||||
## [2.132.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat(web): move emails notifications to one pane and enable opening settings via url (#1614)" (#1617) ([6824dd2](https://github.com/standardnotes/app/commit/6824dd2d5fe4a6d427ff5465540f3b7865933e98)), closes [#1614](https://github.com/standardnotes/app/issues/1614) [#1617](https://github.com/standardnotes/app/issues/1617)
|
||||
|
||||
# [2.132.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Features
|
||||
|
||||
* **web:** move emails notifications to one pane and enable opening settings via url ([#1614](https://github.com/standardnotes/app/issues/1614)) ([1fe833b](https://github.com/standardnotes/app/commit/1fe833bc24a3a426a7230381704d418624cc2b82))
|
||||
|
||||
# [2.131.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
* **snjs:** add retrieving web sockets token for establishing connection ([#1602](https://github.com/standardnotes/app/issues/1602)) ([e809328](https://github.com/standardnotes/app/commit/e809328fa86d49f76d24bc0d95db010e483c1853))
|
||||
|
||||
## [2.130.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -44,8 +44,9 @@ To run a stable server environment for E2E tests that is up to date with product
|
||||
|
||||
```
|
||||
yarn install --immutable
|
||||
yarn test:stable-server
|
||||
yarn start:local-server
|
||||
```
|
||||
Wait for the `All services are up!` message.
|
||||
|
||||
Once the server infrastructure is ready, and you've built all packages, you can run the test suite in the browser via:
|
||||
|
||||
@@ -53,6 +54,11 @@ Once the server infrastructure is ready, and you've built all packages, you can
|
||||
yarn start:server:e2e
|
||||
```
|
||||
|
||||
Once you are finished you can close the running local server on E2E repo by typing:
|
||||
```
|
||||
yarn stop:local-server
|
||||
```
|
||||
|
||||
### Unit Tests
|
||||
|
||||
From the root of the repository, run:
|
||||
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
UserRegistrationResponseBody,
|
||||
UserServer,
|
||||
UserServerInterface,
|
||||
WebSocketApiService,
|
||||
WebSocketApiServiceInterface,
|
||||
WebSocketServer,
|
||||
WebSocketServerInterface,
|
||||
} from '@standardnotes/api'
|
||||
import * as Common from '@standardnotes/common'
|
||||
import * as ExternalServices from '@standardnotes/services'
|
||||
@@ -108,6 +112,8 @@ export class SNApplication
|
||||
private declare subscriptionApiService: SubscriptionApiServiceInterface
|
||||
private declare subscriptionServer: SubscriptionServerInterface
|
||||
private declare subscriptionManager: SubscriptionClientInterface
|
||||
private declare webSocketApiService: WebSocketApiServiceInterface
|
||||
private declare webSocketServer: WebSocketServerInterface
|
||||
private sessionManager!: InternalServices.SNSessionManager
|
||||
private syncService!: InternalServices.SNSyncService
|
||||
private challengeService!: InternalServices.ChallengeService
|
||||
@@ -329,7 +335,7 @@ export class SNApplication
|
||||
|
||||
this.apiService.loadHost()
|
||||
this.webSocketsService.loadWebSocketUrl()
|
||||
this.sessionManager.initializeFromDisk()
|
||||
await this.sessionManager.initializeFromDisk()
|
||||
|
||||
this.settingsService.initializeFromDisk()
|
||||
|
||||
@@ -954,10 +960,6 @@ export class SNApplication
|
||||
| undefined
|
||||
}
|
||||
|
||||
async loadMobileUnlockTiming() {
|
||||
return this.protectionService.loadMobileUnlockTiming()
|
||||
}
|
||||
|
||||
getBiometricsTimingOptions() {
|
||||
return this.protectionService.getBiometricsTimingOptions()
|
||||
}
|
||||
@@ -1094,6 +1096,8 @@ export class SNApplication
|
||||
this.createUserApiService()
|
||||
this.createSubscriptionServer()
|
||||
this.createSubscriptionApiService()
|
||||
this.createWebSocketServer()
|
||||
this.createWebSocketApiService()
|
||||
this.createSubscriptionManager()
|
||||
this.createWebSocketsService()
|
||||
this.createSessionManager()
|
||||
@@ -1135,6 +1139,8 @@ export class SNApplication
|
||||
;(this.userServer as unknown) = undefined
|
||||
;(this.subscriptionApiService as unknown) = undefined
|
||||
;(this.subscriptionServer as unknown) = undefined
|
||||
;(this.webSocketApiService as unknown) = undefined
|
||||
;(this.webSocketServer as unknown) = undefined
|
||||
;(this.subscriptionManager as unknown) = undefined
|
||||
;(this.sessionManager as unknown) = undefined
|
||||
;(this.syncService as unknown) = undefined
|
||||
@@ -1252,6 +1258,7 @@ export class SNApplication
|
||||
this.webSocketsService = new InternalServices.SNWebSocketsService(
|
||||
this.diskStorageService,
|
||||
this.options.webSocketUrl,
|
||||
this.webSocketApiService,
|
||||
this.internalEventBus,
|
||||
)
|
||||
this.services.push(this.webSocketsService)
|
||||
@@ -1337,6 +1344,14 @@ export class SNApplication
|
||||
this.subscriptionServer = new SubscriptionServer(this.httpService)
|
||||
}
|
||||
|
||||
private createWebSocketApiService() {
|
||||
this.webSocketApiService = new WebSocketApiService(this.webSocketServer)
|
||||
}
|
||||
|
||||
private createWebSocketServer() {
|
||||
this.webSocketServer = new WebSocketServer(this.httpService)
|
||||
}
|
||||
|
||||
private createSubscriptionManager() {
|
||||
this.subscriptionManager = new SubscriptionManager(this.subscriptionApiService, this.internalEventBus)
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ export enum ReactNativeToWebEvent {
|
||||
ResumingFromBackground = 'ResumingFromBackground',
|
||||
GainingFocus = 'GainingFocus',
|
||||
LosingFocus = 'LosingFocus',
|
||||
AndroidBackButtonPressed = 'AndroidBackButtonPressed',
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { InternalEventBusInterface } from '@standardnotes/services'
|
||||
import { WebSocketApiServiceInterface } from '@standardnotes/api'
|
||||
|
||||
import { StorageKey, DiskStorageService } from '@Lib/index'
|
||||
import { SNWebSocketsService } from './WebsocketsService'
|
||||
|
||||
@@ -6,10 +8,11 @@ describe('webSocketsService', () => {
|
||||
const webSocketUrl = ''
|
||||
|
||||
let storageService: DiskStorageService
|
||||
let webSocketApiService: WebSocketApiServiceInterface
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createService = () => {
|
||||
return new SNWebSocketsService(storageService, webSocketUrl, internalEventBus)
|
||||
return new SNWebSocketsService(storageService, webSocketUrl, webSocketApiService, internalEventBus)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -18,6 +21,9 @@ describe('webSocketsService', () => {
|
||||
|
||||
internalEventBus = {} as jest.Mocked<InternalEventBusInterface>
|
||||
internalEventBus.publish = jest.fn()
|
||||
|
||||
webSocketApiService = {} as jest.Mocked<WebSocketApiServiceInterface>
|
||||
webSocketApiService.createConnectionToken = jest.fn().mockReturnValue({ token: 'foobar' })
|
||||
})
|
||||
|
||||
describe('setWebSocketUrl()', () => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { UserRolesChangedEvent } from '@standardnotes/domain-events'
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
import { AbstractService, InternalEventBusInterface, StorageKey } from '@standardnotes/services'
|
||||
import { WebSocketApiServiceInterface } from '@standardnotes/api'
|
||||
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
|
||||
export enum WebSocketsServiceEvent {
|
||||
UserRoleMessageReceived = 'WebSocketMessageReceived',
|
||||
@@ -12,6 +14,7 @@ export class SNWebSocketsService extends AbstractService<WebSocketsServiceEvent,
|
||||
constructor(
|
||||
private storageService: DiskStorageService,
|
||||
private webSocketUrl: string | undefined,
|
||||
private webSocketApiService: WebSocketApiServiceInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
@@ -34,10 +37,15 @@ export class SNWebSocketsService extends AbstractService<WebSocketsServiceEvent,
|
||||
)._websocket_url
|
||||
}
|
||||
|
||||
public startWebSocketConnection(authToken: string): void {
|
||||
async startWebSocketConnection(): Promise<void> {
|
||||
const webSocketConectionToken = await this.createWebSocketConnectionToken()
|
||||
if (webSocketConectionToken === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.webSocketUrl) {
|
||||
try {
|
||||
this.webSocket = new WebSocket(`${this.webSocketUrl}?authToken=Bearer+${authToken}`)
|
||||
this.webSocket = new WebSocket(`${this.webSocketUrl}?authToken=${webSocketConectionToken}`)
|
||||
this.webSocket.onmessage = this.onWebSocketMessage.bind(this)
|
||||
this.webSocket.onclose = this.onWebSocketClose.bind(this)
|
||||
} catch (e) {
|
||||
@@ -59,9 +67,22 @@ export class SNWebSocketsService extends AbstractService<WebSocketsServiceEvent,
|
||||
this.webSocket = undefined
|
||||
}
|
||||
|
||||
private async createWebSocketConnectionToken(): Promise<string | undefined> {
|
||||
try {
|
||||
const response = await this.webSocketApiService.createConnectionToken()
|
||||
|
||||
return response.data.token
|
||||
} catch (error) {
|
||||
console.error((error as Error).message)
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
override deinit(): void {
|
||||
super.deinit()
|
||||
;(this.storageService as unknown) = undefined
|
||||
;(this.webSocketApiService as unknown) = undefined
|
||||
this.closeWebSocketConnection()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
override handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
this.updateSessionExpiryTimer(this.getSessionExpiryDate())
|
||||
this.mobilePasscodeTiming = this.getPasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getBiometricsTiming()
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
@@ -284,11 +286,6 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default, false)
|
||||
}
|
||||
|
||||
loadMobileUnlockTiming(): void {
|
||||
this.mobilePasscodeTiming = this.getPasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getBiometricsTiming()
|
||||
}
|
||||
|
||||
private async validateOrRenewSession(
|
||||
reason: ChallengeReason,
|
||||
{ fallBackToAccountPassword = true, requireAccountPassword = false } = {},
|
||||
|
||||
@@ -107,7 +107,7 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
|
||||
this.apiService.setUser(user)
|
||||
}
|
||||
|
||||
public initializeFromDisk() {
|
||||
async initializeFromDisk() {
|
||||
this.setUser(this.diskStorageService.getValue(StorageKey.User))
|
||||
|
||||
if (!this.user) {
|
||||
@@ -121,7 +121,7 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
|
||||
if (rawSession) {
|
||||
const session = SessionFromRawStorageValue(rawSession)
|
||||
this.setSession(session, false)
|
||||
this.webSocketsService.startWebSocketConnection(session.authorizationValue)
|
||||
await this.webSocketsService.startWebSocketConnection()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
|
||||
const serverPassword = rootKey.serverPassword as string
|
||||
const keyParams = rootKey.keyParams
|
||||
|
||||
const registerResponse = await this.userApiService.register(email, serverPassword, keyParams, ephemeral)
|
||||
const registerResponse = await this.userApiService.register({ email, serverPassword, keyParams, ephemeral })
|
||||
|
||||
if ('error' in registerResponse.data) {
|
||||
throw new ApiCallError((registerResponse.data as HttpErrorResponseBody).error.message)
|
||||
@@ -623,9 +623,9 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
|
||||
|
||||
this.httpService.setHost(host)
|
||||
|
||||
await this.setSession(session)
|
||||
this.setSession(session)
|
||||
|
||||
this.webSocketsService.startWebSocketConnection(session.authorizationValue)
|
||||
await this.webSocketsService.startWebSocketConnection()
|
||||
}
|
||||
|
||||
private async handleAuthResponse(body: UserRegistrationResponseBody, rootKey: SNRootKey, wrappingKey?: SNRootKey) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ListedAuthorSecretsData,
|
||||
LogSessionUserAgentOption,
|
||||
SettingName,
|
||||
MuteMarketingEmailsOption,
|
||||
} from '@standardnotes/settings'
|
||||
|
||||
type SettingType =
|
||||
@@ -23,6 +24,7 @@ type SettingType =
|
||||
| MuteFailedBackupsEmailsOption
|
||||
| MuteFailedCloudBackupsEmailsOption
|
||||
| MuteSignInEmailsOption
|
||||
| MuteMarketingEmailsOption
|
||||
| OneDriveBackupFrequency
|
||||
|
||||
export class SettingsList {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import * as Factory from './lib/factory.js'
|
||||
import * as Utils from './lib/Utils.js'
|
||||
import * as Files from './lib/Files.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -52,38 +54,6 @@ describe('files', function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
const uploadFile = async (fileService, buffer, name, ext, chunkSize) => {
|
||||
const operation = await fileService.beginNewFileUpload(buffer.byteLength)
|
||||
|
||||
let chunkId = 1
|
||||
for (let i = 0; i < buffer.length; i += chunkSize) {
|
||||
const readUntil = i + chunkSize > buffer.length ? buffer.length : i + chunkSize
|
||||
const chunk = buffer.slice(i, readUntil)
|
||||
const isFinalChunk = readUntil === buffer.length
|
||||
|
||||
const error = await fileService.pushBytesForUpload(operation, chunk, chunkId++, isFinalChunk)
|
||||
if (error) {
|
||||
throw new Error('Could not upload file chunk')
|
||||
}
|
||||
}
|
||||
|
||||
const file = await fileService.finishUpload(operation, name, ext)
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
const downloadFile = async (fileService, itemManager, remoteIdentifier) => {
|
||||
const file = itemManager.getItems(ContentType.File).find((file) => file.remoteIdentifier === remoteIdentifier)
|
||||
|
||||
let receivedBytes = new Uint8Array()
|
||||
|
||||
await fileService.downloadFile(file, (decryptedBytes) => {
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...decryptedBytes])
|
||||
})
|
||||
|
||||
return receivedBytes
|
||||
}
|
||||
|
||||
it('should create valet token from server', async function () {
|
||||
await setup({ fakeCrypto: true, subscription: true })
|
||||
const remoteIdentifier = Utils.generateUuid()
|
||||
@@ -141,9 +111,9 @@ describe('files', function () {
|
||||
const response = await fetch('/packages/snjs/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
|
||||
const file = await uploadFile(fileService, buffer, 'my-file', 'md', 1000)
|
||||
const file = await Files.uploadFile(fileService, buffer, 'my-file', 'md', 1000)
|
||||
|
||||
const downloadedBytes = await downloadFile(fileService, itemManager, file.remoteIdentifier)
|
||||
const downloadedBytes = await Files.downloadFile(fileService, itemManager, file.remoteIdentifier)
|
||||
|
||||
expect(downloadedBytes).to.eql(buffer)
|
||||
})
|
||||
@@ -154,9 +124,9 @@ describe('files', function () {
|
||||
const response = await fetch('/packages/snjs/mocha/assets/two_mb_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
|
||||
const file = await uploadFile(fileService, buffer, 'my-file', 'md', 100000)
|
||||
const file = await Files.uploadFile(fileService, buffer, 'my-file', 'md', 100000)
|
||||
|
||||
const downloadedBytes = await downloadFile(fileService, itemManager, file.remoteIdentifier)
|
||||
const downloadedBytes = await Files.downloadFile(fileService, itemManager, file.remoteIdentifier)
|
||||
|
||||
expect(downloadedBytes).to.eql(buffer)
|
||||
})
|
||||
@@ -167,7 +137,7 @@ describe('files', function () {
|
||||
const response = await fetch('/packages/snjs/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
|
||||
const file = await uploadFile(fileService, buffer, 'my-file', 'md', 1000)
|
||||
const file = await Files.uploadFile(fileService, buffer, 'my-file', 'md', 1000)
|
||||
|
||||
const error = await fileService.deleteFile(file)
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
export async function uploadFile(fileService, buffer, name, ext, chunkSize) {
|
||||
const operation = await fileService.beginNewFileUpload(buffer.byteLength)
|
||||
|
||||
let chunkId = 1
|
||||
for (let i = 0; i < buffer.length; i += chunkSize) {
|
||||
const readUntil = i + chunkSize > buffer.length ? buffer.length : i + chunkSize
|
||||
const chunk = buffer.slice(i, readUntil)
|
||||
const isFinalChunk = readUntil === buffer.length
|
||||
|
||||
const error = await fileService.pushBytesForUpload(operation, chunk, chunkId++, isFinalChunk)
|
||||
if (error) {
|
||||
throw new Error('Could not upload file chunk')
|
||||
}
|
||||
}
|
||||
|
||||
const file = await fileService.finishUpload(operation, name, ext)
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
export async function downloadFile(fileService, itemManager, remoteIdentifier) {
|
||||
const file = itemManager.getItems(ContentType.File).find((file) => file.remoteIdentifier === remoteIdentifier)
|
||||
|
||||
let receivedBytes = new Uint8Array()
|
||||
|
||||
await fileService.downloadFile(file, (decryptedBytes) => {
|
||||
receivedBytes = new Uint8Array([...receivedBytes, ...decryptedBytes])
|
||||
})
|
||||
|
||||
return receivedBytes
|
||||
}
|
||||
@@ -144,7 +144,11 @@ export async function registerOldUser({ application, email, password, version })
|
||||
const operator = application.protocolService.operatorManager.operatorForVersion(version)
|
||||
const accountKey = await operator.createRootKey(email, password, KeyParamsOrigination.Registration)
|
||||
|
||||
const response = await application.userApiService.register(email, accountKey.serverPassword, accountKey.keyParams)
|
||||
const response = await application.userApiService.register({
|
||||
email: email,
|
||||
serverPassword: accountKey.serverPassword,
|
||||
keyParams: accountKey.keyParams,
|
||||
})
|
||||
/** Mark all existing items as dirty. */
|
||||
await application.itemManager.changeItems(application.itemManager.items, (m) => {
|
||||
m.dirty = true
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import * as Factory from './lib/factory.js'
|
||||
import * as Files from './lib/Files.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('settings service', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
const validSetting = SettingName.GoogleDriveBackupFrequency
|
||||
const fakePayload = 'Im so meta even this acronym'
|
||||
const updatedFakePayload = 'is meta'
|
||||
|
||||
let application
|
||||
let context
|
||||
let user
|
||||
|
||||
beforeEach(async function () {
|
||||
context = await Factory.createAppContextWithFakeCrypto()
|
||||
@@ -17,13 +22,31 @@ describe('settings service', function () {
|
||||
|
||||
application = context.application
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
const registerResponse = await Factory.registerUserToApplication({
|
||||
application: context.application,
|
||||
email: context.email,
|
||||
password: context.password,
|
||||
})
|
||||
user = registerResponse.user
|
||||
})
|
||||
|
||||
const reInitializeApplicationWithRealCrypto = async () => {
|
||||
await Factory.safeDeinit(application)
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
|
||||
application = context.application
|
||||
|
||||
const registerResponse = await Factory.registerUserToApplication({
|
||||
application: context.application,
|
||||
email: context.email,
|
||||
password: context.password,
|
||||
})
|
||||
user = registerResponse.user
|
||||
}
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
@@ -103,9 +126,68 @@ describe('settings service', function () {
|
||||
offline: false,
|
||||
})
|
||||
|
||||
await Factory.sleep(0.5)
|
||||
await Factory.sleep(1)
|
||||
|
||||
const setting = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_LIMIT')
|
||||
expect(setting).to.be.a('string')
|
||||
})
|
||||
|
||||
it('persist irreplaceable subscription settings between subsequent subscriptions', async () => {
|
||||
await reInitializeApplicationWithRealCrypto()
|
||||
|
||||
await Factory.publishMockedEvent('SUBSCRIPTION_PURCHASED', {
|
||||
userEmail: context.email,
|
||||
subscriptionId: 1,
|
||||
subscriptionName: 'PRO_PLAN',
|
||||
subscriptionExpiresAt: (new Date().getTime() + 3_600_000) * 1_000,
|
||||
timestamp: Date.now(),
|
||||
offline: false,
|
||||
})
|
||||
await Factory.sleep(1)
|
||||
|
||||
const response = await fetch('/packages/snjs/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
|
||||
await Files.uploadFile(application.fileService, buffer, 'my-file', 'md', 1000)
|
||||
|
||||
await Factory.publishMockedEvent('FILE_UPLOADED', {
|
||||
userUuid: user.uuid,
|
||||
fileByteSize: 123,
|
||||
filePath: 'foobar',
|
||||
fileName: 'barbuzz',
|
||||
})
|
||||
await Factory.sleep(1)
|
||||
|
||||
const limitSettingBefore = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_LIMIT')
|
||||
expect(limitSettingBefore).to.equal('107374182400')
|
||||
|
||||
const usedSettingBefore = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_USED')
|
||||
expect(usedSettingBefore).to.equal('123')
|
||||
|
||||
|
||||
await Factory.publishMockedEvent('SUBSCRIPTION_EXPIRED', {
|
||||
userEmail: context.email,
|
||||
subscriptionId: 1,
|
||||
subscriptionName: 'PRO_PLAN',
|
||||
timestamp: Date.now(),
|
||||
offline: false,
|
||||
})
|
||||
await Factory.sleep(1)
|
||||
|
||||
await Factory.publishMockedEvent('SUBSCRIPTION_PURCHASED', {
|
||||
userEmail: context.email,
|
||||
subscriptionId: 2,
|
||||
subscriptionName: 'PRO_PLAN',
|
||||
subscriptionExpiresAt: (new Date().getTime() + 3_600_000) * 1_000,
|
||||
timestamp: Date.now(),
|
||||
offline: false,
|
||||
})
|
||||
await Factory.sleep(1)
|
||||
|
||||
const limitSettingAfter = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_LIMIT')
|
||||
expect(limitSettingAfter).to.equal(limitSettingBefore)
|
||||
|
||||
const usedSettingAfter = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_USED')
|
||||
expect(usedSettingAfter).to.equal(usedSettingBefore)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.130.2",
|
||||
"version": "2.134.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,46 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.3.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.3.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.3.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **web:** incorrect native access ([b9a07d8](https://github.com/standardnotes/app/commit/b9a07d8b5abb9ae6e7fb00b0edc1fa87a2b209e0))
|
||||
|
||||
## [1.3.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* export/sharing notes on mobile webview ([#1644](https://github.com/standardnotes/app/issues/1644)) ([6d5ebde](https://github.com/standardnotes/app/commit/6d5ebdeaa1777f31878b6e7d4dfc33fe83abc9fc))
|
||||
|
||||
## [1.3.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.3.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** adjust status bar color to match current theme ([#1624](https://github.com/standardnotes/app/issues/1624)) ([4d5429c](https://github.com/standardnotes/app/commit/4d5429cc89c15f870825f20e97844825102c6aec))
|
||||
|
||||
# [1.3.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Features
|
||||
|
||||
* sync page theme color metadata with active theme bg ([#1623](https://github.com/standardnotes/app/issues/1623)) ([5de6f45](https://github.com/standardnotes/app/commit/5de6f459c106127f294693389f62fa1c3ed57798))
|
||||
|
||||
## [1.2.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.2.7",
|
||||
"version": "1.3.7",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -36,6 +36,7 @@
|
||||
"@typescript-eslint/parser": "^5.12.1",
|
||||
"eslint-plugin-prettier": "*",
|
||||
"jest": "^28.1.2",
|
||||
"ts-jest": "^28.0.5"
|
||||
"ts-jest": "^28.0.5",
|
||||
"typescript": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ApplicationInterface } from '@standardnotes/services'
|
||||
|
||||
function sanitizeFileName(name: string): string {
|
||||
export function sanitizeFileName(name: string): string {
|
||||
return name.trim().replace(/[.\\/:"?*|<>]/g, '_')
|
||||
}
|
||||
|
||||
@@ -52,13 +52,16 @@ export class ArchiveManager {
|
||||
})
|
||||
|
||||
if (encrypted) {
|
||||
this.downloadData(blobData, `Standard Notes Encrypted Backup and Import File - ${this.formattedDate()}.txt`)
|
||||
this.downloadData(
|
||||
blobData,
|
||||
`Standard Notes Encrypted Backup and Import File - ${this.formattedDateForExports()}.txt`,
|
||||
)
|
||||
} else {
|
||||
this.downloadZippedDecryptedItems(data).catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
private formattedDate() {
|
||||
formattedDateForExports() {
|
||||
const string = `${new Date()}`
|
||||
// Match up to the first parenthesis, i.e do not include '(Central Standard Time)'
|
||||
const matches = string.match(/^(.*?) \(/)
|
||||
@@ -108,7 +111,7 @@ export class ArchiveManager {
|
||||
await nextFile()
|
||||
} else {
|
||||
const finalBlob = await zipWriter.close()
|
||||
this.downloadData(finalBlob, `Standard Notes Backup - ${this.formattedDate()}.zip`)
|
||||
this.downloadData(finalBlob, `Standard Notes Backup - ${this.formattedDateForExports()}.zip`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +146,7 @@ export class ArchiveManager {
|
||||
|
||||
async downloadDataAsZip(data: ZippableData) {
|
||||
const zipFileAsBlob = await this.zipData(data)
|
||||
this.downloadData(zipFileAsBlob, `Standard Notes Export - ${this.formattedDate()}.zip`)
|
||||
this.downloadData(zipFileAsBlob, `Standard Notes Export - ${this.formattedDateForExports()}.zip`)
|
||||
}
|
||||
|
||||
private hrefForData(data: Blob) {
|
||||
|
||||
@@ -248,13 +248,14 @@ export class ThemeManager extends AbstractService {
|
||||
this.deactivateTheme(theme.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
if (source !== PayloadEmitSource.LocalRetrieved) {
|
||||
this.cacheThemeState().catch(console.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public deactivateAllThemes() {
|
||||
private deactivateAllThemes() {
|
||||
const activeThemes = this.activeThemes.slice()
|
||||
|
||||
for (const uuid of activeThemes) {
|
||||
@@ -287,10 +288,33 @@ export class ThemeManager extends AbstractService {
|
||||
link.rel = 'stylesheet'
|
||||
link.media = 'screen,print'
|
||||
link.id = theme.uuid
|
||||
link.onload = this.syncThemeColorMetadata
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
|
||||
if (this.application.isNativeMobileWeb()) {
|
||||
this.application.mobileDevice.handleThemeSchemeChange(theme.package_info.isDark ?? false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs the active theme's background color to the 'theme-color' meta tag
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color
|
||||
*/
|
||||
private syncThemeColorMetadata() {
|
||||
const themeColorMetaElement = document.querySelector('meta[name="theme-color"]')
|
||||
if (!themeColorMetaElement) {
|
||||
return
|
||||
}
|
||||
|
||||
const bgColor = getComputedStyle(document.documentElement).getPropertyValue('--sn-stylekit-background-color')
|
||||
themeColorMetaElement.setAttribute('content', bgColor ? bgColor.trim() : '#ffffff')
|
||||
}
|
||||
|
||||
private deactivateTheme(uuid: string) {
|
||||
if (!this.activeThemes.includes(uuid)) {
|
||||
return
|
||||
}
|
||||
|
||||
const element = document.getElementById(uuid) as HTMLLinkElement
|
||||
if (element) {
|
||||
element.disabled = true
|
||||
@@ -298,6 +322,10 @@ export class ThemeManager extends AbstractService {
|
||||
}
|
||||
|
||||
removeFromArray(this.activeThemes, uuid)
|
||||
|
||||
if (this.activeThemes.length === 0 && this.application.isNativeMobileWeb()) {
|
||||
this.application.mobileDevice.handleThemeSchemeChange(false)
|
||||
}
|
||||
}
|
||||
|
||||
private async cacheThemeState() {
|
||||
|
||||
@@ -3,6 +3,239 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.64.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
### Features
|
||||
|
||||
* add custom note title format pref ([#1678](https://github.com/standardnotes/app/issues/1678)) ([11dd39c](https://github.com/standardnotes/app/commit/11dd39c126019c4295c03fb59b05ea5aa3adcd27))
|
||||
|
||||
## [3.63.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.63.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.63.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ipad web view ui improvements ([#1664](https://github.com/standardnotes/app/issues/1664)) ([20e4208](https://github.com/standardnotes/app/commit/20e420820d45e2556f60604de41a1bee96c98fe1))
|
||||
|
||||
# [3.63.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-29)
|
||||
|
||||
### Features
|
||||
|
||||
* add panel settings section in quick settings ([#1669](https://github.com/standardnotes/app/issues/1669)) ([0ed692f](https://github.com/standardnotes/app/commit/0ed692ff9b056d00dde73040482e6b1bdc404d04))
|
||||
|
||||
## [3.62.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve biometrics input on mobile webview ([#1658](https://github.com/standardnotes/app/issues/1658)) ([3f9d3ce](https://github.com/standardnotes/app/commit/3f9d3ceffac206645ff9e5b66e68bf2e7b9cd0e2))
|
||||
|
||||
## [3.62.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* grey flash on textarea focus on safari/webkit ([#1661](https://github.com/standardnotes/app/issues/1661)) ([eafa65e](https://github.com/standardnotes/app/commit/eafa65e93487e60a4b9ee37f8cc9932bbbd8917c))
|
||||
|
||||
## [3.62.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sticky header behavior on iOS ([#1662](https://github.com/standardnotes/app/issues/1662)) ([c0af596](https://github.com/standardnotes/app/commit/c0af596f2d69654b50dffc36250137998dc629fb))
|
||||
|
||||
## [3.62.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* challenge modal ([9502a7f](https://github.com/standardnotes/app/commit/9502a7fa32f2cca26b6964788a44acc9778bf721))
|
||||
* webapp blanking because of mobileDevice check ([727d78c](https://github.com/standardnotes/app/commit/727d78cfe23c268dcd97566f355584a23c8b9d45))
|
||||
|
||||
# [3.62.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* handle android back button on android ([#1656](https://github.com/standardnotes/app/issues/1656)) ([981d8a7](https://github.com/standardnotes/app/commit/981d8a7497ea2600e1535ad9170cb5559550ca4d))
|
||||
|
||||
# [3.61.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Features
|
||||
|
||||
* delete account ([#1653](https://github.com/standardnotes/app/issues/1653)) ([5db47e8](https://github.com/standardnotes/app/commit/5db47e83e82993bb9d210cb264d05f59ea1db5f3))
|
||||
|
||||
# [3.60.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Features
|
||||
|
||||
* upgrade cta ([#1655](https://github.com/standardnotes/app/issues/1655)) ([ec52e56](https://github.com/standardnotes/app/commit/ec52e5684da2c7caaa24929275727cdbc23c28e1))
|
||||
|
||||
## [3.59.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.59.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't hide note tags container on desktop ([70e57e3](https://github.com/standardnotes/app/commit/70e57e32b3bd6e3943d6fc1af2ae0ad9618e1812))
|
||||
|
||||
## [3.59.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* export/sharing notes on mobile webview ([#1644](https://github.com/standardnotes/app/issues/1644)) ([6d5ebde](https://github.com/standardnotes/app/commit/6d5ebdeaa1777f31878b6e7d4dfc33fe83abc9fc))
|
||||
|
||||
# [3.59.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* sticky header on mobile if plaintext note is scrolled ([#1645](https://github.com/standardnotes/app/issues/1645)) ([f8f6a52](https://github.com/standardnotes/app/commit/f8f6a5212b608c7a43957449d42b1d462134773c))
|
||||
|
||||
## [3.58.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **web:** check for proper feature in the subscription sharing section ([507823d](https://github.com/standardnotes/app/commit/507823dc4f1a2c1db47635ac26a686d4d8f2f4e8))
|
||||
|
||||
# [3.58.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* note tags button for mobile ([#1641](https://github.com/standardnotes/app/issues/1641)) ([fe2ce9f](https://github.com/standardnotes/app/commit/fe2ce9f1e8b0c027c30bcd892606e914f71d4634))
|
||||
|
||||
## [3.57.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* improve new note title format ([#1640](https://github.com/standardnotes/app/issues/1640)) ([59e814b](https://github.com/standardnotes/app/commit/59e814bcec8c3633b1b2baeae3e435fa2a19777d))
|
||||
|
||||
## [3.57.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* move prefs button next to account menu button in footer ([#1639](https://github.com/standardnotes/app/issues/1639)) ([a3611c8](https://github.com/standardnotes/app/commit/a3611c802f08817d2ba73adf6b9d48465738b1b6))
|
||||
|
||||
## [3.57.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* responsiveness when resizing window on web/desktop ([#1637](https://github.com/standardnotes/app/issues/1637)) ([e4b1b64](https://github.com/standardnotes/app/commit/e4b1b641194cede65b8f0413bb1c805a84585dfe))
|
||||
|
||||
# [3.57.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
### Features
|
||||
|
||||
* new note title format w/ prefs ([#1629](https://github.com/standardnotes/app/issues/1629)) ([d3621d7](https://github.com/standardnotes/app/commit/d3621d70b1a34537417b06830275467055b753cb))
|
||||
|
||||
# [3.56.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-24)
|
||||
|
||||
### Features
|
||||
|
||||
* flatten quick settings menu and add separate prefs button ([#1631](https://github.com/standardnotes/app/issues/1631)) ([97835e6](https://github.com/standardnotes/app/commit/97835e64ac0ebea829a923af082880b269a2965e))
|
||||
|
||||
## [3.55.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.55.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* prefs view on desktop ([#1628](https://github.com/standardnotes/app/issues/1628)) ([a548883](https://github.com/standardnotes/app/commit/a548883863b4e0ec949fa63a8581cc2984614993))
|
||||
|
||||
# [3.55.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Features
|
||||
|
||||
* add prefs button to navigation panel on mobile ([#1625](https://github.com/standardnotes/app/issues/1625)) ([0af7201](https://github.com/standardnotes/app/commit/0af720146c412316fcb24999ff73c91337f19fb9))
|
||||
|
||||
## [3.54.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* biometrics timing loading ([#1622](https://github.com/standardnotes/app/issues/1622)) ([3c0d4fa](https://github.com/standardnotes/app/commit/3c0d4fac766c3a31bdbebe4f7e88f83ba1ea58da))
|
||||
|
||||
## [3.54.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* protected overlay on mobile ([#1621](https://github.com/standardnotes/app/issues/1621)) ([51e5043](https://github.com/standardnotes/app/commit/51e50433d3e1a3a682bcb7a774129a904a39b12e))
|
||||
|
||||
## [3.54.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-23)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **web:** move email notification preferences to account pane ([#1620](https://github.com/standardnotes/app/issues/1620)) ([de64f1b](https://github.com/standardnotes/app/commit/de64f1bfce6d343fe698c55d6eaf307d200df3ee))
|
||||
|
||||
## [3.54.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* better safe area padding on iOS ([#1618](https://github.com/standardnotes/app/issues/1618)) ([86251b4](https://github.com/standardnotes/app/commit/86251b49d3b4965571da1a3c039ec4c6043f733c))
|
||||
|
||||
## [3.54.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "Revert "feat(web): move emails notifications to one pane and enable opening settings via url (#1614)" (#1617)" (#1619) ([e50f4eb](https://github.com/standardnotes/app/commit/e50f4eb92efd65b0b8fa3a5001b8dfb611f60180)), closes [#1614](https://github.com/standardnotes/app/issues/1614) [#1617](https://github.com/standardnotes/app/issues/1617) [#1619](https://github.com/standardnotes/app/issues/1619)
|
||||
|
||||
## [3.54.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat(web): move emails notifications to one pane and enable opening settings via url (#1614)" (#1617) ([6824dd2](https://github.com/standardnotes/app/commit/6824dd2d5fe4a6d427ff5465540f3b7865933e98)), closes [#1614](https://github.com/standardnotes/app/issues/1614) [#1617](https://github.com/standardnotes/app/issues/1617)
|
||||
|
||||
# [3.54.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Features
|
||||
|
||||
* show selected editor icon in change editor button ([#1615](https://github.com/standardnotes/app/issues/1615)) ([ce252de](https://github.com/standardnotes/app/commit/ce252def332c99d8d8be6a3ce5d76b66e2ea325f))
|
||||
|
||||
# [3.53.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Features
|
||||
|
||||
* **web:** move emails notifications to one pane and enable opening settings via url ([#1614](https://github.com/standardnotes/app/issues/1614)) ([1fe833b](https://github.com/standardnotes/app/commit/1fe833bc24a3a426a7230381704d418624cc2b82))
|
||||
|
||||
## [3.52.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* manage subscription button on mobile webview ([#1613](https://github.com/standardnotes/app/issues/1613)) ([db4ceca](https://github.com/standardnotes/app/commit/db4ceca10e4b41707cae2a4daa9718646d808c19))
|
||||
|
||||
# [3.52.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-22)
|
||||
|
||||
### Features
|
||||
|
||||
* on mobile goto content tab after selecting revision ([#1611](https://github.com/standardnotes/app/issues/1611)) ([f281041](https://github.com/standardnotes/app/commit/f2810412217afdaa05d5012db37276c843d360b4))
|
||||
|
||||
# [3.51.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable focus mode and dynamic panel switches on mobile ([#1607](https://github.com/standardnotes/app/issues/1607)) ([9dfeac7](https://github.com/standardnotes/app/commit/9dfeac74897594628c0bf657226f3e5e586267dc))
|
||||
* iOS safe area using CSS ([#1610](https://github.com/standardnotes/app/issues/1610)) ([8fa5663](https://github.com/standardnotes/app/commit/8fa566336ceef8a3d7b559b1aaf342f7c51f21cd))
|
||||
|
||||
### Features
|
||||
|
||||
* add lock button to navigation panel on mobile ([#1609](https://github.com/standardnotes/app/issues/1609)) ([d1b7186](https://github.com/standardnotes/app/commit/d1b71861ecf5c8093cbf3d37813ad7ab514edc42))
|
||||
|
||||
## [3.50.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* revert webview alert changes ([#1606](https://github.com/standardnotes/app/issues/1606)) ([ee0d444](https://github.com/standardnotes/app/commit/ee0d44405d14505e043aae2c63848bae1c14f1c3))
|
||||
* tag input auto-submitting when pressing space ([#1608](https://github.com/standardnotes/app/issues/1608)) ([581a08c](https://github.com/standardnotes/app/commit/581a08cd09e02354676d9c14058475e71be05175))
|
||||
|
||||
## [3.50.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **web:** subscription sharing copy ([271a8d1](https://github.com/standardnotes/app/commit/271a8d1d70d25e4d5bdc7e3ad5cfffb2a5cb4f01))
|
||||
|
||||
## [3.50.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/web",
|
||||
"version": "3.50.4",
|
||||
"version": "3.64.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/app.js",
|
||||
"author": "Standard Notes.",
|
||||
@@ -85,6 +85,7 @@
|
||||
"@standardnotes/toast": "workspace:*",
|
||||
"@standardnotes/ui-services": "workspace:^",
|
||||
"@zip.js/zip.js": "^2.6.26",
|
||||
"dayjs": "^1.11.5",
|
||||
"mobx": "^6.6.2",
|
||||
"mobx-react-lite": "^3.4.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<meta content="viewport-fit=cover, width=device-width, initial-scale=1" name="viewport"/>
|
||||
|
||||
<link rel="canonical" href="https://app.standardnotes.com" />
|
||||
|
||||
|
||||
@@ -41,6 +41,13 @@ const getKey = () => {
|
||||
return keyCount++
|
||||
}
|
||||
|
||||
const setViewportHeight = () => {
|
||||
document.documentElement.style.setProperty(
|
||||
'--viewport-height',
|
||||
`${visualViewport ? visualViewport.height : window.innerHeight}px`,
|
||||
)
|
||||
}
|
||||
|
||||
const startApplication: StartApplication = async function startApplication(
|
||||
defaultSyncServerHost: string,
|
||||
device: WebOrDesktopDevice,
|
||||
@@ -53,6 +60,7 @@ const startApplication: StartApplication = async function startApplication(
|
||||
let root: Root
|
||||
|
||||
const onDestroy = () => {
|
||||
window.removeEventListener('orientationchange', setViewportHeight)
|
||||
const rootElement = document.getElementById(ElementIds.RootId) as HTMLElement
|
||||
root.unmount()
|
||||
rootElement.remove()
|
||||
@@ -66,10 +74,8 @@ const startApplication: StartApplication = async function startApplication(
|
||||
root = createRoot(appendedRootNode)
|
||||
|
||||
disableIosTextFieldZoom()
|
||||
document.documentElement.style.setProperty(
|
||||
'--viewport-height',
|
||||
`${visualViewport ? visualViewport.height : window.innerHeight}px`,
|
||||
)
|
||||
setViewportHeight()
|
||||
window.addEventListener('orientationchange', setViewportHeight)
|
||||
|
||||
root.render(
|
||||
<ApplicationGroupView
|
||||
|
||||
@@ -26,6 +26,8 @@ import { isDesktopApplication } from '@/Utils'
|
||||
import { DesktopManager } from './Device/DesktopManager'
|
||||
import { ArchiveManager, AutolockService, IOService, WebAlertService, ThemeManager } from '@standardnotes/ui-services'
|
||||
import { MobileWebReceiver } from './MobileWebReceiver'
|
||||
import { AndroidBackHandler } from '@/NativeMobileWeb/AndroidBackHandler'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
|
||||
type WebServices = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
@@ -45,6 +47,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
public iconsController: IconsController
|
||||
private onVisibilityChange: () => void
|
||||
private mobileWebReceiver?: MobileWebReceiver
|
||||
private androidBackHandler?: AndroidBackHandler
|
||||
|
||||
constructor(
|
||||
deviceInterface: WebOrDesktopDevice,
|
||||
@@ -76,6 +79,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
|
||||
if (this.isNativeMobileWeb()) {
|
||||
this.mobileWebReceiver = new MobileWebReceiver(this)
|
||||
this.androidBackHandler = new AndroidBackHandler()
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log = (...args) => {
|
||||
@@ -207,7 +211,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
}
|
||||
|
||||
isGlobalSpellcheckEnabled(): boolean {
|
||||
return this.getPreference(PrefKey.EditorSpellcheck, true)
|
||||
return this.getPreference(PrefKey.EditorSpellcheck, PrefDefaults[PrefKey.EditorSpellcheck])
|
||||
}
|
||||
|
||||
public getItemTags(item: DecryptedItemInterface) {
|
||||
@@ -264,4 +268,17 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
await this.lock()
|
||||
}
|
||||
}
|
||||
|
||||
handleAndroidBackButtonPressed(): void {
|
||||
if (typeof this.androidBackHandler !== 'undefined') {
|
||||
this.androidBackHandler.notifyEvent()
|
||||
}
|
||||
}
|
||||
|
||||
addAndroidBackHandlerEventListener(listener: () => boolean) {
|
||||
if (typeof this.androidBackHandler !== 'undefined') {
|
||||
return this.androidBackHandler.addEventListener(listener)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ const createApplication = (
|
||||
device: WebOrDesktopDevice,
|
||||
webSocketUrl: string,
|
||||
) => {
|
||||
const platform = getPlatform()
|
||||
const platform = getPlatform(device)
|
||||
|
||||
const application = new WebApplication(
|
||||
deviceInterface,
|
||||
|
||||
@@ -53,6 +53,9 @@ export class MobileWebReceiver {
|
||||
case ReactNativeToWebEvent.ResumingFromBackground:
|
||||
void this.application.handleMobileResumingFromBackgroundEvent()
|
||||
break
|
||||
case ReactNativeToWebEvent.AndroidBackButtonPressed:
|
||||
void this.application.handleAndroidBackButtonPressed()
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
|
||||
+3
-35
@@ -45,9 +45,7 @@ const WorkspaceSwitcherMenu: FunctionComponent<Props> = ({
|
||||
|
||||
const signoutAll = useCallback(async () => {
|
||||
const confirmed = await viewControllerManager.application.alertService.confirm(
|
||||
`Are you sure you want to sign out of all workspaces on this device?${
|
||||
viewControllerManager.application.isNativeMobileWeb() && '<b> Your app will quit after sign out completes.</b>'
|
||||
}`,
|
||||
'Are you sure you want to sign out of all workspaces on this device?',
|
||||
undefined,
|
||||
'Sign out all',
|
||||
ButtonType.Danger,
|
||||
@@ -64,44 +62,14 @@ const WorkspaceSwitcherMenu: FunctionComponent<Props> = ({
|
||||
|
||||
const activateWorkspace = useCallback(
|
||||
async (descriptor: ApplicationDescriptor) => {
|
||||
if (viewControllerManager.application.isNativeMobileWeb()) {
|
||||
const confirmed = await viewControllerManager.application.alertService.confirm(
|
||||
'<b>The app needs to be restarted to activate the workspace</b>',
|
||||
undefined,
|
||||
'Quit app and activate workspace',
|
||||
ButtonType.Danger,
|
||||
)
|
||||
|
||||
if (confirmed) {
|
||||
void mainApplicationGroup.unloadCurrentAndActivateDescriptor(descriptor)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
void mainApplicationGroup.unloadCurrentAndActivateDescriptor(descriptor)
|
||||
},
|
||||
[mainApplicationGroup, viewControllerManager.application],
|
||||
[mainApplicationGroup],
|
||||
)
|
||||
|
||||
const addAnotherWorkspace = useCallback(async () => {
|
||||
if (viewControllerManager.application.isNativeMobileWeb()) {
|
||||
const confirmed = await viewControllerManager.application.alertService.confirm(
|
||||
'<b>The app needs to be restarted to add another workspace</b>',
|
||||
undefined,
|
||||
'Quit app and add new workspace',
|
||||
ButtonType.Danger,
|
||||
)
|
||||
|
||||
if (confirmed) {
|
||||
void mainApplicationGroup.unloadCurrentAndCreateNewDescriptor()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
void mainApplicationGroup.unloadCurrentAndCreateNewDescriptor()
|
||||
}, [mainApplicationGroup, viewControllerManager.application])
|
||||
}, [mainApplicationGroup])
|
||||
|
||||
return (
|
||||
<Menu a11yLabel="Workspace switcher menu" className="px-0 focus:shadow-none" isOpen={isOpen}>
|
||||
|
||||
@@ -12,7 +12,7 @@ import PreferencesViewWrapper from '@/Components/Preferences/PreferencesViewWrap
|
||||
import ChallengeModal from '@/Components/ChallengeModal/ChallengeModal'
|
||||
import NotesContextMenu from '@/Components/NotesContextMenu/NotesContextMenu'
|
||||
import PurchaseFlowWrapper from '@/Components/PurchaseFlow/PurchaseFlowWrapper'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import RevisionHistoryModal from '@/Components/RevisionHistoryModal/RevisionHistoryModal'
|
||||
import PremiumModalProvider from '@/Hooks/usePremiumModal'
|
||||
import ConfirmSignoutContainer from '@/Components/ConfirmSignoutModal/ConfirmSignoutModal'
|
||||
@@ -25,6 +25,8 @@ import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import TagContextMenuWrapper from '@/Components/Tags/TagContextMenuWrapper'
|
||||
import FileDragNDropProvider from '../FileDragNDropProvider/FileDragNDropProvider'
|
||||
import ResponsivePaneProvider from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import AndroidBackHandlerProvider from '@/NativeMobileWeb/useAndroidBackHandler'
|
||||
import ConfirmDeleteAccountContainer from '@/Components/ConfirmDeleteAccountModal/ConfirmDeleteAccountModal'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -33,13 +35,14 @@ type Props = {
|
||||
|
||||
const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicationGroup }) => {
|
||||
const platformString = getPlatformString()
|
||||
const [appClass, setAppClass] = useState('')
|
||||
const [launched, setLaunched] = useState(false)
|
||||
const [needsUnlock, setNeedsUnlock] = useState(true)
|
||||
const [challenges, setChallenges] = useState<Challenge[]>([])
|
||||
|
||||
const viewControllerManager = application.getViewControllerManager()
|
||||
|
||||
const appColumnContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const desktopService = application.getDesktopService()
|
||||
|
||||
@@ -123,15 +126,27 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
useEffect(() => {
|
||||
const removeObserver = application.addWebEventObserver(async (eventName, data) => {
|
||||
if (eventName === WebAppEvent.PanelResized) {
|
||||
if (!appColumnContainerRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
const { panel, collapsed } = data as PanelResizedData
|
||||
let appClass = ''
|
||||
if (panel === PANEL_NAME_NOTES && collapsed) {
|
||||
appClass += 'collapsed-notes'
|
||||
|
||||
if (panel === PANEL_NAME_NOTES) {
|
||||
if (collapsed) {
|
||||
appColumnContainerRef.current.classList.add('collapsed-notes')
|
||||
} else {
|
||||
appColumnContainerRef.current.classList.remove('collapsed-notes')
|
||||
}
|
||||
}
|
||||
if (panel === PANEL_NAME_NAVIGATION && collapsed) {
|
||||
appClass += ' collapsed-navigation'
|
||||
|
||||
if (panel === PANEL_NAME_NAVIGATION) {
|
||||
if (collapsed) {
|
||||
appColumnContainerRef.current.classList.add('collapsed-navigation')
|
||||
} else {
|
||||
appColumnContainerRef.current.classList.remove('collapsed-navigation')
|
||||
}
|
||||
}
|
||||
setAppClass(appClass)
|
||||
} else if (eventName === WebAppEvent.WindowDidFocus) {
|
||||
if (!(await application.isLocked())) {
|
||||
application.sync.sync().catch(console.error)
|
||||
@@ -150,7 +165,7 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
|
||||
const renderChallenges = useCallback(() => {
|
||||
return (
|
||||
<>
|
||||
<AndroidBackHandlerProvider application={application}>
|
||||
{challenges.map((challenge) => {
|
||||
return (
|
||||
<div className="sk-modal" key={`${challenge.id}${application.ephemeralIdentifier}`}>
|
||||
@@ -165,7 +180,7 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
</AndroidBackHandlerProvider>
|
||||
)
|
||||
}, [viewControllerManager, challenges, mainApplicationGroup, removeChallenge, application])
|
||||
|
||||
@@ -174,76 +189,79 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
}
|
||||
|
||||
return (
|
||||
<PremiumModalProvider application={application} viewControllerManager={viewControllerManager}>
|
||||
<AndroidBackHandlerProvider application={application}>
|
||||
<ResponsivePaneProvider>
|
||||
<div className={platformString + ' main-ui-view sn-component'}>
|
||||
<div id="app" className={appClass + ' app app-column-container'}>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
>
|
||||
<Navigation application={application} />
|
||||
<ContentListView
|
||||
<PremiumModalProvider application={application} viewControllerManager={viewControllerManager}>
|
||||
<div className={platformString + ' main-ui-view sn-component'}>
|
||||
<div id="app" className="app app-column-container" ref={appColumnContainerRef}>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
accountMenuController={viewControllerManager.accountMenuController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
itemListController={viewControllerManager.itemListController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
noAccountWarningController={viewControllerManager.noAccountWarningController}
|
||||
noteTagsController={viewControllerManager.noteTagsController}
|
||||
>
|
||||
<Navigation application={application} />
|
||||
<ContentListView
|
||||
application={application}
|
||||
accountMenuController={viewControllerManager.accountMenuController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
itemListController={viewControllerManager.itemListController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
noAccountWarningController={viewControllerManager.noAccountWarningController}
|
||||
noteTagsController={viewControllerManager.noteTagsController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
searchOptionsController={viewControllerManager.searchOptionsController}
|
||||
/>
|
||||
<NoteGroupView application={application} />
|
||||
</FileDragNDropProvider>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<Footer application={application} applicationGroup={mainApplicationGroup} />
|
||||
<SessionsModal application={application} viewControllerManager={viewControllerManager} />
|
||||
<PreferencesViewWrapper viewControllerManager={viewControllerManager} application={application} />
|
||||
<RevisionHistoryModal
|
||||
application={application}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
searchOptionsController={viewControllerManager.searchOptionsController}
|
||||
subscriptionController={viewControllerManager.subscriptionController}
|
||||
/>
|
||||
<NoteGroupView application={application} />
|
||||
</FileDragNDropProvider>
|
||||
</>
|
||||
|
||||
{renderChallenges()}
|
||||
|
||||
<>
|
||||
<NotesContextMenu
|
||||
application={application}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
noteTagsController={viewControllerManager.noteTagsController}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
/>
|
||||
<TagContextMenuWrapper
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<FileContextMenuWrapper
|
||||
filesController={viewControllerManager.filesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
/>
|
||||
<PurchaseFlowWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<ConfirmSignoutContainer
|
||||
applicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
/>
|
||||
<ToastContainer />
|
||||
<FilePreviewModalWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<PermissionsModalWrapper application={application} />
|
||||
<ConfirmDeleteAccountContainer application={application} viewControllerManager={viewControllerManager} />
|
||||
</>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<Footer application={application} applicationGroup={mainApplicationGroup} />
|
||||
<SessionsModal application={application} viewControllerManager={viewControllerManager} />
|
||||
<PreferencesViewWrapper viewControllerManager={viewControllerManager} application={application} />
|
||||
<RevisionHistoryModal
|
||||
application={application}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
subscriptionController={viewControllerManager.subscriptionController}
|
||||
/>
|
||||
</>
|
||||
|
||||
{renderChallenges()}
|
||||
|
||||
<>
|
||||
<NotesContextMenu
|
||||
application={application}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
noteTagsController={viewControllerManager.noteTagsController}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
/>
|
||||
<TagContextMenuWrapper
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<FileContextMenuWrapper
|
||||
filesController={viewControllerManager.filesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
/>
|
||||
<PurchaseFlowWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<ConfirmSignoutContainer
|
||||
applicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
/>
|
||||
<ToastContainer />
|
||||
<FilePreviewModalWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<PermissionsModalWrapper application={application} />
|
||||
</>
|
||||
</div>
|
||||
</PremiumModalProvider>
|
||||
</ResponsivePaneProvider>
|
||||
</PremiumModalProvider>
|
||||
</AndroidBackHandlerProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ChallengePrompt } from '@standardnotes/services'
|
||||
import { RefObject, useState } from 'react'
|
||||
import Button from '../Button/Button'
|
||||
import Icon from '../Icon/Icon'
|
||||
import { InputValue } from './InputValue'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
onValueChange: (value: InputValue['value'], prompt: ChallengePrompt) => void
|
||||
prompt: ChallengePrompt
|
||||
buttonRef: RefObject<HTMLButtonElement>
|
||||
}
|
||||
|
||||
const BiometricsPrompt = ({ application, onValueChange, prompt, buttonRef }: Props) => {
|
||||
const [authenticated, setAuthenticated] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="min-w-76">
|
||||
<Button
|
||||
primary
|
||||
fullWidth
|
||||
colorStyle={authenticated ? 'success' : 'info'}
|
||||
onClick={async () => {
|
||||
const authenticated = await application.mobileDevice.authenticateWithBiometrics()
|
||||
setAuthenticated(authenticated)
|
||||
onValueChange(authenticated, prompt)
|
||||
}}
|
||||
ref={buttonRef}
|
||||
>
|
||||
{authenticated ? (
|
||||
<span className="flex items-center justify-center gap-3">
|
||||
<Icon type="check-circle" />
|
||||
Biometrics successful
|
||||
</span>
|
||||
) : (
|
||||
'Tap to use biometrics'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BiometricsPrompt
|
||||
@@ -184,6 +184,20 @@ const ChallengeModal: FunctionComponent<Props> = ({
|
||||
}
|
||||
}, [hasBiometricPromptValue, hasOnlyBiometricPrompt, submit])
|
||||
|
||||
useEffect(() => {
|
||||
const removeListener = application.addAndroidBackHandlerEventListener(() => {
|
||||
if (challenge.cancelable) {
|
||||
cancelChallenge()
|
||||
}
|
||||
return true
|
||||
})
|
||||
return () => {
|
||||
if (removeListener) {
|
||||
removeListener()
|
||||
}
|
||||
}
|
||||
}, [application, cancelChallenge, challenge.cancelable])
|
||||
|
||||
if (!challenge.prompts) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import {
|
||||
ChallengePrompt,
|
||||
ChallengeValidation,
|
||||
MobileDeviceInterface,
|
||||
ProtectionSessionDurations,
|
||||
} from '@standardnotes/snjs'
|
||||
import { ChallengePrompt, ChallengeValidation, ProtectionSessionDurations } from '@standardnotes/snjs'
|
||||
import { FunctionComponent, useEffect, useRef } from 'react'
|
||||
import DecoratedInput from '@/Components/Input/DecoratedInput'
|
||||
import DecoratedPasswordInput from '@/Components/Input/DecoratedPasswordInput'
|
||||
import { ChallengeModalValues } from './ChallengeModalValues'
|
||||
import Button from '../Button/Button'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { InputValue } from './InputValue'
|
||||
import BiometricsPrompt from './BiometricsPrompt'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -87,21 +82,12 @@ const ChallengeModalPrompt: FunctionComponent<Props> = ({
|
||||
</div>
|
||||
</div>
|
||||
) : prompt.validation === ChallengeValidation.Biometric ? (
|
||||
<div className="min-w-76">
|
||||
<Button
|
||||
primary
|
||||
fullWidth
|
||||
onClick={async () => {
|
||||
const authenticated = await (
|
||||
application.deviceInterface as MobileDeviceInterface
|
||||
).authenticateWithBiometrics()
|
||||
onValueChange(authenticated, prompt)
|
||||
}}
|
||||
ref={biometricsButtonRef}
|
||||
>
|
||||
Tap to use biometrics
|
||||
</Button>
|
||||
</div>
|
||||
<BiometricsPrompt
|
||||
application={application}
|
||||
onValueChange={onValueChange}
|
||||
prompt={prompt}
|
||||
buttonRef={biometricsButtonRef}
|
||||
/>
|
||||
) : prompt.secureTextEntry ? (
|
||||
<DecoratedPasswordInput
|
||||
ref={inputRef}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import ChangeEditorMenu from './ChangeEditorMenu'
|
||||
import Popover from '../Popover/Popover'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -21,6 +22,12 @@ const ChangeEditorButton: FunctionComponent<Props> = ({
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [selectedEditor, setSelectedEditor] = useState(() => {
|
||||
return note ? application.componentManager.editorForNote(note) : undefined
|
||||
})
|
||||
const [selectedEditorIcon, selectedEditorIconTint] = application.iconsController.getIconAndTintForNoteType(
|
||||
selectedEditor?.package_info.note_type,
|
||||
)
|
||||
|
||||
const toggleMenu = useCallback(async () => {
|
||||
const willMenuOpen = !isOpen
|
||||
@@ -33,13 +40,16 @@ const ChangeEditorButton: FunctionComponent<Props> = ({
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
<button
|
||||
className="bg-text-padding flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border text-neutral hover:bg-contrast focus:bg-contrast"
|
||||
className={classNames(
|
||||
'bg-text-padding flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid text-neutral hover:bg-contrast focus:bg-contrast',
|
||||
`border-accessory-tint-${selectedEditorIconTint}`,
|
||||
)}
|
||||
title="Change note type"
|
||||
aria-label="Change note type"
|
||||
onClick={toggleMenu}
|
||||
ref={buttonRef}
|
||||
>
|
||||
<Icon type="dashboard" />
|
||||
<Icon type={selectedEditorIcon} className={`text-accessory-tint-${selectedEditorIconTint}`} />
|
||||
</button>
|
||||
<Popover togglePopover={toggleMenu} anchorElement={buttonRef.current} open={isOpen} className="pt-2 md:pt-0">
|
||||
<ChangeEditorMenu
|
||||
@@ -49,6 +59,9 @@ const ChangeEditorButton: FunctionComponent<Props> = ({
|
||||
closeMenu={() => {
|
||||
setIsOpen(false)
|
||||
}}
|
||||
onSelect={(component) => {
|
||||
setSelectedEditor(component)
|
||||
}}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
@@ -31,11 +31,18 @@ type ChangeEditorMenuProps = {
|
||||
closeMenu: () => void
|
||||
isVisible: boolean
|
||||
note: SNNote | undefined
|
||||
onSelect?: (component: SNComponent | undefined) => void
|
||||
}
|
||||
|
||||
const getGroupId = (group: EditorMenuGroup) => group.title.toLowerCase().replace(/\s/, '-')
|
||||
|
||||
const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({ application, closeMenu, isVisible, note }) => {
|
||||
const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
||||
application,
|
||||
closeMenu,
|
||||
isVisible,
|
||||
note,
|
||||
onSelect,
|
||||
}) => {
|
||||
const editors = useMemo(
|
||||
() =>
|
||||
application.componentManager.componentsForArea(ComponentArea.Editor).sort((a, b) => {
|
||||
@@ -162,8 +169,12 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({ applicatio
|
||||
}
|
||||
|
||||
closeMenu()
|
||||
|
||||
if (onSelect) {
|
||||
onSelect(itemToBeSelected.component)
|
||||
}
|
||||
},
|
||||
[application.componentManager, closeMenu, currentEditor, note, premiumModal, selectComponent],
|
||||
[application.componentManager, closeMenu, currentEditor, note, onSelect, premiumModal, selectComponent],
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@Controllers/ViewControllerManager'
|
||||
import { AlertDialog, AlertDialogDescription, AlertDialogLabel } from '@reach/alert-dialog'
|
||||
import { useRef } from 'react'
|
||||
import { STRING_DELETE_ACCOUNT_CONFIRMATION } from '@/Constants/Strings'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
const ConfirmDeleteAccountModal = ({ application, viewControllerManager }: Props) => {
|
||||
function closeDialog() {
|
||||
viewControllerManager.accountMenuController.setDeletingAccount(false)
|
||||
}
|
||||
|
||||
const cancelRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
return (
|
||||
<AlertDialog onDismiss={closeDialog} leastDestructiveRef={cancelRef} className="max-w-[600px] p-0">
|
||||
<div className="sk-modal-content">
|
||||
<div className="sn-component">
|
||||
<div className="sk-panel">
|
||||
<div className="sk-panel-content">
|
||||
<div className="sk-panel-section">
|
||||
<AlertDialogLabel className="sk-h3 sk-panel-section-title">Delete account?</AlertDialogLabel>
|
||||
<AlertDialogDescription className="sk-panel-row">
|
||||
<div>
|
||||
<p className="text-foreground">{STRING_DELETE_ACCOUNT_CONFIRMATION}</p>
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
|
||||
<div className="my-1 mt-4 flex gap-2">
|
||||
<Button primary small colorStyle="neutral" rounded={false} ref={cancelRef} onClick={closeDialog}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
primary
|
||||
small
|
||||
colorStyle="danger"
|
||||
rounded={false}
|
||||
onClick={() => {
|
||||
application.user.deleteAccount().catch(console.error)
|
||||
closeDialog()
|
||||
}}
|
||||
>
|
||||
Delete my account for good
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
|
||||
ConfirmDeleteAccountModal.displayName = 'ConfirmDeleteAccountModal'
|
||||
|
||||
const ConfirmDeleteAccountContainer = (props: Props) => {
|
||||
if (!props.viewControllerManager.accountMenuController.deletingAccount) {
|
||||
return null
|
||||
}
|
||||
return <ConfirmDeleteAccountModal {...props} />
|
||||
}
|
||||
|
||||
export default observer(ConfirmDeleteAccountContainer)
|
||||
@@ -41,15 +41,7 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
|
||||
<AlertDialogLabel className="sk-h3 sk-panel-section-title">Sign out workspace?</AlertDialogLabel>
|
||||
<AlertDialogDescription className="sk-panel-row">
|
||||
<div>
|
||||
<p className="text-foreground">
|
||||
{STRING_SIGN_OUT_CONFIRMATION}
|
||||
{application.isNativeMobileWeb() && (
|
||||
<div className="font-bold">
|
||||
<br />
|
||||
Your app will quit after sign out completes.
|
||||
</div>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-foreground">{STRING_SIGN_OUT_CONFIRMATION}</p>
|
||||
{showWorkspaceWarning && (
|
||||
<>
|
||||
<br />
|
||||
|
||||
@@ -69,7 +69,7 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
<div
|
||||
className={classNames(
|
||||
'infinite-scroll overflow-y-auto overflow-x-hidden focus:shadow-none focus:outline-none',
|
||||
'md:max-h-full md:overflow-y-hidden md:hover:overflow-y-auto',
|
||||
'md:max-h-full md:overflow-y-hidden md:hover:overflow-y-auto pointer-coarse:md:overflow-y-auto',
|
||||
'md:hover:[overflow-y:_overlay]',
|
||||
)}
|
||||
id={ElementIds.ContentList}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { StreamingFileReader } from '@standardnotes/filepicker'
|
||||
import SearchBar from '../SearchBar/SearchBar'
|
||||
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
|
||||
type Props = {
|
||||
accountMenuController: AccountMenuController
|
||||
@@ -50,7 +51,7 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
selectionController,
|
||||
searchOptionsController,
|
||||
}) => {
|
||||
const { toggleAppPane } = useResponsiveAppPane()
|
||||
const { isNotesListVisibleOnTablets, toggleAppPane } = useResponsiveAppPane()
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const itemsViewPanelRef = useRef<HTMLDivElement>(null)
|
||||
@@ -181,17 +182,24 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
[isFilesSmartView],
|
||||
)
|
||||
|
||||
const matchesMediumBreakpoint = useMediaQuery(MediaQueryBreakpoints.md)
|
||||
const matchesXLBreakpoint = useMediaQuery(MediaQueryBreakpoints.xl)
|
||||
const isTabletScreenSize = matchesMediumBreakpoint && !matchesXLBreakpoint
|
||||
|
||||
return (
|
||||
<div
|
||||
id="items-column"
|
||||
className={classNames(
|
||||
'sn-component section app-column flex h-screen flex-col md:h-full',
|
||||
'xl:w-87.5 xsm-only:!w-full sm-only:!w-full pointer-coarse:md-only:!w-52 pointer-coarse:lg-only:!w-52',
|
||||
'sn-component section app-column flex h-screen flex-col pt-safe-top md:h-full',
|
||||
'xl:w-87.5 xsm-only:!w-full sm-only:!w-full',
|
||||
isTabletScreenSize && !isNotesListVisibleOnTablets
|
||||
? 'pointer-coarse:md-only:!w-0 pointer-coarse:lg-only:!w-0'
|
||||
: 'pointer-coarse:md-only:!w-60 pointer-coarse:lg-only:!w-60',
|
||||
)}
|
||||
aria-label={'Notes & Files'}
|
||||
ref={itemsViewPanelRef}
|
||||
>
|
||||
<ResponsivePaneContent paneId={AppPaneId.Items} className="min-h-[85vh]">
|
||||
<ResponsivePaneContent paneId={AppPaneId.Items}>
|
||||
<div id="items-title-bar" className="section-title-bar border-b border-solid border-border">
|
||||
<div id="items-title-bar-container">
|
||||
<input
|
||||
|
||||
+29
-10
@@ -7,6 +7,7 @@ import MenuItem from '@/Components/Menu/MenuItem'
|
||||
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
|
||||
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
||||
import { DisplayOptionsMenuProps } from './DisplayOptionsMenuProps'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
|
||||
const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
closeDisplayOptionsMenu,
|
||||
@@ -14,17 +15,35 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
isOpen,
|
||||
isFilesSmartView,
|
||||
}) => {
|
||||
const [sortBy, setSortBy] = useState(() => application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt))
|
||||
const [sortReverse, setSortReverse] = useState(() => application.getPreference(PrefKey.SortNotesReverse, false))
|
||||
const [hidePreview, setHidePreview] = useState(() => application.getPreference(PrefKey.NotesHideNotePreview, false))
|
||||
const [hideDate, setHideDate] = useState(() => application.getPreference(PrefKey.NotesHideDate, false))
|
||||
const [hideTags, setHideTags] = useState(() => application.getPreference(PrefKey.NotesHideTags, true))
|
||||
const [hidePinned, setHidePinned] = useState(() => application.getPreference(PrefKey.NotesHidePinned, false))
|
||||
const [showArchived, setShowArchived] = useState(() => application.getPreference(PrefKey.NotesShowArchived, false))
|
||||
const [showTrashed, setShowTrashed] = useState(() => application.getPreference(PrefKey.NotesShowTrashed, false))
|
||||
const [hideProtected, setHideProtected] = useState(() => application.getPreference(PrefKey.NotesHideProtected, false))
|
||||
const [sortBy, setSortBy] = useState(() =>
|
||||
application.getPreference(PrefKey.SortNotesBy, PrefDefaults[PrefKey.SortNotesBy]),
|
||||
)
|
||||
const [sortReverse, setSortReverse] = useState(() =>
|
||||
application.getPreference(PrefKey.SortNotesReverse, PrefDefaults[PrefKey.SortNotesReverse]),
|
||||
)
|
||||
const [hidePreview, setHidePreview] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesHideNotePreview, PrefDefaults[PrefKey.NotesHideNotePreview]),
|
||||
)
|
||||
const [hideDate, setHideDate] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesHideDate, PrefDefaults[PrefKey.NotesHideDate]),
|
||||
)
|
||||
const [hideTags, setHideTags] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesHideTags, PrefDefaults[PrefKey.NotesHideTags]),
|
||||
)
|
||||
const [hidePinned, setHidePinned] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesHidePinned, PrefDefaults[PrefKey.NotesHidePinned]),
|
||||
)
|
||||
const [showArchived, setShowArchived] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesShowArchived, PrefDefaults[PrefKey.NotesShowArchived]),
|
||||
)
|
||||
const [showTrashed, setShowTrashed] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesShowTrashed, PrefDefaults[PrefKey.NotesShowTrashed]),
|
||||
)
|
||||
const [hideProtected, setHideProtected] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesHideProtected, PrefDefaults[PrefKey.NotesHideProtected]),
|
||||
)
|
||||
const [hideEditorIcon, setHideEditorIcon] = useState(() =>
|
||||
application.getPreference(PrefKey.NotesHideEditorIcon, false),
|
||||
application.getPreference(PrefKey.NotesHideEditorIcon, PrefDefaults[PrefKey.NotesHideEditorIcon]),
|
||||
)
|
||||
|
||||
const toggleSortReverse = useCallback(() => {
|
||||
|
||||
@@ -28,16 +28,12 @@ const FileView = ({ application, viewControllerManager, file }: FileViewProps) =
|
||||
}, [application, file])
|
||||
|
||||
return shouldShowProtectedOverlay ? (
|
||||
<div aria-label="Note" className="section editor sn-component">
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<ProtectedItemOverlay
|
||||
viewControllerManager={viewControllerManager}
|
||||
hasProtectionSources={application.hasProtectionSources()}
|
||||
onViewItem={dismissProtectedOverlay}
|
||||
itemType={'note'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ProtectedItemOverlay
|
||||
viewControllerManager={viewControllerManager}
|
||||
hasProtectionSources={application.hasProtectionSources()}
|
||||
onViewItem={dismissProtectedOverlay}
|
||||
itemType={'file'}
|
||||
/>
|
||||
) : (
|
||||
<FileViewWithoutProtection application={application} viewControllerManager={viewControllerManager} file={file} />
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRef } from 'react'
|
||||
import AccountMenu, { AccountMenuProps } from '../AccountMenu/AccountMenu'
|
||||
import Icon from '../Icon/Icon'
|
||||
import Popover from '../Popover/Popover'
|
||||
import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
||||
|
||||
type Props = AccountMenuProps & {
|
||||
isOpen: boolean
|
||||
@@ -25,18 +26,20 @@ const AccountMenuButton = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
ref={buttonRef}
|
||||
onClick={toggleMenu}
|
||||
className={classNames(
|
||||
isOpen ? 'bg-border' : '',
|
||||
'flex h-full w-8 cursor-pointer items-center justify-center rounded-full',
|
||||
)}
|
||||
>
|
||||
<div className={hasError ? 'text-danger' : user ? 'text-info' : 'text-neutral'}>
|
||||
<Icon type="account-circle" className="h-5 w-5 hover:text-info" />
|
||||
</div>
|
||||
</button>
|
||||
<StyledTooltip label="Open account menu">
|
||||
<button
|
||||
ref={buttonRef}
|
||||
onClick={toggleMenu}
|
||||
className={classNames(
|
||||
isOpen ? 'bg-border' : '',
|
||||
'flex h-full w-8 cursor-pointer items-center justify-center rounded-full',
|
||||
)}
|
||||
>
|
||||
<div className={hasError ? 'text-danger' : user ? 'text-info' : 'text-neutral'}>
|
||||
<Icon type="account-circle" className="h-5 w-5 hover:text-info" />
|
||||
</div>
|
||||
</button>
|
||||
</StyledTooltip>
|
||||
<Popover anchorElement={buttonRef.current} open={isOpen} togglePopover={toggleMenu} side="top" className="py-2">
|
||||
<AccountMenu
|
||||
onClickOutside={onClickOutside}
|
||||
|
||||
@@ -18,6 +18,8 @@ import { AccountMenuPane } from '../AccountMenu/AccountMenuPane'
|
||||
import { EditorEventSource } from '@/Types/EditorEventSource'
|
||||
import QuickSettingsButton from './QuickSettingsButton'
|
||||
import AccountMenuButton from './AccountMenuButton'
|
||||
import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
||||
import UpgradeNow from './UpgradeNow'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -336,6 +338,11 @@ class Footer extends PureComponent<Props, State> {
|
||||
this.viewControllerManager.quickSettingsMenuController.closeQuickSettingsMenu()
|
||||
}
|
||||
|
||||
openPreferences = () => {
|
||||
this.clickOutsideQuickSettingsMenu()
|
||||
this.viewControllerManager.preferencesController.openPreferences()
|
||||
}
|
||||
|
||||
override render() {
|
||||
return (
|
||||
<div className="sn-component">
|
||||
@@ -356,15 +363,30 @@ class Footer extends PureComponent<Props, State> {
|
||||
viewControllerManager={this.viewControllerManager}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative z-footer-bar-item select-none">
|
||||
<StyledTooltip label="Open preferences">
|
||||
<button
|
||||
onClick={this.openPreferences}
|
||||
className="flex h-full w-8 cursor-pointer items-center justify-center"
|
||||
>
|
||||
<div className="h-5">
|
||||
<Icon type="tune" className="rounded hover:text-info" />
|
||||
</div>
|
||||
</button>
|
||||
</StyledTooltip>
|
||||
</div>
|
||||
<div className="relative z-footer-bar-item select-none">
|
||||
<QuickSettingsButton
|
||||
isOpen={this.state.showQuickSettingsMenu}
|
||||
toggleMenu={this.quickSettingsClickHandler}
|
||||
application={this.application}
|
||||
preferencesController={this.viewControllerManager.preferencesController}
|
||||
quickSettingsMenuController={this.viewControllerManager.quickSettingsMenuController}
|
||||
/>
|
||||
</div>
|
||||
<UpgradeNow
|
||||
application={this.application}
|
||||
featuresController={this.viewControllerManager.featuresController}
|
||||
/>
|
||||
{this.state.showBetaWarning && (
|
||||
<Fragment>
|
||||
<div className="relative z-footer-bar-item ml-3 flex select-none items-center border-l border-solid border-border pl-3">
|
||||
@@ -417,14 +439,16 @@ class Footer extends PureComponent<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
{this.state.hasPasscode && (
|
||||
<div
|
||||
id="lock-item"
|
||||
onClick={this.lockClickHandler}
|
||||
title="Locks application and wipes unencrypted data from memory."
|
||||
className="relative z-footer-bar-item ml-3 flex cursor-pointer select-none items-center border-l border-solid border-border pl-2 hover:text-info"
|
||||
>
|
||||
<Icon type="lock-filled" size="custom" className="h-4.5 w-4.5" />
|
||||
</div>
|
||||
<StyledTooltip label="Lock application">
|
||||
<div
|
||||
id="lock-item"
|
||||
onClick={this.lockClickHandler}
|
||||
title="Locks application and wipes unencrypted data from memory."
|
||||
className="relative z-footer-bar-item ml-3 flex cursor-pointer select-none items-center border-l border-solid border-border pl-2 hover:text-info"
|
||||
>
|
||||
<Icon type="lock-filled" size="custom" className="h-4.5 w-4.5" />
|
||||
</div>
|
||||
</StyledTooltip>
|
||||
)}
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,40 +1,35 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PreferencesController } from '@/Controllers/PreferencesController'
|
||||
import { QuickSettingsController } from '@/Controllers/QuickSettingsController'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
import { useRef } from 'react'
|
||||
import Icon from '../Icon/Icon'
|
||||
import Popover from '../Popover/Popover'
|
||||
import QuickSettingsMenu from '../QuickSettingsMenu/QuickSettingsMenu'
|
||||
import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean
|
||||
toggleMenu: () => void
|
||||
application: WebApplication
|
||||
preferencesController: PreferencesController
|
||||
quickSettingsMenuController: QuickSettingsController
|
||||
}
|
||||
|
||||
const QuickSettingsButton = ({
|
||||
application,
|
||||
isOpen,
|
||||
toggleMenu,
|
||||
preferencesController,
|
||||
quickSettingsMenuController,
|
||||
}: Props) => {
|
||||
const QuickSettingsButton = ({ application, isOpen, toggleMenu, quickSettingsMenuController }: Props) => {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={toggleMenu}
|
||||
className="flex h-full w-8 cursor-pointer items-center justify-center"
|
||||
ref={buttonRef}
|
||||
>
|
||||
<div className="h-5">
|
||||
<Icon type="tune" className={classNames(isOpen && 'text-info', 'rounded hover:text-info')} />
|
||||
</div>
|
||||
</button>
|
||||
<StyledTooltip label="Open quick settings menu">
|
||||
<button
|
||||
onClick={toggleMenu}
|
||||
className="flex h-full w-8 cursor-pointer items-center justify-center"
|
||||
ref={buttonRef}
|
||||
>
|
||||
<div className="h-5">
|
||||
<Icon type="themes" className={classNames(isOpen && 'text-info', 'rounded hover:text-info')} />
|
||||
</div>
|
||||
</button>
|
||||
</StyledTooltip>
|
||||
<Popover
|
||||
togglePopover={toggleMenu}
|
||||
anchorElement={buttonRef.current}
|
||||
@@ -43,11 +38,7 @@ const QuickSettingsButton = ({
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<QuickSettingsMenu
|
||||
preferencesController={preferencesController}
|
||||
quickSettingsMenuController={quickSettingsMenuController}
|
||||
application={application}
|
||||
/>
|
||||
<QuickSettingsMenu quickSettingsMenuController={quickSettingsMenuController} application={application} />
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { loadPurchaseFlowUrl } from '../PurchaseFlow/PurchaseFlowFunctions'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
featuresController: FeaturesController
|
||||
}
|
||||
|
||||
const UpgradeNow = ({ application, featuresController }: Props) => {
|
||||
const shouldShowCTA = !featuresController.hasFolders
|
||||
const hasAccount = application.hasAccount()
|
||||
|
||||
const openPlansPage = () => {
|
||||
if (!window.plansUrl) {
|
||||
return
|
||||
}
|
||||
|
||||
if (application.isNativeMobileWeb()) {
|
||||
application.mobileDevice.openUrl(window.plansUrl)
|
||||
} else {
|
||||
window.location.assign(window.plansUrl)
|
||||
}
|
||||
}
|
||||
|
||||
return shouldShowCTA ? (
|
||||
<div className="flex h-full items-center px-2">
|
||||
<button
|
||||
className="rounded bg-info py-0.5 px-1.5 text-xs font-bold uppercase text-info-contrast hover:brightness-125"
|
||||
onClick={() => {
|
||||
if (hasAccount) {
|
||||
void loadPurchaseFlowUrl(application)
|
||||
return
|
||||
}
|
||||
|
||||
openPlansPage()
|
||||
}}
|
||||
>
|
||||
Upgrade now
|
||||
</button>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
|
||||
export default observer(UpgradeNow)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user