Compare commits

...
Author SHA1 Message Date
StandardNotes CI ebd56cd5aa chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-09-21 19:32:23 +00:00
Aman Harwara 8fa566336c fix: iOS safe area using CSS (#1610) 2022-09-22 00:22:11 +05:30
Aman Harwara 9dfeac7489 fix: disable focus mode and dynamic panel switches on mobile (#1607) 2022-09-21 22:09:02 +05:30
Aman Harwara d1b71861ec feat: add lock button to navigation panel on mobile (#1609) 2022-09-21 21:40:32 +05:30
StandardNotes CI 766548f12d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
2022-09-21 15:42:20 +00:00
Aman Harwara 581a08cd09 fix: tag input auto-submitting when pressing space (#1608) 2022-09-21 20:39:29 +05:30
Aman Harwara ee0d44405d fix: revert webview alert changes (#1606) 2022-09-21 20:18:56 +05:30
Aman Harwara 1ae64dcac4 fix: iOS webview window size and padding (#1605) 2022-09-21 19:58:46 +05:30
StandardNotes CI 2be4fb4f83 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].5
2022-09-21 13:19:58 +00:00
Mo 271a8d1d70 fix(web): subscription sharing copy 2022-09-21 07:50:17 -05:00
Karol Sójko e809328fa8 feat(snjs): add retrieving web sockets token for establishing connection (#1602)
* feat(snjs): add retrieving web sockets token for establishing connection

* fix(snjs): imports

* fix(snjs): use only web sockets connection token

* fix(snjs): linter issue
2022-09-21 14:39:43 +02:00
StandardNotes CI 784cefa26f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-09-20 17:45:27 +00:00
Mo c5e225d335 fix: move wrapped storage to unwrapped if not encrypted (#1603) 2022-09-20 12:18:00 -05:00
StandardNotes CI cb4c0f1b5c chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-09-20 14:32:24 +00:00
Aman Harwara 91d9364e95 fix: close menu & toggle notes list when note action occurs (#1601) 2022-09-20 19:33:14 +05:30
74 changed files with 542 additions and 223 deletions
+18 -1
View File
@@ -1,2 +1,19 @@
node_modules
scripts
scripts
actions
.yarn
.vscode
.sass-cache
.husky
.github
.git
**/dist
**/coverage
**/node_modules
packages/components
packages/docs
packages/mobile/android
packages/mobile/ios
packages/mobile/html
packages/snjs/mocha
*.d.ts
+1 -1
View File
@@ -1,4 +1,4 @@
{
"extends": ["./node_modules/@standardnotes/config/src/.eslintrc"],
"ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__"]
"ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__", "**/**/coverage"]
}
+1
View File
@@ -1,2 +1,3 @@
node_modules
dist
coverage
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.8.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
### Bug Fixes
* move wrapped storage to unwrapped if not encrypted ([#1603](https://github.com/standardnotes/app/issues/1603)) ([c5e225d](https://github.com/standardnotes/app/commit/c5e225d335537257f44168b8ad05222afccb508a))
# [1.8.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.8.0",
"version": "1.8.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+3
View File
@@ -0,0 +1,3 @@
node_modules
dist
jsign
+22
View File
@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
* move wrapped storage to unwrapped if not encrypted ([#1603](https://github.com/standardnotes/app/issues/1603)) ([c5e225d](https://github.com/standardnotes/app/commit/c5e225d335537257f44168b8ad05222afccb508a))
## [3.23.135](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.134](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.23.134",
"version": "3.23.139",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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.15.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-15)
**Note:** Version bump only for package @standardnotes/encryption
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.15.2",
"version": "1.15.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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.23.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-15)
**Note:** Version bump only for package @standardnotes/filepicker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.23.2",
"version": "1.23.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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.10.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-15)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.10.2",
"version": "1.10.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+2 -1
View File
@@ -5,4 +5,5 @@ ios
e2e
android
fastlane
WebFrame
WebFrame
__tests__
+26
View File
@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
* move wrapped storage to unwrapped if not encrypted ([#1603](https://github.com/standardnotes/app/issues/1603)) ([c5e225d](https://github.com/standardnotes/app/commit/c5e225d335537257f44168b8ad05222afccb508a))
## [3.35.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/mobile
## [3.35.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/mobile
@@ -4,7 +4,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="stylesheet" href="web-src/app.css" />
<script>
window.defaultSyncServer = "https://api.standardnotes.com";
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.35.2",
"version": "3.35.7",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+1 -2
View File
@@ -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'
@@ -150,7 +150,6 @@ export const AppStackComponent = (props: ModalStackNavigationProp<'AppStack'>) =
onDrawerStateChanged={handleDrawerStateChange}
renderNavigationView={() => !isLocked && <MainSideMenu drawerRef={drawerRef.current} />}
>
<StatusBar translucent={!shouldOpenWebApp} />
<DrawerLayout
ref={noteDrawerRef}
drawerWidth={getDefaultDrawerWidth(dimensions)}
+1 -1
View File
@@ -570,7 +570,7 @@ export class ApplicationState extends ApplicationService {
).includes(state)
}
private async getScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
private async getScreenshotPrivacyEnabled(): Promise<boolean> {
return this.application.getMobileScreenshotPrivacyEnabled()
}
+6 -1
View File
@@ -73,6 +73,11 @@ export class MobileDevice implements MobileDeviceInterface {
;(this.stateObserverService as unknown) = undefined
}
consoleLog(...args: any[]): void {
// eslint-disable-next-line no-console
console.log(args)
}
async setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void> {
await Keychain.setKeys(value)
}
@@ -378,7 +383,7 @@ export class MobileDevice implements MobileDeviceInterface {
await Keychain.clearKeys()
}
async setAndroidScreenshotPrivacy(enable: boolean): Promise<void> {
setAndroidScreenshotPrivacy(enable: boolean): void {
if (Platform.OS === 'android') {
enable ? FlagSecure.activate() : FlagSecure.deactivate()
}
@@ -1,11 +1,12 @@
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 { WebView, WebViewMessageEvent } from 'react-native-webview'
import { AppStateObserverService } from './AppStateObserverService'
const LoggingEnabled = false
const LoggingEnabled = IsDev
export const MobileWebAppContainer = () => {
const [identifier, setIdentifier] = useState(Math.random())
@@ -175,7 +176,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')}
@@ -38,14 +38,14 @@ export const SecuritySection = (props: Props) => {
useEffect(() => {
let mounted = true
const getHasScreenshotPrivacy = async () => {
const hasScreenshotPrivacyEnabled = (await application?.getMobileScreenshotPrivacyEnabled()) ?? true
const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled()
if (mounted) {
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
}
}
void getHasScreenshotPrivacy()
const getHasBiometrics = async () => {
const appHasBiometrics = await application!.hasBiometrics()
const appHasBiometrics = application!.hasBiometrics()
if (mounted) {
setHasBiometrics(appHasBiometrics)
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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.18.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-15)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.18.2",
"version": "1.18.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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.3.58](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.57](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.3.57",
"version": "1.3.62",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+1
View File
@@ -1,2 +1,3 @@
node_modules
dist
coverage
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
### Bug Fixes
* move wrapped storage to unwrapped if not encrypted ([#1603](https://github.com/standardnotes/app/issues/1603)) ([c5e225d](https://github.com/standardnotes/app/commit/c5e225d335537257f44168b8ad05222afccb508a))
## [1.10.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-13)
**Note:** Version bump only for package @standardnotes/responses
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.10.1",
"version": "1.10.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
### Bug Fixes
* move wrapped storage to unwrapped if not encrypted ([#1603](https://github.com/standardnotes/app/issues/1603)) ([c5e225d](https://github.com/standardnotes/app/commit/c5e225d335537257f44168b8ad05222afccb508a))
## [1.21.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.21.1",
"version": "1.21.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -6,8 +6,9 @@ export interface MobileDeviceInterface extends DeviceInterface {
getRawKeychainValue(): Promise<RawKeychainValue | undefined>
getDeviceBiometricsAvailability(): Promise<boolean>
setAndroidScreenshotPrivacy(enable: boolean): Promise<void>
setAndroidScreenshotPrivacy(enable: boolean): void
authenticateWithBiometrics(): Promise<boolean>
hideMobileInterfaceFromScreenshots(): void
stopHidingMobileInterfaceFromScreenshots(): void
consoleLog(...args: any[]): void
}
+5 -1
View File
@@ -2,4 +2,8 @@ node_modules
dist
test
*.config.js
mocha/**/*
mocha/**/*
coverage
e2e-server.js
jest-global.ts
webpack.*.js
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [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
* move wrapped storage to unwrapped if not encrypted ([#1603](https://github.com/standardnotes/app/issues/1603)) ([c5e225d](https://github.com/standardnotes/app/commit/c5e225d335537257f44168b8ad05222afccb508a))
## [2.130.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
**Note:** Version bump only for package @standardnotes/snjs
+36 -16
View File
@@ -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
@@ -324,11 +330,12 @@ export class SNApplication
)
}
}
await this.handleStage(ExternalServices.ApplicationStage.StorageDecrypted_09)
this.apiService.loadHost()
this.webSocketsService.loadWebSocketUrl()
this.sessionManager.initializeFromDisk()
await this.sessionManager.initializeFromDisk()
this.settingsService.initializeFromDisk()
@@ -929,30 +936,30 @@ export class SNApplication
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
}
async setBiometricsTiming(timing: MobileUnlockTiming) {
setBiometricsTiming(timing: MobileUnlockTiming) {
return this.protectionService.setBiometricsTiming(timing)
}
async getMobileScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
getMobileScreenshotPrivacyEnabled(): boolean {
return this.protectionService.getMobileScreenshotPrivacyEnabled()
}
async getMobilePasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as Promise<
MobileUnlockTiming | undefined
>
}
async getMobileBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as Promise<
MobileUnlockTiming | undefined
>
}
async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
return this.protectionService.setMobileScreenshotPrivacyEnabled(isEnabled)
}
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as
| MobileUnlockTiming
| undefined
}
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as
| MobileUnlockTiming
| undefined
}
async loadMobileUnlockTiming() {
return this.protectionService.loadMobileUnlockTiming()
}
@@ -1093,6 +1100,8 @@ export class SNApplication
this.createUserApiService()
this.createSubscriptionServer()
this.createSubscriptionApiService()
this.createWebSocketServer()
this.createWebSocketApiService()
this.createSubscriptionManager()
this.createWebSocketsService()
this.createSessionManager()
@@ -1134,6 +1143,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
@@ -1251,6 +1262,7 @@ export class SNApplication
this.webSocketsService = new InternalServices.SNWebSocketsService(
this.diskStorageService,
this.options.webSocketUrl,
this.webSocketApiService,
this.internalEventBus,
)
this.services.push(this.webSocketsService)
@@ -1336,6 +1348,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)
}
@@ -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()
}
}
@@ -257,36 +257,36 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
]
}
private async getBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
return this.storageService.getValue<Promise<MobileUnlockTiming | undefined>>(
private getBiometricsTiming(): MobileUnlockTiming | undefined {
return this.storageService.getValue<MobileUnlockTiming | undefined>(
StorageKey.MobileBiometricsTiming,
StorageValueModes.Nonwrapped,
)
}
private async getPasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
return this.storageService.getValue<Promise<MobileUnlockTiming | undefined>>(
private getPasscodeTiming(): MobileUnlockTiming | undefined {
return this.storageService.getValue<MobileUnlockTiming | undefined>(
StorageKey.MobilePasscodeTiming,
StorageValueModes.Nonwrapped,
)
}
async setBiometricsTiming(timing: MobileUnlockTiming) {
await this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
this.mobileBiometricsTiming = timing
}
async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default)
}
async getMobileScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default)
getMobileScreenshotPrivacyEnabled(): boolean {
return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default, false)
}
async loadMobileUnlockTiming() {
this.mobilePasscodeTiming = await this.getPasscodeTiming()
this.mobileBiometricsTiming = await this.getBiometricsTiming()
loadMobileUnlockTiming(): void {
this.mobilePasscodeTiming = this.getPasscodeTiming()
this.mobileBiometricsTiming = this.getBiometricsTiming()
}
private async validateOrRenewSession(
@@ -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()
}
}
@@ -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) {
@@ -0,0 +1,33 @@
import { DiskStorageService } from './DiskStorageService'
import { InternalEventBus, DeviceInterface, InternalEventBusInterface } from '@standardnotes/services'
import { Environment } from '@standardnotes/models'
describe('diskStorageService', () => {
let storageService: DiskStorageService
let internalEventBus: InternalEventBusInterface
let device: DeviceInterface
beforeEach(() => {
internalEventBus = {} as jest.Mocked<InternalEventBus>
device = {} as jest.Mocked<DeviceInterface>
storageService = new DiskStorageService(device, 'test', Environment.Desktop, internalEventBus)
})
it('setInitialValues should set unwrapped values as wrapped value if wrapped value is not encrypted', async () => {
storageService.isStorageWrapped = jest.fn().mockReturnValue(false)
await storageService['setInitialValues']({
wrapped: { content: { foo: 'bar' } } as never,
nonwrapped: {},
unwrapped: { bar: 'zoo' },
})
expect(storageService['values']).toEqual({
wrapped: { content: { foo: 'bar' } } as never,
nonwrapped: {},
unwrapped: { bar: 'zoo', foo: 'bar' },
})
})
})
@@ -112,14 +112,10 @@ export class DiskStorageService extends Services.AbstractService implements Serv
const value = await this.deviceInterface.getRawStorageValue(this.getPersistenceKey())
const values = value ? JSON.parse(value as string) : undefined
this.setInitialValues(values)
await this.setInitialValues(values)
}
/**
* Called by platforms with the value they load from disk,
* after they handle initializeFromDisk
*/
private setInitialValues(values?: Services.StorageValuesObject) {
private async setInitialValues(values?: Services.StorageValuesObject) {
const sureValues = values || this.defaultValuesObject()
if (!sureValues[Services.ValueModesKeys.Unwrapped]) {
@@ -127,6 +123,13 @@ export class DiskStorageService extends Services.AbstractService implements Serv
}
this.values = sureValues
if (!this.isStorageWrapped()) {
this.values[Services.ValueModesKeys.Unwrapped] = {
...(this.values[Services.ValueModesKeys.Wrapped].content as object),
...this.values[Services.ValueModesKeys.Unwrapped],
}
}
}
public isStorageWrapped(): boolean {
@@ -370,7 +373,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
* Clears simple values from storage only. Does not affect payloads.
*/
async clearValues() {
this.setInitialValues()
await this.setInitialValues()
await this.immediatelyPersistValuesToDisk()
}
+1 -1
View File
@@ -1,5 +1,5 @@
module.exports = {
extends: ['../.eslintrc.js'],
extends: ['../.eslintrc'],
globals: {
chai: true,
chaiAsPromised: true,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.130.1",
"version": "2.131.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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.2.6](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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.2.6",
"version": "1.2.7",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -0,0 +1,4 @@
dist
node_modules
web.webpack-defaults.js
coverage
+3 -1
View File
@@ -19,7 +19,9 @@
"__mocks__",
"src/components",
"src/favicon",
"src/vendor"
"src/vendor",
"coverage",
"*.config.js"
],
"rules": {
"standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [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
* move wrapped storage to unwrapped if not encrypted ([#1603](https://github.com/standardnotes/app/issues/1603)) ([c5e225d](https://github.com/standardnotes/app/commit/c5e225d335537257f44168b8ad05222afccb508a))
## [3.50.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
### Bug Fixes
* close menu & toggle notes list when note action occurs ([#1601](https://github.com/standardnotes/app/issues/1601)) ([91d9364](https://github.com/standardnotes/app/commit/91d9364e950bc5e613b449427fa32761feb30365))
## [3.50.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-20)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.50.2",
"version": "3.51.0",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -76,6 +76,11 @@ export class WebApplication extends SNApplication implements WebApplicationInter
if (this.isNativeMobileWeb()) {
this.mobileWebReceiver = new MobileWebReceiver(this)
// eslint-disable-next-line no-console
console.log = (...args) => {
this.mobileDevice.consoleLog(...args)
}
}
this.onVisibilityChange = () => {
@@ -228,7 +233,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
async handleMobileGainingFocusEvent(): Promise<void> {}
async handleMobileLosingFocusEvent(): Promise<void> {
if (await this.getMobileScreenshotPrivacyEnabled()) {
if (this.getMobileScreenshotPrivacyEnabled()) {
this.mobileDevice.stopHidingMobileInterfaceFromScreenshots()
}
@@ -236,7 +241,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
}
async handleMobileResumingFromBackgroundEvent(): Promise<void> {
if (await this.getMobileScreenshotPrivacyEnabled()) {
if (this.getMobileScreenshotPrivacyEnabled()) {
this.mobileDevice.hideMobileInterfaceFromScreenshots()
}
}
@@ -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}>
@@ -175,14 +175,14 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
return (
<PremiumModalProvider application={application} viewControllerManager={viewControllerManager}>
<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}
>
<ResponsivePaneProvider>
<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
application={application}
@@ -197,52 +197,52 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
searchOptionsController={viewControllerManager.searchOptionsController}
/>
<NoteGroupView application={application} />
</ResponsivePaneProvider>
</FileDragNDropProvider>
</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}
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>
<>
<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>
</ResponsivePaneProvider>
</PremiumModalProvider>
)
}
@@ -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 />
@@ -8,6 +8,8 @@ import { FilesController } from '@/Controllers/FilesController'
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { formatSizeToReadableString } from '@standardnotes/filepicker'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
type Props = {
closeMenu: () => void
@@ -30,6 +32,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
}) => {
const { selectedFiles } = selectionController
const { handleFileAction } = filesController
const { toggleAppPane } = useResponsiveAppPane()
const hasProtectedFiles = useMemo(() => selectedFiles.some((file) => file.protected), [selectedFiles])
const hasSelectedMultipleFiles = useMemo(() => selectedFiles.length > 1, [selectedFiles.length])
@@ -68,6 +71,11 @@ const FileMenuOptions: FunctionComponent<Props> = ({
closeMenu()
}, [closeMenu, handleFileAction, selectedFiles])
const closeMenuAndToggleFilesList = useCallback(() => {
toggleAppPane(AppPaneId.Items)
closeMenu()
}, [closeMenu, toggleAppPane])
return (
<>
<button
@@ -139,6 +147,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
<button
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={() => {
closeMenuAndToggleFilesList()
void filesController.deleteFilesPermanently(selectionController.selectedFiles)
}}
>
@@ -1,4 +1,4 @@
import { IconType } from '@standardnotes/snjs'
export const PremiumFeatureIconName: IconType = 'asterisk'
export const PremiumFeatureIconClass = 'text-info'
export const PremiumFeatureIconClass = 'text-info group-disabled:text-passive-2'
@@ -22,6 +22,15 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
const [panelWidth, setPanelWidth] = useState<number>(0)
const { toggleAppPane } = useResponsiveAppPane()
const [hasPasscode, setHasPasscode] = useState(() => application.hasPasscode())
useEffect(() => {
const removeObserver = application.addEventObserver(async () => {
setHasPasscode(application.hasPasscode())
}, ApplicationEvent.KeyStatusChanged)
return removeObserver
}, [application])
useEffect(() => {
const removeObserver = application.addEventObserver(async () => {
const width = application.getPreference(PrefKey.TagsPanelWidth)
@@ -87,18 +96,29 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
onClick={() => {
viewControllerManager.accountMenuController.toggleShow()
}}
title="Go to items list"
aria-label="Go to items list"
title="Go to account menu"
aria-label="Go to account menu"
>
<Icon type="account-circle" />
</button>
{hasPasscode && (
<button
id="lock-item"
onClick={() => application.lock()}
title="Locks application and wipes unencrypted data from memory."
aria-label="Locks application and wipes unencrypted data from memory."
className="ml-2.5 flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border bg-default text-neutral hover:bg-contrast focus:bg-contrast"
>
<Icon type="lock-filled" size="custom" className="h-4.5 w-4.5" />
</button>
)}
<button
className="ml-2.5 flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border bg-default text-neutral hover:bg-contrast focus:bg-contrast"
onClick={() => {
viewControllerManager.quickSettingsMenuController.toggle()
}}
title="Go to items list"
aria-label="Go to items list"
title="Go to quick settings menu"
aria-label="Go to quick settings menu"
>
<Icon type="tune" />
</button>
@@ -23,10 +23,12 @@ const NotesContextMenu = ({
noteTagsController,
historyModalController,
}: Props) => {
const { contextMenuOpen, contextMenuClickLocation } = notesController
const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = notesController
const contextMenuRef = useRef<HTMLDivElement>(null)
const closeMenu = () => setContextMenuOpen(!contextMenuOpen)
return (
<Popover
align="start"
@@ -37,7 +39,7 @@ const NotesContextMenu = ({
className="py-2"
open={contextMenuOpen}
side="right"
togglePopover={() => notesController.setContextMenuOpen(!contextMenuOpen)}
togglePopover={closeMenu}
>
<div ref={contextMenuRef}>
<NotesOptions
@@ -46,6 +48,7 @@ const NotesContextMenu = ({
notesController={notesController}
noteTagsController={noteTagsController}
historyModalController={historyModalController}
closeMenu={closeMenu}
/>
</div>
</Popover>
@@ -13,6 +13,8 @@ import { NotesOptionsProps } from './NotesOptionsProps'
import { NotesController } from '@/Controllers/NotesController'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { formatDateForContextMenu } from '@/Utils/DateUtils'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
type DeletePermanentlyButtonProps = {
onClick: () => void
@@ -175,8 +177,10 @@ const NotesOptions = ({
notesController,
noteTagsController,
historyModalController,
closeMenu,
}: NotesOptionsProps) => {
const [altKeyDown, setAltKeyDown] = useState(false)
const { toggleAppPane } = useResponsiveAppPane()
const toggleOn = (condition: (note: SNNote) => boolean) => {
const notesMatchingAttribute = notes.filter(condition)
@@ -252,11 +256,15 @@ const NotesOptions = ({
}
}, [application, getNoteFileName, notes])
const duplicateSelectedItems = useCallback(() => {
notes.forEach((note) => {
application.mutator.duplicateItem(note).catch(console.error)
})
}, [application, notes])
const closeMenuAndToggleNotesList = useCallback(() => {
toggleAppPane(AppPaneId.Items)
closeMenu()
}, [closeMenu, toggleAppPane])
const duplicateSelectedItems = useCallback(async () => {
await Promise.all(notes.map((note) => application.mutator.duplicateItem(note).catch(console.error)))
closeMenuAndToggleNotesList()
}, [application.mutator, closeMenuAndToggleNotesList, notes])
const openRevisionHistoryModal = useCallback(() => {
historyModalController.openModal(notesController.firstSelectedNote)
@@ -365,8 +373,9 @@ const NotesOptions = ({
{unarchived && (
<button
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={() => {
notesController.setArchiveSelectedNotes(true).catch(console.error)
onClick={async () => {
await notesController.setArchiveSelectedNotes(true).catch(console.error)
closeMenuAndToggleNotesList()
}}
>
<Icon type="archive" className={iconClassWarning} />
@@ -376,8 +385,9 @@ const NotesOptions = ({
{archived && (
<button
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={() => {
notesController.setArchiveSelectedNotes(false).catch(console.error)
onClick={async () => {
await notesController.setArchiveSelectedNotes(false).catch(console.error)
closeMenuAndToggleNotesList()
}}
>
<Icon type="unarchive" className={iconClassWarning} />
@@ -389,6 +399,7 @@ const NotesOptions = ({
<DeletePermanentlyButton
onClick={async () => {
await notesController.deleteNotesPermanently()
closeMenuAndToggleNotesList()
}}
/>
) : (
@@ -396,6 +407,7 @@ const NotesOptions = ({
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={async () => {
await notesController.setTrashSelectedNotes(true)
closeMenuAndToggleNotesList()
}}
>
<Icon type="trash" className={iconClassDanger} />
@@ -408,6 +420,7 @@ const NotesOptions = ({
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={async () => {
await notesController.setTrashSelectedNotes(false)
closeMenuAndToggleNotesList()
}}
>
<Icon type="restore" className={iconClassSuccess} />
@@ -416,12 +429,14 @@ const NotesOptions = ({
<DeletePermanentlyButton
onClick={async () => {
await notesController.deleteNotesPermanently()
closeMenuAndToggleNotesList()
}}
/>
<button
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
onClick={async () => {
await notesController.emptyTrash()
closeMenuAndToggleNotesList()
}}
>
<div className="flex items-start">
@@ -55,6 +55,7 @@ const NotesOptionsPanel = ({
notesController={notesController}
noteTagsController={noteTagsController}
historyModalController={historyModalController}
closeMenu={toggleMenu}
/>
</Popover>
</>
@@ -10,4 +10,5 @@ export type NotesOptionsProps = {
notesController: NotesController
noteTagsController: NoteTagsController
historyModalController: HistoryModalController
closeMenu: () => void
}
@@ -57,9 +57,8 @@ const PositionedPopoverContent = ({
<Portal>
<div
className={classNames(
'absolute top-0 left-0 flex h-full w-full min-w-80 cursor-auto flex-col overflow-y-auto rounded bg-default shadow-main md:h-auto md:max-w-xs',
'safe-area-padding absolute top-0 left-0 flex h-full w-full min-w-80 cursor-auto flex-col overflow-y-auto rounded bg-default shadow-main md:h-auto md:max-w-xs',
overrideZIndex ? overrideZIndex : 'z-dropdown-menu',
className,
)}
style={{
...styles,
@@ -71,15 +70,17 @@ const PositionedPopoverContent = ({
}}
data-popover={id}
>
<div className="md:hidden">
<div className="flex items-center justify-end px-3">
<button className="rounded-full border border-border p-1" onClick={togglePopover}>
<Icon type="close" className="h-4 w-4" />
</button>
<div className={className}>
<div className="md:hidden">
<div className="flex items-center justify-end px-3">
<button className="rounded-full border border-border p-1" onClick={togglePopover}>
<Icon type="close" className="h-4 w-4" />
</button>
</div>
<HorizontalSeparator classes="my-2" />
</div>
<HorizontalSeparator classes="my-2" />
{children}
</div>
{children}
</div>
</Portal>
)
@@ -51,7 +51,7 @@ const InvitationsList = ({ subscriptionState, application }: Props) => {
return (
<div>
<SubtitleLight className="mb-2 text-info">Active Invitations:</SubtitleLight>
<SubtitleLight className="mb-2 text-info">Active Invites:</SubtitleLight>
{activeSubscriptions?.map((invitation) => (
<div key={invitation.uuid} className="mt-1 mb-4">
<Text>
@@ -64,7 +64,7 @@ const InvitationsList = ({ subscriptionState, application }: Props) => {
))}
{!!inActiveSubscriptions?.length && (
<>
<SubtitleLight className="mb-2 text-info">Inactive Invitations:</SubtitleLight>
<SubtitleLight className="mb-2 text-info">Inactive Invites:</SubtitleLight>
<div>
{inActiveSubscriptions?.map((invitation) => (
<div key={invitation.uuid} className="mb-3 first:mt-2">
@@ -13,7 +13,7 @@ import InviteForm from './InviteForm'
import InviteSuccess from './InviteSuccess'
enum SubmitButtonTitles {
Default = 'Send Invitation',
Default = 'Send Invite',
Sending = 'Sending...',
Finish = 'Finish',
}
@@ -96,7 +96,7 @@ const Invite: FunctionComponent<Props> = ({ onCloseDialog, application, subscrip
const success = await processInvite()
if (!success) {
application.alertService
.alert('We could not send the invitation. Please try again or contact support if the issue persists.')
.alert('An error occurred while sending the invite. Please try again or contact support if the issue persists.')
.catch(console.error)
resetProgressState()
@@ -112,7 +112,7 @@ const Invite: FunctionComponent<Props> = ({ onCloseDialog, application, subscrip
return (
<div>
<ModalDialog>
<ModalDialogLabel closeDialog={handleDialogClose}>Invite</ModalDialogLabel>
<ModalDialogLabel closeDialog={handleDialogClose}>Share your Subscription</ModalDialogLabel>
<ModalDialogDescription className="flex flex-row items-center px-4.5">
{currentStep === Steps.InitialStep && <InviteForm setInviteeEmail={setInviteeEmail} />}
{currentStep === Steps.FinishStep && <InviteSuccess />}
@@ -9,8 +9,8 @@ const SharingStatusText = ({ subscriptionState }: Props) => {
return (
<Text className="mt-1">
You have have used <span className="font-bold">{usedInvitationsCount}</span> out of {allowedInvitationsCount}{' '}
subscription invitations.
You've used <span className="font-bold">{usedInvitationsCount}</span> out of {allowedInvitationsCount}{' '}
subscription invites.
</Text>
)
}
@@ -20,7 +20,7 @@ const BiometricsLock = ({ application }: Props) => {
useEffect(() => {
const getHasBiometrics = async () => {
const appHasBiometrics = await application.hasBiometrics()
const appHasBiometrics = application.hasBiometrics()
setHasBiometrics(appHasBiometrics)
}
@@ -50,7 +50,7 @@ const BiometricsLock = ({ application }: Props) => {
await disableBiometrics()
} else {
setHasBiometrics(true)
await application.enableBiometrics()
application.enableBiometrics()
await setBiometricsTimingValue(MobileUnlockTiming.OnQuit)
}
}
@@ -13,22 +13,19 @@ type Props = {
}
const MultitaskingPrivacy = ({ application }: Props) => {
const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState<boolean | undefined>(false)
const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState<boolean>(false)
useEffect(() => {
const getHasScreenshotPrivacy = async () => {
const hasScreenshotPrivacyEnabled = (await application.getMobileScreenshotPrivacyEnabled()) ?? true
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
}
void getHasScreenshotPrivacy()
const hasScreenshotPrivacyEnabled = application.getMobileScreenshotPrivacyEnabled()
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
}, [application])
const onScreenshotPrivacyPress = async () => {
const enable = !hasScreenshotPrivacy
setHasScreenshotPrivacy(enable)
await application.setMobileScreenshotPrivacyEnabled(enable)
await (application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable)
application.setMobileScreenshotPrivacyEnabled(enable)
;(application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable)
}
const screenshotPrivacyFeatureText = isIOS() ? 'Multitasking Privacy' : 'Multitasking/Screenshot Privacy'
@@ -5,6 +5,7 @@ import Icon from '@/Components/Icon/Icon'
import { usePremiumModal } from '@/Hooks/usePremiumModal'
import Switch from '@/Components/Switch/Switch'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
import { isMobileScreen } from '@/Utils'
type Props = {
application: WebApplication
@@ -34,11 +35,12 @@ const FocusModeSwitch: FunctionComponent<Props> = ({ application, onToggle, onCl
return (
<>
<button
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
className="group flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none disabled:bg-default disabled:text-passive-2"
onClick={toggle}
disabled={application.isNativeMobileWeb() || isMobileScreen()}
>
<div className="flex items-center">
<Icon type="menu-close" className="mr-2 text-neutral" />
<Icon type="menu-close" className="mr-2 text-neutral group-disabled:text-passive-2" />
Focused Writing
</div>
{isEntitled ? (
@@ -1,5 +1,5 @@
import { WebApplication } from '@/Application/Application'
import { FeatureStatus } from '@standardnotes/snjs'
import { FeatureIdentifier, FeatureStatus } from '@standardnotes/snjs'
import { FunctionComponent, MouseEventHandler, useCallback, useMemo } from 'react'
import Icon from '@/Components/Icon/Icon'
import { usePremiumModal } from '@/Hooks/usePremiumModal'
@@ -7,6 +7,7 @@ import Switch from '@/Components/Switch/Switch'
import { ThemeItem } from './ThemeItem'
import RadioIndicator from '../RadioIndicator/RadioIndicator'
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
import { isMobileScreen } from '@/Utils'
type Props = {
item: ThemeItem
@@ -43,11 +44,14 @@ const ThemesMenuButton: FunctionComponent<Props> = ({ application, item }) => {
[application, canActivateTheme, item, premiumModal],
)
const isMobile = application.isNativeMobileWeb() || isMobileScreen()
return (
<button
className={
'flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:bg-info-backdrop focus:shadow-none focus:shadow-none'
'group flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none disabled:bg-default disabled:text-passive-2'
}
disabled={item.identifier === FeatureIdentifier.DynamicTheme && isMobile}
onClick={toggleTheme}
>
{item.component?.isLayerable() ? (
@@ -1,6 +1,6 @@
import { ElementIds } from '@/Constants/ElementIDs'
import { isMobileScreen } from '@/Utils'
import { useEffect, ReactNode, useMemo, createContext, useCallback, useContext, useState } from 'react'
import { useEffect, ReactNode, useMemo, createContext, useCallback, useContext, useState, memo } from 'react'
import { AppPaneId } from './AppPaneMetadata'
type ResponsivePaneData = {
@@ -24,6 +24,8 @@ type Props = {
children: ReactNode
}
const MemoizedChildren = memo(({ children }: Props) => <div>{children}</div>)
const ResponsivePaneProvider = ({ children }: Props) => {
const [currentSelectedPane, setCurrentSelectedPane] = useState<AppPaneId>(
isMobileScreen() ? AppPaneId.Items : AppPaneId.Editor,
@@ -63,7 +65,11 @@ const ResponsivePaneProvider = ({ children }: Props) => {
[currentSelectedPane, toggleAppPane],
)
return <ResponsivePaneContext.Provider value={contextValue}>{children}</ResponsivePaneContext.Provider>
return (
<ResponsivePaneContext.Provider value={contextValue}>
<MemoizedChildren children={children} />
</ResponsivePaneContext.Provider>
)
}
export default ResponsivePaneProvider
@@ -112,6 +112,8 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
return (
<>
<div
role="button"
tabIndex={0}
className={classNames('tag py-2 px-3.5 md:py-1', isSelected && 'selected', isFaded && 'opacity-50')}
onClick={selectCurrentTag}
style={{
@@ -202,7 +202,9 @@ export const TagsListItem: FunctionComponent<Props> = observer(({ tag, features,
return (
<>
<button
<div
role="button"
tabIndex={0}
className={classNames(
'tag py-2 px-3.5 focus:shadow-inner md:py-1',
isSelected && 'selected',
@@ -272,7 +274,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(({ tag, features,
<div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}>
{tag.conflictOf && <div className="danger text-[0.625rem] font-bold">Conflicted Copy {tag.conflictOf}</div>}
</div>
</button>
</div>
{isAddingSubtag && (
<div
className="tag overflow-hidden"
+16
View File
@@ -23,6 +23,15 @@
--sn-stylekit-editor-font-family: var(--sn-stylekit-sans-serif-font);
--reach-checkbox: 1;
--safe-area-inset-top: env(safe-area-inset-top, 0);
--safe-area-inset-bottom: env(safe-area-inset-bottom, 0);
--safe-area-inset-left: env(safe-area-inset-left, 0);
--safe-area-inset-right: env(safe-area-inset-right, 0);
--safe-viewport-height: calc(
var(--viewport-height) - calc(var(--safe-area-inset-top) + var(--safe-area-inset-bottom))
);
}
html {
@@ -31,9 +40,16 @@ html {
body {
font-size: var(--sn-stylekit-base-font-size);
background-color: var(--sn-stylekit-background-color);
color: var(--sn-stylekit-foreground-color);
}
body,
.safe-area-padding {
padding: var(--safe-area-inset-top) var(--safe-area-inset-right) var(--safe-area-inset-bottom)
var(--safe-area-inset-left);
}
html,
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
+3 -3
View File
@@ -43,16 +43,16 @@ module.exports = {
125: '31.25rem',
},
height: {
screen: 'var(--viewport-height)',
screen: 'var(--safe-viewport-height)',
},
minHeight: {
1: '0.25rem',
2: '0.5rem',
screen: 'var(--viewport-height)',
screen: 'var(--safe-viewport-height)',
},
maxHeight: {
110: '27.5rem',
screen: 'var(--viewport-height)',
screen: 'var(--safe-viewport-height)',
},
zIndex: {
'editor-content': 'var(--z-index-editor-content)',