Compare commits

..
Author SHA1 Message Date
StandardNotes CI f02465346f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-09-29 15:10:20 +00:00
Aman Harwara 11dd39c126 feat: add custom note title format pref (#1678) 2022-09-29 20:10:05 +05:30
Mo d7a90c4d91 refactor: add pkc fields for registration endpoint (#1680) 2022-09-29 09:13:05 -05:00
StandardNotes CI 00075616e6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].3
2022-09-29 13:41:22 +00:00
Karol Sójko 5ffffbff20 feat(snjs): add e2e test for subsequent subscriptions settings persistance 2022-09-29 15:11:44 +02:00
StandardNotes CI ecd8d56171 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
2022-09-29 10:01:20 +00:00
Karol Sójko 33226000f5 fix(snjs): increase wait time in retrieving settings test for processing replaceable settings 2022-09-29 11:30:36 +02:00
Karol Sójko c841ac99d2 fix(snjs): instructions on running e2e locally 2022-09-29 11:07:12 +02:00
StandardNotes CI b0e708186c chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-09-29 08:54:37 +00:00
Aman Harwara 20e420820d fix: ipad web view ui improvements (#1664) 2022-09-29 13:47:00 +05:30
StandardNotes CI c3d6a91730 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-09-29 07:45:24 +00:00
Aman Harwara 0ed692ff9b feat: add panel settings section in quick settings (#1669) 2022-09-29 12:46:06 +05:30
StandardNotes CI c74fa272fb chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-09-28 16:34:58 +00:00
Aman Harwara 3f9d3ceffa fix: improve biometrics input on mobile webview (#1658) 2022-09-28 21:26:20 +05:30
63 changed files with 821 additions and 205 deletions
Binary file not shown.
+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.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.8.4",
"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
}
+24
View File
@@ -3,6 +3,30 @@
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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.23.169",
"version": "3.23.175",
"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.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.15.6",
"version": "1.15.7",
"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.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.23.6",
"version": "1.23.7",
"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.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.10.6",
"version": "1.10.7",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+24
View File
@@ -3,6 +3,30 @@
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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.37.3",
"version": "3.37.9",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+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.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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.19.0",
"version": "1.20.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -33,11 +33,13 @@ export enum PrefKey {
MobileSelectedTagUuid = 'mobileSelectedTagUuid',
MobileNotesHideEditorIcon = 'mobileHideEditorIcon',
NewNoteTitleFormat = 'newNoteTitleFormat',
CustomNoteTitleFormat = 'customNoteTitleFormat',
}
export enum NewNoteTitleFormat {
CurrentDateAndTime = 'CurrentDateAndTime',
CurrentNoteCount = 'CurrentNoteCount',
CustomFormat = 'CustomFormat',
Empty = 'Empty',
}
@@ -73,4 +75,5 @@ export type PrefValue = {
[PrefKey.MobileSelectedTagUuid]: string | undefined
[PrefKey.MobileNotesHideEditorIcon]: boolean
[PrefKey.NewNoteTitleFormat]: NewNoteTitleFormat
[PrefKey.CustomNoteTitleFormat]: string
}
+24
View File
@@ -3,6 +3,30 @@
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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.3.94",
"version": "1.3.100",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.22.1",
"version": "1.22.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+17
View File
@@ -3,6 +3,23 @@
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
+7 -1
View File
@@ -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:
@@ -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)
+7 -37
View File
@@ -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)
+31
View 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
}
+5 -1
View File
@@ -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
+84 -2
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.133.1",
"version": "2.134.1",
"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.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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.3.6",
"version": "1.3.7",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+32
View File
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [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
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.62.3",
"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",
+10 -4
View File
@@ -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
@@ -27,6 +27,7 @@ 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
@@ -210,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) {
@@ -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'
@@ -35,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()
@@ -125,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)
@@ -180,7 +193,7 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
<ResponsivePaneProvider>
<PremiumModalProvider application={application} viewControllerManager={viewControllerManager}>
<div className={platformString + ' main-ui-view sn-component'}>
<div id="app" className={appClass + ' app app-column-container'}>
<div id="app" className="app app-column-container" ref={appColumnContainerRef}>
<FileDragNDropProvider
application={application}
featuresController={viewControllerManager.featuresController}
@@ -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
@@ -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}
@@ -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,12 +182,19 @@ 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 pt-safe-top 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',
'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}
@@ -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(() => {
@@ -100,6 +100,7 @@ export const ICONS = {
unarchive: icons.UnarchiveIcon,
unpin: icons.UnpinIcon,
user: icons.UserIcon,
view: icons.ViewIcon,
warning: icons.WarningIcon,
window: icons.WindowIcon,
}
@@ -22,7 +22,7 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
const viewControllerManager = useMemo(() => application.getViewControllerManager(), [application])
const ref = useRef<HTMLDivElement>(null)
const [panelWidth, setPanelWidth] = useState<number>(0)
const { toggleAppPane } = useResponsiveAppPane()
const { selectedPane, toggleAppPane } = useResponsiveAppPane()
const [hasPasscode, setHasPasscode] = useState(() => application.hasPasscode())
useEffect(() => {
@@ -63,7 +63,11 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
<div
id="navigation"
className={classNames(
'sn-component section app-column h-screen max-h-screen w-[220px] overflow-hidden pt-safe-top md:h-full md:max-h-full md:min-h-0 md:py-0 xsm-only:!w-full sm-only:!w-full',
'sn-component section app-column h-screen max-h-screen overflow-hidden pt-safe-top md:h-full md:max-h-full md:min-h-0 md:pb-0',
'w-[220px] xl:w-[220px] xsm-only:!w-full sm-only:!w-full',
selectedPane === AppPaneId.Navigation
? 'pointer-coarse:md-only:!w-48 pointer-coarse:lg-only:!w-48'
: 'pointer-coarse:md-only:!w-0 pointer-coarse:lg-only:!w-0',
isIOS() ? 'pb-safe-bottom' : 'pb-2.5',
)}
ref={ref}
@@ -71,7 +75,7 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
<ResponsivePaneContent paneId={AppPaneId.Navigation} contentElementId="navigation-content">
<div
className={classNames(
'flex-grow overflow-y-auto overflow-x-hidden md:overflow-y-hidden md:hover:overflow-y-auto',
'flex-grow overflow-y-auto overflow-x-hidden md:overflow-y-hidden md:hover:overflow-y-auto pointer-coarse:md:overflow-y-auto',
'md:hover:[overflow-y:_overlay]',
)}
>
@@ -3,13 +3,17 @@ import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
export const NavigationMenuButton = () => {
const { toggleAppPane } = useResponsiveAppPane()
const { selectedPane, toggleAppPane } = useResponsiveAppPane()
return (
<button
className="bg-text-padding mr-3 inline-flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border align-middle text-neutral hover:bg-contrast focus:bg-contrast md:hidden"
className="bg-text-padding mr-3 inline-flex h-8 min-w-8 cursor-pointer items-center justify-center rounded-full border border-solid border-border align-middle text-neutral hover:bg-contrast focus:bg-contrast md:hidden pointer-coarse:md-only:inline-flex pointer-coarse:lg-only:inline-flex"
onClick={() => {
toggleAppPane(AppPaneId.Navigation)
if (selectedPane === AppPaneId.Items || selectedPane === AppPaneId.Editor) {
toggleAppPane(AppPaneId.Navigation)
} else {
toggleAppPane(AppPaneId.Items)
}
}}
title="Navigation menu"
aria-label="Navigation menu"
@@ -1,20 +1,36 @@
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import Icon from '../Icon/Icon'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { useMediaQuery, MediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
import { IconType } from '@standardnotes/snjs'
const MobileItemsListButton = () => {
const { toggleAppPane } = useResponsiveAppPane()
const { toggleAppPane, isNotesListVisibleOnTablets, toggleNotesListOnTablets } = useResponsiveAppPane()
const matchesMediumBreakpoint = useMediaQuery(MediaQueryBreakpoints.md)
const matchesXLBreakpoint = useMediaQuery(MediaQueryBreakpoints.xl)
const isTabletScreenSize = matchesMediumBreakpoint && !matchesXLBreakpoint
const iconType: IconType = isTabletScreenSize && !isNotesListVisibleOnTablets ? 'chevron-right' : 'chevron-left'
const label = isTabletScreenSize
? isNotesListVisibleOnTablets
? 'Hide items list'
: 'Show items list'
: 'Go to items list'
return (
<button
className="bg-text-padding mr-3 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 md:hidden"
className="bg-text-padding mr-3 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 md:hidden pointer-coarse:md-only:flex pointer-coarse:lg-only:flex"
onClick={() => {
toggleAppPane(AppPaneId.Items)
if (isTabletScreenSize) {
toggleNotesListOnTablets()
} else {
toggleAppPane(AppPaneId.Items)
}
}}
title="Go to items list"
aria-label="Go to items list"
title={label}
aria-label={label}
>
<Icon type="chevron-left" />
<Icon type={iconType} />
</button>
)
}
@@ -41,6 +41,7 @@ import AutoresizingNoteViewTextarea from './AutoresizingTextarea'
import MobileItemsListButton from '../NoteGroupView/MobileItemsListButton'
import NoteTagsPanel from '../NoteTags/NoteTagsPanel'
import NoteTagsContainer from '../NoteTags/NoteTagsContainer'
import { PrefDefaults } from '@/Constants/PrefDefaults'
const MinimumStatusDuration = 400
const TextareaDebounce = 100
@@ -686,9 +687,15 @@ class NoteView extends PureComponent<NoteViewProps, State> {
}
async reloadPreferences() {
const monospaceFont = this.application.getPreference(PrefKey.EditorMonospaceEnabled, true)
const monospaceFont = this.application.getPreference(
PrefKey.EditorMonospaceEnabled,
PrefDefaults[PrefKey.EditorMonospaceEnabled],
)
const marginResizersEnabled = this.application.getPreference(PrefKey.EditorResizersEnabled, true)
const marginResizersEnabled = this.application.getPreference(
PrefKey.EditorResizersEnabled,
PrefDefaults[PrefKey.EditorResizersEnabled],
)
await this.reloadSpellcheck()
@@ -700,14 +707,14 @@ class NoteView extends PureComponent<NoteViewProps, State> {
reloadFont(monospaceFont)
if (marginResizersEnabled) {
const width = this.application.getPreference(PrefKey.EditorWidth, null)
const width = this.application.getPreference(PrefKey.EditorWidth, PrefDefaults[PrefKey.EditorWidth])
if (width != null) {
this.setState({
leftResizerWidth: width,
rightResizerWidth: width,
})
}
const left = this.application.getPreference(PrefKey.EditorLeft, null)
const left = this.application.getPreference(PrefKey.EditorLeft, PrefDefaults[PrefKey.EditorLeft])
if (left != null) {
this.setState({
leftResizerOffset: left,
@@ -58,8 +58,9 @@ const PositionedPopoverContent = ({
<Portal>
<div
className={classNames(
'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',
'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',
!isDesktopScreen ? 'pt-safe-top pb-safe-bottom' : '',
)}
style={{
...styles,
@@ -13,6 +13,7 @@ import PreferencesPane from '../PreferencesComponents/PreferencesPane'
import PreferencesGroup from '../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../PreferencesComponents/PreferencesSegment'
import { PremiumFeatureIconName } from '@/Components/Icon/PremiumFeatureIcon'
import { PrefDefaults } from '@/Constants/PrefDefaults'
type Props = {
application: WebApplication
@@ -24,18 +25,17 @@ const Appearance: FunctionComponent<Props> = ({ application }) => {
application.features.getFeatureStatus(FeatureIdentifier.MidnightTheme) === FeatureStatus.Entitled
const [themeItems, setThemeItems] = useState<DropdownItem[]>([])
const [autoLightTheme, setAutoLightTheme] = useState<string>(
() => application.getPreference(PrefKey.AutoLightThemeIdentifier, 'Default') as string,
const [autoLightTheme, setAutoLightTheme] = useState<string>(() =>
application.getPreference(PrefKey.AutoLightThemeIdentifier, PrefDefaults[PrefKey.AutoLightThemeIdentifier]),
)
const [autoDarkTheme, setAutoDarkTheme] = useState<string>(
() =>
application.getPreference(
PrefKey.AutoDarkThemeIdentifier,
isEntitledToMidnightTheme ? FeatureIdentifier.MidnightTheme : 'Default',
) as string,
const [autoDarkTheme, setAutoDarkTheme] = useState<string>(() =>
application.getPreference(
PrefKey.AutoDarkThemeIdentifier,
isEntitledToMidnightTheme ? FeatureIdentifier.MidnightTheme : PrefDefaults[PrefKey.AutoDarkThemeIdentifier],
),
)
const [useDeviceSettings, setUseDeviceSettings] = useState(
() => application.getPreference(PrefKey.UseSystemColorScheme, false) as boolean,
const [useDeviceSettings, setUseDeviceSettings] = useState(() =>
application.getPreference(PrefKey.UseSystemColorScheme, PrefDefaults[PrefKey.UseSystemColorScheme]),
)
useEffect(() => {
@@ -11,13 +11,15 @@ import {
} from '@standardnotes/snjs'
import { Subtitle, Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
import { WebApplication } from '@/Application/Application'
import { FunctionComponent, useEffect, useState } from 'react'
import { FunctionComponent, useEffect, useMemo, useState } from 'react'
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
import Switch from '@/Components/Switch/Switch'
import { PLAIN_EDITOR_NAME } from '@/Constants/Constants'
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
import Button from '@/Components/Button/Button'
import CustomNoteTitleFormat from './Defaults/CustomNoteTitleFormat'
import { PrefDefaults } from '@/Constants/PrefDefaults'
type Props = {
application: WebApplication
@@ -60,10 +62,12 @@ const Defaults: FunctionComponent<Props> = ({ application }) => {
() => getDefaultEditor(application)?.package_info?.identifier || 'plain-editor',
)
const [spellcheck, setSpellcheck] = useState(() => application.getPreference(PrefKey.EditorSpellcheck, true))
const [spellcheck, setSpellcheck] = useState(() =>
application.getPreference(PrefKey.EditorSpellcheck, PrefDefaults[PrefKey.EditorSpellcheck]),
)
const [newNoteTitleFormat, setNewNoteTitleFormat] = useState(() =>
application.getPreference(PrefKey.NewNoteTitleFormat, NewNoteTitleFormat.CurrentDateAndTime),
application.getPreference(PrefKey.NewNoteTitleFormat, PrefDefaults[PrefKey.NewNoteTitleFormat]),
)
const handleNewNoteTitleFormatChange = (value: string) => {
setNewNoteTitleFormat(value as NewNoteTitleFormat)
@@ -71,7 +75,7 @@ const Defaults: FunctionComponent<Props> = ({ application }) => {
}
const [addNoteToParentFolders, setAddNoteToParentFolders] = useState(() =>
application.getPreference(PrefKey.NoteAddToParentFolders, true),
application.getPreference(PrefKey.NoteAddToParentFolders, PrefDefaults[PrefKey.NoteAddToParentFolders]),
)
const toggleSpellcheck = () => {
@@ -128,6 +132,28 @@ const Defaults: FunctionComponent<Props> = ({ application }) => {
}, 1000)
}
const noteTitleFormatOptions = useMemo(
() => [
{
label: 'Current date and time',
value: NewNoteTitleFormat.CurrentDateAndTime,
},
{
label: 'Current note count',
value: NewNoteTitleFormat.CurrentNoteCount,
},
{
label: 'Custom format',
value: NewNoteTitleFormat.CustomFormat,
},
{
label: 'Empty',
value: NewNoteTitleFormat.Empty,
},
],
[],
)
return (
<PreferencesGroup>
<PreferencesSegment>
@@ -166,25 +192,13 @@ const Defaults: FunctionComponent<Props> = ({ application }) => {
<Dropdown
id="def-new-note-title-format"
label="Select the default note type"
items={[
{
label: 'Current date and time',
value: NewNoteTitleFormat.CurrentDateAndTime,
},
{
label: 'Current note count',
value: NewNoteTitleFormat.CurrentNoteCount,
},
{
label: 'Empty',
value: NewNoteTitleFormat.Empty,
},
]}
items={noteTitleFormatOptions}
value={newNoteTitleFormat}
onChange={handleNewNoteTitleFormatChange}
/>
</div>
</div>
{newNoteTitleFormat === NewNoteTitleFormat.CustomFormat && <CustomNoteTitleFormat application={application} />}
<HorizontalSeparator classes="my-4" />
<div className="flex items-center justify-between">
<div className="flex flex-col">
@@ -0,0 +1,67 @@
import { WebApplication } from '@/Application/Application'
import { Text, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { PrefKey } from '@standardnotes/snjs'
import { ChangeEventHandler, useRef, useState } from 'react'
import dayjs from 'dayjs'
type Props = {
application: WebApplication
}
const PrefChangeDebounceTimeInMs = 25
const CustomNoteTitleFormat = ({ application }: Props) => {
const [customNoteTitleFormat, setCustomNoteTitleFormat] = useState(() =>
application.getPreference(PrefKey.CustomNoteTitleFormat, PrefDefaults[PrefKey.CustomNoteTitleFormat]),
)
const setCustomNoteTitleFormatPreference = () => {
application.setPreference(PrefKey.CustomNoteTitleFormat, customNoteTitleFormat)
}
const debounceTimeoutRef = useRef<number>()
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (event) => {
setCustomNoteTitleFormat(event.currentTarget.value)
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current)
}
debounceTimeoutRef.current = window.setTimeout(async () => {
setCustomNoteTitleFormatPreference()
}, PrefChangeDebounceTimeInMs)
}
return (
<>
<HorizontalSeparator classes="my-4" />
<div>
<Subtitle>Custom Note Title Format</Subtitle>
<Text>
All available date-time formatting options can be found{' '}
<a className="underline" href="https://day.js.org/docs/en/display/format#list-of-all-available-formats">
here
</a>
. Use square brackets (<code>[]</code>) to escape date-time formatting.
</Text>
<div className="mt-2">
<input
className="min-w-55 rounded border border-solid border-passive-3 bg-default px-2 py-1.5 text-sm focus-within:ring-2 focus-within:ring-info"
placeholder="e.g. YYYY-MM-DD"
value={customNoteTitleFormat}
onChange={handleInputChange}
onBlur={setCustomNoteTitleFormatPreference}
/>
</div>
<div className="mt-2">
<span className="font-bold">Preview:</span> {dayjs().format(customNoteTitleFormat)}
</div>
</div>
</>
)
}
export default CustomNoteTitleFormat
@@ -7,6 +7,7 @@ import { observer } from 'mobx-react-lite'
import { FunctionComponent, useState } from 'react'
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
import { PrefDefaults } from '@/Constants/PrefDefaults'
type Props = {
application: WebApplication
@@ -14,10 +15,10 @@ type Props = {
const Tools: FunctionComponent<Props> = ({ application }: Props) => {
const [monospaceFont, setMonospaceFont] = useState(() =>
application.getPreference(PrefKey.EditorMonospaceEnabled, true),
application.getPreference(PrefKey.EditorMonospaceEnabled, PrefDefaults[PrefKey.EditorMonospaceEnabled]),
)
const [marginResizers, setMarginResizers] = useState(() =>
application.getPreference(PrefKey.EditorResizersEnabled, true),
application.getPreference(PrefKey.EditorResizersEnabled, PrefDefaults[PrefKey.EditorResizersEnabled]),
)
const toggleMonospaceFont = () => {
@@ -0,0 +1,79 @@
import { WebApplication } from '@/Application/Application'
import { memo, useEffect, useState } from 'react'
import { ApplicationEvent, PrefKey } from '@standardnotes/snjs'
import MenuItem from '../Menu/MenuItem'
import { MenuItemType } from '../Menu/MenuItemType'
import { PANEL_NAME_NAVIGATION, PANEL_NAME_NOTES } from '@/Constants/Constants'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { PrefDefaults } from '@/Constants/PrefDefaults'
type Props = {
application: WebApplication
}
const WidthForCollapsedPanel = 5
const MinimumNavPanelWidth = PrefDefaults[PrefKey.TagsPanelWidth]
const MinimumNotesPanelWidth = PrefDefaults[PrefKey.NotesPanelWidth]
const PanelSettingsSection = ({ application }: Props) => {
const [currentNavPanelWidth, setCurrentNavPanelWidth] = useState(
application.getPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth),
)
const [currentItemsPanelWidth, setCurrentItemsPanelWidth] = useState(
application.getPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth),
)
const toggleNavigationPanel = () => {
const isCollapsed = currentNavPanelWidth <= WidthForCollapsedPanel
if (isCollapsed) {
void application.setPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth)
} else {
void application.setPreference(PrefKey.TagsPanelWidth, WidthForCollapsedPanel)
}
application.publishPanelDidResizeEvent(PANEL_NAME_NAVIGATION, !isCollapsed)
}
const toggleItemsListPanel = () => {
const isCollapsed = currentItemsPanelWidth <= WidthForCollapsedPanel
if (isCollapsed) {
void application.setPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth)
} else {
void application.setPreference(PrefKey.NotesPanelWidth, WidthForCollapsedPanel)
}
application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, !isCollapsed)
}
useEffect(() => {
const removeObserver = application.addEventObserver(async () => {
setCurrentNavPanelWidth(application.getPreference(PrefKey.TagsPanelWidth, MinimumNavPanelWidth))
setCurrentItemsPanelWidth(application.getPreference(PrefKey.NotesPanelWidth, MinimumNotesPanelWidth))
}, ApplicationEvent.PreferencesChanged)
return removeObserver
}, [application])
return (
<div className="hidden text-sm md:block pointer-coarse:md-only:hidden pointer-coarse:lg-only:hidden">
<HorizontalSeparator classes="my-2" />
<div className="my-1 px-3 text-sm font-semibold uppercase text-text">Panel Settings</div>
<MenuItem
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={currentNavPanelWidth > WidthForCollapsedPanel}
onChange={toggleNavigationPanel}
>
Show navigation panel
</MenuItem>
<MenuItem
type={MenuItemType.SwitchButton}
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
checked={currentItemsPanelWidth > WidthForCollapsedPanel}
onChange={toggleItemsListPanel}
>
Show items list panel
</MenuItem>
</div>
)
}
export default memo(PanelSettingsSection)
@@ -11,6 +11,7 @@ import { sortThemes } from '@/Utils/SortThemes'
import RadioIndicator from '../RadioIndicator/RadioIndicator'
import HorizontalSeparator from '../Shared/HorizontalSeparator'
import { QuickSettingsController } from '@/Controllers/QuickSettingsController'
import PanelSettingsSection from './PanelSettingsSection'
const focusModeAnimationDuration = 1255
@@ -174,6 +175,7 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = ({ application, quickSet
onClose={closeQuickSettingsMenu}
isEnabled={focusModeEnabled}
/>
<PanelSettingsSection application={application} />
</div>
)
}
@@ -19,6 +19,8 @@ import { AppPaneId } from './AppPaneMetadata'
type ResponsivePaneData = {
selectedPane: AppPaneId
toggleAppPane: (paneId: AppPaneId) => void
isNotesListVisibleOnTablets: boolean
toggleNotesListOnTablets: () => void
}
const ResponsivePaneContext = createContext<ResponsivePaneData | undefined>(undefined)
@@ -60,15 +62,10 @@ const ResponsivePaneProvider = ({ children }: ChildrenProps) => {
const toggleAppPane = useCallback(
(paneId: AppPaneId) => {
if (paneId === currentSelectedPane) {
setCurrentSelectedPane(previousSelectedPane ? previousSelectedPane : AppPaneId.Editor)
setPreviousSelectedPane(paneId)
} else {
setPreviousSelectedPane(currentSelectedPane)
setCurrentSelectedPane(paneId)
}
setPreviousSelectedPane(currentSelectedPane)
setCurrentSelectedPane(paneId)
},
[currentSelectedPane, previousSelectedPane],
[currentSelectedPane],
)
useEffect(() => {
@@ -102,12 +99,20 @@ const ResponsivePaneProvider = ({ children }: ChildrenProps) => {
}
}, [addAndroidBackHandler, currentSelectedPaneRef, toggleAppPane])
const [isNotesListVisibleOnTablets, setNotesListVisibleOnTablets] = useState(true)
const toggleNotesListOnTablets = useCallback(() => {
setNotesListVisibleOnTablets((visible) => !visible)
}, [])
const contextValue = useMemo(
() => ({
selectedPane: currentSelectedPane,
toggleAppPane,
isNotesListVisibleOnTablets,
toggleNotesListOnTablets,
}),
[currentSelectedPane, toggleAppPane],
[currentSelectedPane, isNotesListVisibleOnTablets, toggleAppPane, toggleNotesListOnTablets],
)
return (
@@ -9,6 +9,5 @@ export default styled(Tooltip)`
background-color: var(--sn-stylekit-contrast-background-color);
color: var(--sn-stylekit-foreground-color);
border-color: var(--sn-stylekit-border-color);
z-index: var(--z-index-tooltip);
}
`
@@ -0,0 +1,27 @@
import { PrefKey, CollectionSort, NewNoteTitleFormat } from '@standardnotes/models'
export const PrefDefaults = {
[PrefKey.TagsPanelWidth]: 220,
[PrefKey.NotesPanelWidth]: 350,
[PrefKey.EditorWidth]: null,
[PrefKey.EditorLeft]: null,
[PrefKey.EditorMonospaceEnabled]: true,
[PrefKey.EditorSpellcheck]: true,
[PrefKey.EditorResizersEnabled]: true,
[PrefKey.SortNotesBy]: CollectionSort.CreatedAt,
[PrefKey.SortNotesReverse]: false,
[PrefKey.NotesShowArchived]: false,
[PrefKey.NotesShowTrashed]: false,
[PrefKey.NotesHidePinned]: false,
[PrefKey.NotesHideProtected]: false,
[PrefKey.NotesHideNotePreview]: false,
[PrefKey.NotesHideDate]: false,
[PrefKey.NotesHideTags]: true,
[PrefKey.NotesHideEditorIcon]: false,
[PrefKey.UseSystemColorScheme]: false,
[PrefKey.AutoLightThemeIdentifier]: 'Default',
[PrefKey.AutoDarkThemeIdentifier]: 'Default',
[PrefKey.NoteAddToParentFolders]: true,
[PrefKey.NewNoteTitleFormat]: NewNoteTitleFormat.CurrentDateAndTime,
[PrefKey.CustomNoteTitleFormat]: 'YYYY-MM-DD [at] hh:mm A',
} as const
@@ -31,6 +31,8 @@ import { SelectedItemsController } from '../SelectedItemsController'
import { NotesController } from '../NotesController'
import { NoteTagsController } from '../NoteTagsController'
import { formatDateAndTimeForNote } from '@/Utils/DateUtils'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import dayjs from 'dayjs'
const MinNoteCellHeight = 51.0
const DefaultListNumNotes = 20
@@ -188,6 +190,7 @@ export class ItemListController extends AbstractViewController implements Intern
notes: observable,
notesToDisplay: observable,
panelTitle: observable,
panelWidth: observable,
renderedItems: observable,
showDisplayOptionsMenu: observable,
@@ -350,7 +353,7 @@ export class ItemListController extends AbstractViewController implements Intern
const shouldShowArchivedNotes =
this.navigationController.isInSystemView(SystemViewId.ArchivedNotes) ||
this.searchOptionsController.includeArchived ||
this.application.getPreference(PrefKey.NotesShowArchived, false)
this.application.getPreference(PrefKey.NotesShowArchived, PrefDefaults[PrefKey.NotesShowArchived])
return (activeItem?.trashed && !shouldShowTrashedNotes) || (activeItem?.archived && !shouldShowArchivedNotes)
}
@@ -420,23 +423,49 @@ export class ItemListController extends AbstractViewController implements Intern
const currentSortBy = this.displayOptions.sortBy
let sortBy = this.application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt)
let sortBy = this.application.getPreference(PrefKey.SortNotesBy, PrefDefaults[PrefKey.SortNotesBy])
if (sortBy === CollectionSort.UpdatedAt || (sortBy as string) === 'client_updated_at') {
sortBy = CollectionSort.UpdatedAt
}
newDisplayOptions.sortBy = sortBy
newDisplayOptions.sortDirection =
this.application.getPreference(PrefKey.SortNotesReverse, false) === false ? 'dsc' : 'asc'
newDisplayOptions.includeArchived = this.application.getPreference(PrefKey.NotesShowArchived, false)
newDisplayOptions.includeTrashed = this.application.getPreference(PrefKey.NotesShowTrashed, false) as boolean
newDisplayOptions.includePinned = !this.application.getPreference(PrefKey.NotesHidePinned, false)
newDisplayOptions.includeProtected = !this.application.getPreference(PrefKey.NotesHideProtected, false)
this.application.getPreference(PrefKey.SortNotesReverse, PrefDefaults[PrefKey.SortNotesReverse]) === false
? 'dsc'
: 'asc'
newDisplayOptions.includeArchived = this.application.getPreference(
PrefKey.NotesShowArchived,
PrefDefaults[PrefKey.NotesShowArchived],
)
newDisplayOptions.includeTrashed = this.application.getPreference(
PrefKey.NotesShowTrashed,
PrefDefaults[PrefKey.NotesShowTrashed],
) as boolean
newDisplayOptions.includePinned = !this.application.getPreference(
PrefKey.NotesHidePinned,
PrefDefaults[PrefKey.NotesHidePinned],
)
newDisplayOptions.includeProtected = !this.application.getPreference(
PrefKey.NotesHideProtected,
PrefDefaults[PrefKey.NotesHideProtected],
)
newWebDisplayOptions.hideNotePreview = this.application.getPreference(PrefKey.NotesHideNotePreview, false)
newWebDisplayOptions.hideDate = this.application.getPreference(PrefKey.NotesHideDate, false)
newWebDisplayOptions.hideTags = this.application.getPreference(PrefKey.NotesHideTags, true)
newWebDisplayOptions.hideEditorIcon = this.application.getPreference(PrefKey.NotesHideEditorIcon, false)
newWebDisplayOptions.hideNotePreview = this.application.getPreference(
PrefKey.NotesHideNotePreview,
PrefDefaults[PrefKey.NotesHideNotePreview],
)
newWebDisplayOptions.hideDate = this.application.getPreference(
PrefKey.NotesHideDate,
PrefDefaults[PrefKey.NotesHideDate],
)
newWebDisplayOptions.hideTags = this.application.getPreference(
PrefKey.NotesHideTags,
PrefDefaults[PrefKey.NotesHideTags],
)
newWebDisplayOptions.hideEditorIcon = this.application.getPreference(
PrefKey.NotesHideEditorIcon,
PrefDefaults[PrefKey.NotesHideEditorIcon],
)
const displayOptionsChanged =
newDisplayOptions.sortBy !== this.displayOptions.sortBy ||
@@ -453,17 +482,21 @@ export class ItemListController extends AbstractViewController implements Intern
this.displayOptions = newDisplayOptions
this.webDisplayOptions = newWebDisplayOptions
const newWidth = this.application.getPreference(PrefKey.NotesPanelWidth)
if (newWidth && newWidth !== this.panelWidth) {
this.panelWidth = newWidth
}
if (!displayOptionsChanged) {
return
}
if (displayOptionsChanged) {
this.reloadNotesDisplayOptions()
}
await this.reloadItems(ItemsReloadSource.DisplayOptionsChange)
const width = this.application.getPreference(PrefKey.NotesPanelWidth)
if (width) {
this.panelWidth = width
}
if (newDisplayOptions.sortBy !== currentSortBy) {
await this.selectFirstItem()
}
@@ -489,13 +522,19 @@ export class ItemListController extends AbstractViewController implements Intern
const titleFormat = this.application.getPreference(
PrefKey.NewNoteTitleFormat,
NewNoteTitleFormat.CurrentDateAndTime,
PrefDefaults[PrefKey.NewNoteTitleFormat],
)
let title = formatDateAndTimeForNote(new Date())
if (titleFormat === NewNoteTitleFormat.CurrentNoteCount) {
title = `Note ${this.notes.length + 1}`
} else if (titleFormat === NewNoteTitleFormat.CustomFormat) {
const customFormat = this.application.getPreference(
PrefKey.CustomNoteTitleFormat,
PrefDefaults[PrefKey.CustomNoteTitleFormat],
)
title = dayjs().format(customFormat)
} else if (titleFormat === NewNoteTitleFormat.Empty) {
title = ''
}
@@ -1,4 +1,5 @@
import { ElementIds } from '@/Constants/ElementIDs'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { destroyAllObjectProperties } from '@/Utils'
import {
ApplicationEvent,
@@ -60,7 +61,10 @@ export class NoteTagsController extends AbstractViewController {
setTagsContainerMaxWidth: action,
})
this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true)
this.addNoteToParentFolders = application.getPreference(
PrefKey.NoteAddToParentFolders,
PrefDefaults[PrefKey.NoteAddToParentFolders],
)
}
public setServicesPostConstruction(itemListController: ItemListController) {
@@ -71,7 +75,10 @@ export class NoteTagsController extends AbstractViewController {
this.reloadTagsForCurrentNote()
}),
this.application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
this.addNoteToParentFolders = this.application.getPreference(PrefKey.NoteAddToParentFolders, true)
this.addNoteToParentFolders = this.application.getPreference(
PrefKey.NoteAddToParentFolders,
PrefDefaults[PrefKey.NoteAddToParentFolders],
)
}),
)
}
+5 -7
View File
@@ -13,7 +13,6 @@
--z-index-lock-screen: 10000;
--z-index-modal: 10000;
--z-index-toast: 11000;
--z-index-tooltip: 12000;
--sn-stylekit-base-font-size: 0.813rem;
--sn-stylekit-simplified-chinese-font: 'Microsoft Yahei', '微软雅黑体';
@@ -41,11 +40,6 @@ body {
color: var(--sn-stylekit-foreground-color);
}
.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',
@@ -155,12 +149,16 @@ $footer-height: 2rem;
}
.app {
height: calc(100% - #{$footer-height});
height: 100%;
overflow: hidden;
position: relative;
vertical-align: top;
width: 100%;
@media screen and (min-width: 768px) {
height: calc(var(--viewport-height) - #{$footer-height});
}
.section {
position: relative;
overflow: hidden;
+8
View File
@@ -7713,6 +7713,7 @@ __metadata:
circular-dependency-plugin: ^5.2.2
copy-webpack-plugin: ^11.0.0
css-loader: "*"
dayjs: ^1.11.5
dotenv: ^16.0.2
eslint: ^8.23.1
eslint-config-prettier: ^8.5.0
@@ -16129,6 +16130,13 @@ __metadata:
languageName: node
linkType: hard
"dayjs@npm:^1.11.5":
version: 1.11.5
resolution: "dayjs@npm:1.11.5"
checksum: e3bbaa7b4883b31be4bf75a181f1447fbb19800c29b332852125aab96baeff3ac232dcba8b88c4ea17d3b636c99dac5fb9d1af4bb6ae26615698bbc4a852dffb
languageName: node
linkType: hard
"dayjs@npm:^1.8.15":
version: 1.11.3
resolution: "dayjs@npm:1.11.3"