mirror of
https://github.com/standardnotes/app
synced 2026-09-15 15:46:05 -04:00
Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ede3bada2e | ||
|
|
a4ca89118b | ||
|
|
8b9a75530c | ||
|
|
b2831963fd | ||
|
|
0f90db2836 | ||
|
|
6759386bef | ||
|
|
5ed61d1097 | ||
|
|
8bb9f4ed11 | ||
|
|
1252375a2e | ||
|
|
1e2542e4f5 | ||
|
|
633d6189fb | ||
|
|
5075b0c85a | ||
|
|
2dd0586bb1 | ||
|
|
eab3dc99ed | ||
|
|
5bb4435c5b | ||
|
|
63c10c703f | ||
|
|
4036113206 | ||
|
|
de4adca059 | ||
|
|
9ae0ce0bfd | ||
|
|
455cdc01fc | ||
|
|
c4dbc5cbd2 | ||
|
|
305ffdf984 | ||
|
|
dc4530b2f3 | ||
|
|
d6cafa1e73 | ||
|
|
ac96e197e0 | ||
|
|
636963b409 | ||
|
|
7ead0f655b | ||
|
|
6b50372db2 | ||
|
|
a6ef658dae | ||
|
|
8c49ca5572 | ||
|
|
35b21b21ce | ||
|
|
f3e2d80b44 | ||
|
|
dd821c95e6 |
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn commitlint --edit $1
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
+2
-2
@@ -22,12 +22,12 @@
|
||||
"lint": "yarn workspaces foreach -t --verbose --exclude @standardnotes/components-meta run lint",
|
||||
"test": "yarn workspaces foreach -pt --jobs 10 --verbose --exclude @standardnotes/components-meta run test",
|
||||
"clean": "lerna run clean",
|
||||
"build:all": "yarn workspaces foreach -pt --verbose --exclude '{@standardnotes/docs}' run build",
|
||||
"build:all": "yarn workspaces foreach -pt --topological-dev --verbose --exclude '{@standardnotes/docs}' run build",
|
||||
"build:components": "yarn workspaces foreach -pt --verbose --interlaced -R --from @standardnotes/components-meta run build",
|
||||
"build:web": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/web --exclude @standardnotes/components-meta run build",
|
||||
"build:desktop": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/desktop --exclude @standardnotes/components-meta run build",
|
||||
"build:mobile": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/mobile --exclude @standardnotes/components-meta run build",
|
||||
"build:snjs": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/snjs --exclude @standardnotes/components-meta run build",
|
||||
"build:snjs": "yarn workspaces foreach -p --topological-dev --verbose -R --from @standardnotes/snjs --exclude @standardnotes/components-meta run build",
|
||||
"build:services": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/services --exclude @standardnotes/components-meta run build",
|
||||
"build:api": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/api --exclude @standardnotes/components-meta run build",
|
||||
"start:server:web": "lerna run start --scope=@standardnotes/web",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.19.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.19.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.19.0",
|
||||
"version": "1.19.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.7.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
## [2.7.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
+6
-6
@@ -70,9 +70,9 @@
|
||||
"binary": "b621bfd81829a205f2f02e85d446e277ad20e367b92676ec20b3ee59b1bfab3f"
|
||||
},
|
||||
"org.standardnotes.markdown-visual-editor": {
|
||||
"version": "1.1.5",
|
||||
"base64": "7aea4cbf43d8cb6411171d09c1cd7e0428d59ab53419b601011ccfeb747426a3",
|
||||
"binary": "ad75788eee720990203f8443bdc8c0a171ec404ba3462d33f833937d6ba236a4"
|
||||
"version": "1.1.6",
|
||||
"base64": "def28b9d33e378b56a610e662951cbbad1cc9313c994c0c099e63b1df8baa2ec",
|
||||
"binary": "0fb52462d0183397e259afc437c3bd338e1466138505c0fa2179c036a466e505"
|
||||
},
|
||||
"org.standardnotes.simple-task-editor": {
|
||||
"version": "1.4.4",
|
||||
@@ -80,9 +80,9 @@
|
||||
"binary": "9df983a144b9686f4f58b03bf78bf1f84f53aea60251621a75586722bf576475"
|
||||
},
|
||||
"org.standardnotes.token-vault": {
|
||||
"version": "2.2.7",
|
||||
"base64": "fd6ecde33b7135487a60d630813de8f8b4e5e0f06457c7dfda5bee28f3ea1113",
|
||||
"binary": "88d1401384373ea8bc4b1c4669b34733e24d3692f7aa544b2d6f796de2275703"
|
||||
"version": "2.2.8",
|
||||
"base64": "2d91555d9db1aab56d3e3ce15623fb7de14f4efe7d5904bd4db7d081f5991ada",
|
||||
"binary": "8a7cac8e08d1b18fa2c5a76b8a2a621f0c57b5b71eb9465eaf4de96c89361515"
|
||||
},
|
||||
"org.standardnotes.standard-sheets": {
|
||||
"version": "1.5.1",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/components-meta",
|
||||
"version": "2.7.24",
|
||||
"version": "2.7.25",
|
||||
"private": true,
|
||||
"author": "Standard Notes.",
|
||||
"main": "dist",
|
||||
|
||||
+4
@@ -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.1.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/markdown-visual
|
||||
|
||||
## [1.1.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/markdown-visual
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/markdown-visual",
|
||||
"version": "1.1.5",
|
||||
"version": "1.1.6",
|
||||
"author": "Johnny Almonte <[email protected]>",
|
||||
"description": "A lightweight WYSIWYG markdown editor for Standard Notes, derived from Milkdown.",
|
||||
"keywords": [
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/authenticator
|
||||
|
||||
## [2.2.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/authenticator
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/authenticator",
|
||||
"version": "2.2.7",
|
||||
"version": "2.2.8",
|
||||
"main": "dist/dist.js",
|
||||
"author": "Standard Notes",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,51 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.23.275](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.274](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.273](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.272](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.271](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **desktop:** editor column top padding on mac ([9ae0ce0](https://github.com/standardnotes/app/commit/9ae0ce0bfd78bedf3e8318aa56b8542dd81d5870))
|
||||
|
||||
## [3.23.270](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **desktop:** only add extra popover padding for small windows ([ac96e19](https://github.com/standardnotes/app/commit/ac96e197e00a7e7af1b771e46a178b0fa21d0f94))
|
||||
* **desktop:** selected column top padding on small windows ([d6cafa1](https://github.com/standardnotes/app/commit/d6cafa1e739dd46e4cf465d8511aad2024ebe294))
|
||||
|
||||
## [3.23.269](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.268](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.267](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.266](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.265](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -122,7 +122,7 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
|
||||
the app content height so its not overflowing */
|
||||
sheet.insertRule('body { padding-top: var(--sn-desktop-titlebar-height); }', sheet.cssRules.length)
|
||||
sheet.insertRule(
|
||||
'[data-popover] { padding-top: calc(var(--sn-desktop-titlebar-height) + 0.5rem); }',
|
||||
'@media screen and (max-width: 768px) { [data-popover] { padding-top: calc(var(--sn-desktop-titlebar-height) + 0.5rem); } }',
|
||||
sheet.cssRules.length,
|
||||
)
|
||||
sheet.insertRule(
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
transition: 0.15s padding ease;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.mac-desktop .app-column.selected {
|
||||
padding-top: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.mac-desktop #app.collapsed-notes.collapsed-navigation #editor-column {
|
||||
padding-top: 18px;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.23.265",
|
||||
"version": "3.23.275",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.19.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.19.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.19.0",
|
||||
"version": "1.19.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.25.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.25.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
# [1.25.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.25.0",
|
||||
"version": "1.25.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.12.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.12.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
# [1.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.12.0",
|
||||
"version": "1.12.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,48 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.45.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** disable pinch zoom on mobile app UI ([dc4530b](https://github.com/standardnotes/app/commit/dc4530b2f33057ec3d44a94b28dc0407656d4098))
|
||||
|
||||
## [3.45.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.45.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**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="viewport-fit=cover, width=device-width, initial-scale=1" name="viewport" />
|
||||
<meta content="viewport-fit=cover, width=device-width, initial-scale=1, maximum-scale=1.0" name="viewport" />
|
||||
<meta content="#ffffff" name="theme-color" />
|
||||
<link rel="stylesheet" href="web-src/app.css" />
|
||||
<script>
|
||||
@@ -23,4 +23,4 @@
|
||||
<body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.45.14",
|
||||
"version": "3.45.24",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.34.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
# [1.34.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
* dedicated files layout ([#1928](https://github.com/standardnotes/app/issues/1928)) ([dd821c9](https://github.com/standardnotes/app/commit/dd821c95e61c8cd82317ad9fb3788daff7241942))
|
||||
|
||||
# [1.33.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.4",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -22,21 +22,19 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@standardnotes/common": "^1.43.0",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
"@types/jest": "^28.1.5",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
||||
"eslint": "*",
|
||||
"eslint-plugin-prettier": "*",
|
||||
"jest": "^28.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ts-jest": "^28.0.5",
|
||||
"typescript": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.43.0",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
"lodash": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ItemContent } from './../Content/ItemContent'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
export type OutgoingItemMessagePayload<C extends ItemContent = ItemContent> = {
|
||||
uuid: string
|
||||
content_type: ContentType
|
||||
created_at: Date
|
||||
updated_at: Date
|
||||
deleted?: boolean
|
||||
content?: C
|
||||
clientData?: Record<string, unknown>
|
||||
|
||||
/**
|
||||
* isMetadataUpdate implies that the extension should make reference of updated
|
||||
* metadata, but not update content values as they may be stale relative to what the
|
||||
* extension currently has.
|
||||
*/
|
||||
isMetadataUpdate: boolean
|
||||
}
|
||||
@@ -17,4 +17,5 @@ export interface TagPreferences {
|
||||
customNoteTitleFormat?: string
|
||||
editorIdentifier?: FeatureIdentifier | string
|
||||
entryMode?: 'normal' | 'daily'
|
||||
panelWidth?: number
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ export * from './Abstract/Component/ComponentViewerEvent'
|
||||
export * from './Abstract/Component/IncomingComponentItemPayload'
|
||||
export * from './Abstract/Component/KeyboardModifier'
|
||||
export * from './Abstract/Component/MessageData'
|
||||
export * from './Abstract/Component/OutgoingItemMessagePayload'
|
||||
export * from './Abstract/Component/PermissionDialog'
|
||||
export * from './Abstract/Content/ItemContent'
|
||||
export * from './Abstract/Contextual/BackupFile'
|
||||
|
||||
@@ -3,6 +3,46 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.206](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.205](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.204](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.203](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.202](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.201](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.200](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.199](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.198](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.197](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.196](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.3.196",
|
||||
"version": "1.3.206",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -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.42.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
# [1.42.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add sending user requests from UI ([#1927](https://github.com/standardnotes/app/issues/1927)) ([7ead0f6](https://github.com/standardnotes/app/commit/7ead0f655bb2572e5805dd26c297d520949d661a))
|
||||
|
||||
# [1.41.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
* prioritize loading latest selected items ([#1930](https://github.com/standardnotes/app/issues/1930)) ([a6ef658](https://github.com/standardnotes/app/commit/a6ef658daef9a01f5ddacc967fe30d22f580799a))
|
||||
|
||||
## [1.40.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
# [1.40.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.40.0",
|
||||
"version": "1.42.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -38,6 +38,7 @@ export enum StorageKey {
|
||||
ExperimentalFeatures = 'experimental_features',
|
||||
DeinitMode = 'deinit_mode',
|
||||
CodeVerifier = 'code_verifier',
|
||||
LaunchPriorityUuids = 'launch_priority_uuids',
|
||||
}
|
||||
|
||||
export enum NonwrappedStorageKey {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UserRequestType } from '@standardnotes/common'
|
||||
import { DeinitSource } from '../Application/DeinitSource'
|
||||
|
||||
export interface UserClientInterface {
|
||||
@@ -6,4 +7,5 @@ export interface UserClientInterface {
|
||||
message?: string
|
||||
}>
|
||||
signOut(force?: boolean, source?: DeinitSource): Promise<void>
|
||||
submitUserRequest(requestType: UserRequestType): Promise<boolean>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { UserApiServiceInterface } from '@standardnotes/api'
|
||||
import { UserRequestType } from '@standardnotes/common'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { User } from '@standardnotes/responses'
|
||||
|
||||
import {
|
||||
AlertService,
|
||||
ChallengeServiceInterface,
|
||||
InternalEventBusInterface,
|
||||
ItemManagerInterface,
|
||||
ProtectionsClientInterface,
|
||||
} from '..'
|
||||
import { SessionsClientInterface } from '../Session/SessionsClientInterface'
|
||||
import { StorageServiceInterface } from '../Storage/StorageServiceInterface'
|
||||
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import { UserService } from './UserService'
|
||||
|
||||
describe('UserService', () => {
|
||||
let sessionManager: SessionsClientInterface
|
||||
let syncService: SyncServiceInterface
|
||||
let storageService: StorageServiceInterface
|
||||
let itemManager: ItemManagerInterface
|
||||
let protocolService: EncryptionProviderInterface
|
||||
let alertService: AlertService
|
||||
let challengeService: ChallengeServiceInterface
|
||||
let protectionService: ProtectionsClientInterface
|
||||
let userApiService: UserApiServiceInterface
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createService = () =>
|
||||
new UserService(
|
||||
sessionManager,
|
||||
syncService,
|
||||
storageService,
|
||||
itemManager,
|
||||
protocolService,
|
||||
alertService,
|
||||
challengeService,
|
||||
protectionService,
|
||||
userApiService,
|
||||
internalEventBus,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
sessionManager = {} as jest.Mocked<SessionsClientInterface>
|
||||
sessionManager.getSureUser = jest.fn().mockReturnValue({ uuid: '1-2-3' } as jest.Mocked<User>)
|
||||
|
||||
syncService = {} as jest.Mocked<SyncServiceInterface>
|
||||
|
||||
storageService = {} as jest.Mocked<StorageServiceInterface>
|
||||
|
||||
itemManager = {} as jest.Mocked<ItemManagerInterface>
|
||||
|
||||
protocolService = {} as jest.Mocked<EncryptionProviderInterface>
|
||||
|
||||
alertService = {} as jest.Mocked<AlertService>
|
||||
|
||||
challengeService = {} as jest.Mocked<ChallengeServiceInterface>
|
||||
|
||||
protectionService = {} as jest.Mocked<ProtectionsClientInterface>
|
||||
|
||||
userApiService = {} as jest.Mocked<UserApiServiceInterface>
|
||||
|
||||
internalEventBus = {} as jest.Mocked<InternalEventBusInterface>
|
||||
})
|
||||
|
||||
it('should submit a user request to the server', async () => {
|
||||
userApiService.submitUserRequest = jest.fn().mockReturnValue({ data: { success: true } })
|
||||
|
||||
expect(await createService().submitUserRequest(UserRequestType.ExitDiscount)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should indicate error if submit a user request to the server fails', async () => {
|
||||
userApiService.submitUserRequest = jest.fn().mockReturnValue({ data: { success: false } })
|
||||
|
||||
expect(await createService().submitUserRequest(UserRequestType.ExitDiscount)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should indicate error if submit a user request to the server fails with an error on server side', async () => {
|
||||
userApiService.submitUserRequest = jest.fn().mockReturnValue({ data: { error: { message: 'fail' } } })
|
||||
|
||||
expect(await createService().submitUserRequest(UserRequestType.ExitDiscount)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should indicate error if submitting a user request throws an exception', async () => {
|
||||
userApiService.submitUserRequest = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
expect(await createService().submitUserRequest(UserRequestType.ExitDiscount)).toBeFalsy()
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EncryptionProviderInterface, SNRootKey, SNRootKeyParams } from '@standardnotes/encryption'
|
||||
import { HttpResponse, SignInResponse, User } from '@standardnotes/responses'
|
||||
import { KeyParamsOrigination } from '@standardnotes/common'
|
||||
import { KeyParamsOrigination, UserRequestType } from '@standardnotes/common'
|
||||
import { UuidGenerator } from '@standardnotes/utils'
|
||||
import { UserApiServiceInterface, UserRegistrationResponseBody } from '@standardnotes/api'
|
||||
|
||||
@@ -233,6 +233,24 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
|
||||
}
|
||||
}
|
||||
|
||||
async submitUserRequest(requestType: UserRequestType): Promise<boolean> {
|
||||
const userUuid = this.sessionManager.getSureUser().uuid
|
||||
try {
|
||||
const result = await this.userApiService.submitUserRequest({
|
||||
userUuid,
|
||||
requestType,
|
||||
})
|
||||
|
||||
if (result.data.error) {
|
||||
return false
|
||||
}
|
||||
|
||||
return result.data.success
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sign in request that occurs while the user was previously signed in, to correct
|
||||
* for missing keys or storage values. Unlike regular sign in, this doesn't worry about
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.147.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.147.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
# [2.147.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
* prioritize loading latest selected items ([#1930](https://github.com/standardnotes/app/issues/1930)) ([a6ef658](https://github.com/standardnotes/app/commit/a6ef658daef9a01f5ddacc967fe30d22f580799a))
|
||||
|
||||
## [2.146.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
# [2.146.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
MessageData,
|
||||
Environment,
|
||||
Platform,
|
||||
OutgoingItemMessagePayload,
|
||||
} from '@standardnotes/models'
|
||||
import find from 'lodash/find'
|
||||
import uniq from 'lodash/uniq'
|
||||
@@ -41,7 +42,6 @@ import remove from 'lodash/remove'
|
||||
import { SNSyncService } from '@Lib/Services/Sync/SyncService'
|
||||
import { environmentToString, platformToString } from '@Lib/Application/Platforms'
|
||||
import {
|
||||
OutgoingItemMessagePayload,
|
||||
MessageReply,
|
||||
StreamItemsMessageData,
|
||||
AllowedBatchContentTypes,
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
import { ComponentArea, ComponentAction, FeatureIdentifier, LegacyFileSafeIdentifier } from '@standardnotes/features'
|
||||
import { ComponentMessage, ItemContent, MessageData } from '@standardnotes/models'
|
||||
import { ComponentMessage, MessageData, OutgoingItemMessagePayload } from '@standardnotes/models'
|
||||
import { UuidString } from '@Lib/Types/UuidString'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
export type OutgoingItemMessagePayload = {
|
||||
uuid: string
|
||||
content_type: ContentType
|
||||
created_at: Date
|
||||
updated_at: Date
|
||||
deleted?: boolean
|
||||
content?: ItemContent
|
||||
clientData?: Record<string, unknown>
|
||||
|
||||
/**
|
||||
* isMetadataUpdate implies that the extension should make reference of updated
|
||||
* metadata, but not update content values as they may be stale relative to what the
|
||||
* extension currently has.
|
||||
*/
|
||||
isMetadataUpdate: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Extensions allowed to batch stream AllowedBatchContentTypes
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,7 @@ export class SNPreferencesService
|
||||
})
|
||||
|
||||
this.removeSyncObserver = syncService.addEventObserver((event) => {
|
||||
if (event === SyncEvent.SyncCompletedWithAllItemsUploaded) {
|
||||
if (event === SyncEvent.SyncCompletedWithAllItemsUploaded || event === SyncEvent.LocalDataIncrementalLoad) {
|
||||
void this.reload()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,6 +2,8 @@ import { SyncOpStatus } from './SyncOpStatus'
|
||||
import { SyncOptions } from '@standardnotes/services'
|
||||
|
||||
export interface SyncClientInterface {
|
||||
setLaunchPriorityUuids(launchPriorityUuids: string[]): void
|
||||
|
||||
sync(options?: Partial<SyncOptions>): Promise<unknown>
|
||||
|
||||
isOutOfSync(): boolean
|
||||
|
||||
@@ -18,7 +18,7 @@ import { SNHistoryManager } from '../History/HistoryManager'
|
||||
import { SNLog } from '@Lib/Log'
|
||||
import { SNSessionManager } from '../Session/SessionManager'
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
import { SortPayloadsByRecentAndContentPriority } from '@Lib/Services/Sync/Utils'
|
||||
import { GetSortedPayloadsByPriority } from '@Lib/Services/Sync/Utils'
|
||||
import { SyncClientInterface } from './SyncClientInterface'
|
||||
import { SyncPromise } from './Types'
|
||||
import { SyncOpStatus } from '@Lib/Services/Sync/SyncOpStatus'
|
||||
@@ -56,6 +56,7 @@ import {
|
||||
PayloadEmitSource,
|
||||
getIncrementedDirtyIndex,
|
||||
getCurrentDirtyIndex,
|
||||
ItemContent,
|
||||
} from '@standardnotes/models'
|
||||
import {
|
||||
AbstractService,
|
||||
@@ -158,6 +159,14 @@ export class SNSyncService
|
||||
}
|
||||
}
|
||||
|
||||
private get launchPriorityUuids() {
|
||||
return this.storageService.getValue<string[]>(StorageKey.LaunchPriorityUuids) ?? []
|
||||
}
|
||||
|
||||
public setLaunchPriorityUuids(launchPriorityUuids: string[]) {
|
||||
this.storageService.setValue(StorageKey.LaunchPriorityUuids, launchPriorityUuids)
|
||||
}
|
||||
|
||||
public override deinit(): void {
|
||||
this.dealloced = true
|
||||
;(this.sessionManager as unknown) = undefined
|
||||
@@ -272,15 +281,15 @@ export class SNSyncService
|
||||
})
|
||||
.filter(isNotUndefined)
|
||||
|
||||
const payloads = SortPayloadsByRecentAndContentPriority(unsortedPayloads, this.localLoadPriorty)
|
||||
const { itemsKeyPayloads, contentTypePriorityPayloads, remainingPayloads } = GetSortedPayloadsByPriority(
|
||||
unsortedPayloads,
|
||||
this.localLoadPriorty,
|
||||
this.launchPriorityUuids,
|
||||
)
|
||||
|
||||
const itemsKeysPayloads = payloads.filter((payload) => {
|
||||
return payload.content_type === ContentType.ItemsKey
|
||||
})
|
||||
await this.processItemsKeysFirstDuringDatabaseLoad(itemsKeyPayloads)
|
||||
|
||||
subtractFromArray(payloads, itemsKeysPayloads)
|
||||
|
||||
await this.processItemsKeysFirstDuringDatabaseLoad(itemsKeysPayloads)
|
||||
await this.processPayloadBatch(contentTypePriorityPayloads)
|
||||
|
||||
/**
|
||||
* Map in batches to give interface a chance to update. Note that total decryption
|
||||
@@ -288,45 +297,55 @@ export class SNSyncService
|
||||
* batches will result in the same time spent. It's the emitting/painting/rendering
|
||||
* that requires batch size optimization.
|
||||
*/
|
||||
const payloadCount = payloads.length
|
||||
const payloadCount = remainingPayloads.length
|
||||
const batchSize = this.options.loadBatchSize
|
||||
const numBatches = Math.ceil(payloadCount / batchSize)
|
||||
|
||||
for (let batchIndex = 0; batchIndex < numBatches; batchIndex++) {
|
||||
const currentPosition = batchIndex * batchSize
|
||||
const batch = payloads.slice(currentPosition, currentPosition + batchSize)
|
||||
const encrypted: EncryptedPayloadInterface[] = []
|
||||
const nonencrypted: (DecryptedPayloadInterface | DeletedPayloadInterface)[] = []
|
||||
|
||||
for (const payload of batch) {
|
||||
if (isEncryptedPayload(payload)) {
|
||||
encrypted.push(payload)
|
||||
} else {
|
||||
nonencrypted.push(payload)
|
||||
}
|
||||
}
|
||||
|
||||
const split: KeyedDecryptionSplit = {
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: encrypted,
|
||||
},
|
||||
}
|
||||
|
||||
const results = await this.protocolService.decryptSplit(split)
|
||||
|
||||
await this.payloadManager.emitPayloads([...nonencrypted, ...results], PayloadEmitSource.LocalDatabaseLoaded)
|
||||
|
||||
void this.notifyEvent(SyncEvent.LocalDataIncrementalLoad)
|
||||
|
||||
this.opStatus.setDatabaseLoadStatus(currentPosition, payloadCount, false)
|
||||
|
||||
await sleep(1, false)
|
||||
const batch = remainingPayloads.slice(currentPosition, currentPosition + batchSize)
|
||||
await this.processPayloadBatch(batch, currentPosition, payloadCount)
|
||||
}
|
||||
|
||||
this.databaseLoaded = true
|
||||
this.opStatus.setDatabaseLoadStatus(0, 0, true)
|
||||
}
|
||||
|
||||
private async processPayloadBatch(
|
||||
batch: FullyFormedPayloadInterface<ItemContent>[],
|
||||
currentPosition?: number,
|
||||
payloadCount?: number,
|
||||
) {
|
||||
const encrypted: EncryptedPayloadInterface[] = []
|
||||
const nonencrypted: (DecryptedPayloadInterface | DeletedPayloadInterface)[] = []
|
||||
|
||||
for (const payload of batch) {
|
||||
if (isEncryptedPayload(payload)) {
|
||||
encrypted.push(payload)
|
||||
} else {
|
||||
nonencrypted.push(payload)
|
||||
}
|
||||
}
|
||||
|
||||
const split: KeyedDecryptionSplit = {
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: encrypted,
|
||||
},
|
||||
}
|
||||
|
||||
const results = await this.protocolService.decryptSplit(split)
|
||||
|
||||
await this.payloadManager.emitPayloads([...nonencrypted, ...results], PayloadEmitSource.LocalDatabaseLoaded)
|
||||
|
||||
void this.notifyEvent(SyncEvent.LocalDataIncrementalLoad)
|
||||
|
||||
if (currentPosition != undefined && payloadCount != undefined) {
|
||||
this.opStatus.setDatabaseLoadStatus(currentPosition, payloadCount, false)
|
||||
}
|
||||
|
||||
await sleep(1, false)
|
||||
}
|
||||
|
||||
private setLastSyncToken(token: string) {
|
||||
this.syncToken = token
|
||||
return this.storageService.setValue(StorageKey.LastSyncToken, token)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { FullyFormedPayloadInterface } from '@standardnotes/models'
|
||||
import { GetSortedPayloadsByPriority } from './Utils'
|
||||
|
||||
describe('GetSortedPayloadsByPriority', () => {
|
||||
let payloads: FullyFormedPayloadInterface[] = []
|
||||
const contentTypePriority = [ContentType.ItemsKey, ContentType.UserPrefs, ContentType.Component, ContentType.Theme]
|
||||
let launchPriorityUuids: string[] = []
|
||||
|
||||
it('should sort payloads based on content type priority', () => {
|
||||
payloads = [
|
||||
{
|
||||
content_type: ContentType.Theme,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.UserPrefs,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Component,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.ItemsKey,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Note,
|
||||
} as FullyFormedPayloadInterface,
|
||||
]
|
||||
|
||||
const { itemsKeyPayloads, contentTypePriorityPayloads, remainingPayloads } = GetSortedPayloadsByPriority(
|
||||
payloads,
|
||||
contentTypePriority,
|
||||
launchPriorityUuids,
|
||||
)
|
||||
|
||||
expect(itemsKeyPayloads.length).toBe(1)
|
||||
expect(itemsKeyPayloads[0].content_type).toBe(ContentType.ItemsKey)
|
||||
|
||||
expect(contentTypePriorityPayloads.length).toBe(3)
|
||||
expect(contentTypePriorityPayloads[0].content_type).toBe(ContentType.UserPrefs)
|
||||
expect(contentTypePriorityPayloads[1].content_type).toBe(ContentType.Component)
|
||||
expect(contentTypePriorityPayloads[2].content_type).toBe(ContentType.Theme)
|
||||
|
||||
expect(remainingPayloads.length).toBe(1)
|
||||
expect(remainingPayloads[0].content_type).toBe(ContentType.Note)
|
||||
})
|
||||
|
||||
it('should sort payloads based on launch priority uuids', () => {
|
||||
const unprioritizedNoteUuid = 'unprioritized-note'
|
||||
const unprioritizedTagUuid = 'unprioritized-tag'
|
||||
|
||||
const prioritizedNoteUuid = 'prioritized-note'
|
||||
const prioritizedTagUuid = 'prioritized-tag'
|
||||
|
||||
payloads = [
|
||||
{
|
||||
content_type: ContentType.Theme,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.UserPrefs,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Component,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.ItemsKey,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Note,
|
||||
uuid: unprioritizedNoteUuid,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Tag,
|
||||
uuid: unprioritizedTagUuid,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Note,
|
||||
uuid: prioritizedNoteUuid,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Tag,
|
||||
uuid: prioritizedTagUuid,
|
||||
} as FullyFormedPayloadInterface,
|
||||
]
|
||||
|
||||
launchPriorityUuids = [prioritizedNoteUuid, prioritizedTagUuid]
|
||||
|
||||
const { itemsKeyPayloads, contentTypePriorityPayloads, remainingPayloads } = GetSortedPayloadsByPriority(
|
||||
payloads,
|
||||
contentTypePriority,
|
||||
launchPriorityUuids,
|
||||
)
|
||||
|
||||
expect(itemsKeyPayloads.length).toBe(1)
|
||||
expect(itemsKeyPayloads[0].content_type).toBe(ContentType.ItemsKey)
|
||||
|
||||
expect(contentTypePriorityPayloads.length).toBe(3)
|
||||
expect(contentTypePriorityPayloads[0].content_type).toBe(ContentType.UserPrefs)
|
||||
expect(contentTypePriorityPayloads[1].content_type).toBe(ContentType.Component)
|
||||
expect(contentTypePriorityPayloads[2].content_type).toBe(ContentType.Theme)
|
||||
|
||||
expect(remainingPayloads.length).toBe(4)
|
||||
expect(remainingPayloads[0].uuid).toBe(prioritizedNoteUuid)
|
||||
expect(remainingPayloads[1].uuid).toBe(prioritizedTagUuid)
|
||||
expect(remainingPayloads[2].uuid).toBe(unprioritizedNoteUuid)
|
||||
expect(remainingPayloads[3].uuid).toBe(unprioritizedTagUuid)
|
||||
})
|
||||
|
||||
it('should sort payloads based on server updated date if same content type', () => {
|
||||
const unprioritizedNoteUuid = 'unprioritized-note'
|
||||
const unprioritizedTagUuid = 'unprioritized-tag'
|
||||
|
||||
const prioritizedNoteUuid = 'prioritized-note'
|
||||
const prioritizedTagUuid = 'prioritized-tag'
|
||||
|
||||
payloads = [
|
||||
{
|
||||
content_type: ContentType.Note,
|
||||
uuid: unprioritizedNoteUuid,
|
||||
serverUpdatedAt: new Date(1),
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Tag,
|
||||
uuid: unprioritizedTagUuid,
|
||||
serverUpdatedAt: new Date(2),
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Note,
|
||||
uuid: prioritizedNoteUuid,
|
||||
} as FullyFormedPayloadInterface,
|
||||
{
|
||||
content_type: ContentType.Tag,
|
||||
uuid: prioritizedTagUuid,
|
||||
} as FullyFormedPayloadInterface,
|
||||
]
|
||||
|
||||
launchPriorityUuids = [prioritizedNoteUuid, prioritizedTagUuid]
|
||||
|
||||
const { remainingPayloads } = GetSortedPayloadsByPriority(payloads, contentTypePriority, launchPriorityUuids)
|
||||
|
||||
expect(remainingPayloads.length).toBe(4)
|
||||
expect(remainingPayloads[0].uuid).toBe(prioritizedNoteUuid)
|
||||
expect(remainingPayloads[1].uuid).toBe(prioritizedTagUuid)
|
||||
expect(remainingPayloads[2].uuid).toBe(unprioritizedTagUuid)
|
||||
expect(remainingPayloads[3].uuid).toBe(unprioritizedNoteUuid)
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UuidString } from '@Lib/Types'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { FullyFormedPayloadInterface } from '@standardnotes/models'
|
||||
|
||||
@@ -6,9 +7,9 @@ import { FullyFormedPayloadInterface } from '@standardnotes/models'
|
||||
* whereby the earlier a content_type appears in the priorityList,
|
||||
* the earlier it will appear in the resulting sorted array.
|
||||
*/
|
||||
export function SortPayloadsByRecentAndContentPriority(
|
||||
function SortPayloadsByRecentAndContentPriority(
|
||||
payloads: FullyFormedPayloadInterface[],
|
||||
priorityList: ContentType[],
|
||||
contentTypePriorityList: ContentType[],
|
||||
): FullyFormedPayloadInterface[] {
|
||||
return payloads.sort((a, b) => {
|
||||
const dateResult = new Date(b.serverUpdatedAt).getTime() - new Date(a.serverUpdatedAt).getTime()
|
||||
@@ -16,18 +17,15 @@ export function SortPayloadsByRecentAndContentPriority(
|
||||
let aPriority = 0
|
||||
let bPriority = 0
|
||||
|
||||
if (priorityList) {
|
||||
aPriority = priorityList.indexOf(a.content_type)
|
||||
bPriority = priorityList.indexOf(b.content_type)
|
||||
aPriority = contentTypePriorityList.indexOf(a.content_type)
|
||||
bPriority = contentTypePriorityList.indexOf(b.content_type)
|
||||
|
||||
if (aPriority === -1) {
|
||||
/** Not found in list, not prioritized. Set it to max value */
|
||||
aPriority = priorityList.length
|
||||
}
|
||||
if (bPriority === -1) {
|
||||
/** Not found in list, not prioritized. Set it to max value */
|
||||
bPriority = priorityList.length
|
||||
}
|
||||
if (aPriority === -1) {
|
||||
aPriority = contentTypePriorityList.length
|
||||
}
|
||||
|
||||
if (bPriority === -1) {
|
||||
bPriority = contentTypePriorityList.length
|
||||
}
|
||||
|
||||
if (aPriority === bPriority) {
|
||||
@@ -41,3 +39,76 @@ export function SortPayloadsByRecentAndContentPriority(
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts payloads according by most recently modified first, according to the priority,
|
||||
* whereby the earlier a uuid appears in the priorityList,
|
||||
* the earlier it will appear in the resulting sorted array.
|
||||
*/
|
||||
function SortPayloadsByRecentAndUuidPriority(
|
||||
payloads: FullyFormedPayloadInterface[],
|
||||
uuidPriorityList: UuidString[],
|
||||
): FullyFormedPayloadInterface[] {
|
||||
return payloads.sort((a, b) => {
|
||||
const dateResult = new Date(b.serverUpdatedAt).getTime() - new Date(a.serverUpdatedAt).getTime()
|
||||
|
||||
let aPriority = 0
|
||||
let bPriority = 0
|
||||
|
||||
aPriority = uuidPriorityList.indexOf(a.uuid)
|
||||
bPriority = uuidPriorityList.indexOf(b.uuid)
|
||||
|
||||
if (aPriority === -1) {
|
||||
aPriority = uuidPriorityList.length
|
||||
}
|
||||
|
||||
if (bPriority === -1) {
|
||||
bPriority = uuidPriorityList.length
|
||||
}
|
||||
|
||||
if (aPriority === bPriority) {
|
||||
return dateResult
|
||||
}
|
||||
|
||||
if (aPriority < bPriority) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function GetSortedPayloadsByPriority(
|
||||
payloads: FullyFormedPayloadInterface[],
|
||||
contentTypePriorityList: ContentType[],
|
||||
uuidPriorityList: UuidString[],
|
||||
): {
|
||||
itemsKeyPayloads: FullyFormedPayloadInterface[]
|
||||
contentTypePriorityPayloads: FullyFormedPayloadInterface[]
|
||||
remainingPayloads: FullyFormedPayloadInterface[]
|
||||
} {
|
||||
const itemsKeyPayloads: FullyFormedPayloadInterface[] = []
|
||||
const contentTypePriorityPayloads: FullyFormedPayloadInterface[] = []
|
||||
const remainingPayloads: FullyFormedPayloadInterface[] = []
|
||||
|
||||
for (let index = 0; index < payloads.length; index++) {
|
||||
const payload = payloads[index]
|
||||
|
||||
if (payload.content_type === ContentType.ItemsKey) {
|
||||
itemsKeyPayloads.push(payload)
|
||||
} else if (contentTypePriorityList.includes(payload.content_type)) {
|
||||
contentTypePriorityPayloads.push(payload)
|
||||
} else {
|
||||
remainingPayloads.push(payload)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
itemsKeyPayloads,
|
||||
contentTypePriorityPayloads: SortPayloadsByRecentAndContentPriority(
|
||||
contentTypePriorityPayloads,
|
||||
contentTypePriorityList,
|
||||
),
|
||||
remainingPayloads: SortPayloadsByRecentAndUuidPriority(remainingPayloads, uuidPriorityList),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,10 +672,10 @@ describe('online syncing', function () {
|
||||
const payload = Factory.createStorageItemPayload(contentTypes[Math.floor(i / 2)])
|
||||
originalPayloads.push(payload)
|
||||
}
|
||||
const sorted = SortPayloadsByRecentAndContentPriority(originalPayloads, ['C', 'A', 'B'])
|
||||
expect(sorted[0].content_type).to.equal('C')
|
||||
expect(sorted[2].content_type).to.equal('A')
|
||||
expect(sorted[4].content_type).to.equal('B')
|
||||
const { contentTypePriorityPayloads } = GetSortedPayloadsByPriority(originalPayloads, ['C', 'A', 'B'])
|
||||
expect(contentTypePriorityPayloads[0].content_type).to.equal('C')
|
||||
expect(contentTypePriorityPayloads[2].content_type).to.equal('A')
|
||||
expect(contentTypePriorityPayloads[4].content_type).to.equal('B')
|
||||
})
|
||||
|
||||
it('should sign in and retrieve large number of items', async function () {
|
||||
|
||||
+20
-20
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.146.0",
|
||||
"version": "2.147.3",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -13,7 +13,9 @@
|
||||
"e2e-server.js"
|
||||
],
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack -w --config webpack.dev.js",
|
||||
"start:test-server": "yarn node e2e-server.js",
|
||||
@@ -32,6 +34,20 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "*",
|
||||
"@babel/preset-env": "*",
|
||||
"@standardnotes/api": "workspace:*",
|
||||
"@standardnotes/common": "^1.43.0",
|
||||
"@standardnotes/domain-events": "^2.39.0",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/files": "workspace:*",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/security": "^1.2.0",
|
||||
"@standardnotes/services": "workspace:*",
|
||||
"@standardnotes/settings": "^1.17.0",
|
||||
"@standardnotes/sncrypto-common": "workspace:*",
|
||||
"@standardnotes/sncrypto-web": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/jest": "^28.1.5",
|
||||
"@types/jsdom": "^16.2.14",
|
||||
@@ -55,7 +71,9 @@
|
||||
"lodash": "^4.17.21",
|
||||
"nock": "^13.2.4",
|
||||
"otplib": "^12.0.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"script-loader": "^0.7.2",
|
||||
"semver": "^7.3.7",
|
||||
"ts-jest": "^28.0.5",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-node": "^10.8.1",
|
||||
@@ -65,23 +83,5 @@
|
||||
"webpack": "*",
|
||||
"webpack-cli": "*",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/api": "workspace:*",
|
||||
"@standardnotes/common": "^1.43.0",
|
||||
"@standardnotes/domain-events": "^2.39.0",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/files": "workspace:*",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/security": "^1.2.0",
|
||||
"@standardnotes/services": "workspace:*",
|
||||
"@standardnotes/settings": "^1.17.0",
|
||||
"@standardnotes/sncrypto-common": "workspace:*",
|
||||
"@standardnotes/sncrypto-web": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"semver": "^7.3.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.6.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/styles
|
||||
|
||||
## [1.6.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-01)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/styles",
|
||||
"version": "1.6.1",
|
||||
"private": true,
|
||||
"version": "1.6.2",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -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.13.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add sending user requests from UI ([#1927](https://github.com/standardnotes/app/issues/1927)) ([7ead0f6](https://github.com/standardnotes/app/commit/7ead0f655bb2572e5805dd26c297d520949d661a))
|
||||
|
||||
# [1.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
* prioritize loading latest selected items ([#1930](https://github.com/standardnotes/app/issues/1930)) ([a6ef658](https://github.com/standardnotes/app/commit/a6ef658daef9a01f5ddacc967fe30d22f580799a))
|
||||
|
||||
## [1.11.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.11.0",
|
||||
"version": "1.13.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { UserRequestType } from '@standardnotes/common'
|
||||
|
||||
export type UserRequestParams = {
|
||||
requestType: UserRequestType
|
||||
}
|
||||
@@ -3,4 +3,5 @@ export enum RootQueryParam {
|
||||
Settings = 'settings',
|
||||
DemoToken = 'demo-token',
|
||||
AcceptSubscriptionInvite = 'accept-subscription-invite',
|
||||
UserRequest = 'user-request',
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { UserRequestType } from '@standardnotes/common'
|
||||
|
||||
import { RouteParser } from './RouteParser'
|
||||
import { RouteType } from './RouteType'
|
||||
|
||||
@@ -56,4 +58,12 @@ describe('route parser', () => {
|
||||
expect(parser.type).toEqual(RouteType.AcceptSubscriptionInvite)
|
||||
expect(parser.subscriptionInviteParams.inviteUuid).toEqual('1-2-3')
|
||||
})
|
||||
|
||||
it('routes to user request', () => {
|
||||
const url = 'https://app.standardnotes.com/?user-request=exit-discount'
|
||||
const parser = new RouteParser(url)
|
||||
|
||||
expect(parser.type).toEqual(RouteType.UserRequest)
|
||||
expect(parser.userRequestParams.requestType).toEqual(UserRequestType.ExitDiscount)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { UserRequestType, Uuid } from '@standardnotes/common'
|
||||
import { PreferenceId } from './../Preferences/PreferenceId'
|
||||
import { DemoParams } from './Params/DemoParams'
|
||||
import { OnboardingParams } from './Params/OnboardingParams'
|
||||
import { PurchaseParams } from './Params/PurchaseParams'
|
||||
import { SettingsParams } from './Params/SettingsParams'
|
||||
import { SubscriptionInviteParams } from './Params/SubscriptionInviteParams'
|
||||
import { UserRequestParams } from './Params/UserRequestParams'
|
||||
|
||||
import { RootQueryParam } from './RootQueryParam'
|
||||
import { RootRoutes } from './RootRoutes'
|
||||
@@ -28,6 +29,14 @@ export class RouteParser implements RouteParserInterface {
|
||||
return this.parsedType
|
||||
}
|
||||
|
||||
get userRequestParams(): UserRequestParams {
|
||||
this.checkForProperRouteType(RouteType.UserRequest)
|
||||
|
||||
return {
|
||||
requestType: this.searchParams.get(RootQueryParam.UserRequest) as UserRequestType,
|
||||
}
|
||||
}
|
||||
|
||||
get subscriptionInviteParams(): SubscriptionInviteParams {
|
||||
this.checkForProperRouteType(RouteType.AcceptSubscriptionInvite)
|
||||
|
||||
@@ -89,6 +98,7 @@ export class RouteParser implements RouteParserInterface {
|
||||
[RootQueryParam.Settings, RouteType.Settings],
|
||||
[RootQueryParam.DemoToken, RouteType.Demo],
|
||||
[RootQueryParam.AcceptSubscriptionInvite, RouteType.AcceptSubscriptionInvite],
|
||||
[RootQueryParam.UserRequest, RouteType.UserRequest],
|
||||
])
|
||||
|
||||
for (const rootQueryParam of rootQueryParametersMap.keys()) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { OnboardingParams } from './Params/OnboardingParams'
|
||||
import { PurchaseParams } from './Params/PurchaseParams'
|
||||
import { SettingsParams } from './Params/SettingsParams'
|
||||
import { SubscriptionInviteParams } from './Params/SubscriptionInviteParams'
|
||||
import { UserRequestParams } from './Params/UserRequestParams'
|
||||
import { RouteType } from './RouteType'
|
||||
|
||||
export interface RouteParserInterface {
|
||||
@@ -11,5 +12,6 @@ export interface RouteParserInterface {
|
||||
get purchaseParams(): PurchaseParams
|
||||
get onboardingParams(): OnboardingParams
|
||||
get subscriptionInviteParams(): SubscriptionInviteParams
|
||||
get userRequestParams(): UserRequestParams
|
||||
get type(): RouteType
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ export enum RouteType {
|
||||
Settings = 'settings',
|
||||
Purchase = 'purchase',
|
||||
AcceptSubscriptionInvite = 'accept-subscription-invite',
|
||||
UserRequest = 'user-request',
|
||||
Demo = 'demo',
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
export enum PersistenceKey {
|
||||
SelectedItemsController = 'selected-items-controller',
|
||||
NavigationController = 'navigation-controller',
|
||||
}
|
||||
|
||||
export type SelectionControllerPersistableValue = {
|
||||
selectedUuids: string[]
|
||||
}
|
||||
|
||||
export type NavigationControllerPersistableValue = {
|
||||
selectedTagUuid: string
|
||||
}
|
||||
|
||||
export type PersistedStateValue = {
|
||||
[PersistenceKey.SelectedItemsController]: SelectionControllerPersistableValue
|
||||
[PersistenceKey.NavigationController]: NavigationControllerPersistableValue
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
import { PersistedStateValue } from '../StatePersistence/StatePersistence'
|
||||
|
||||
export enum StorageKey {
|
||||
AnonymousUserId = 'AnonymousUserId',
|
||||
ShowBetaWarning = 'ShowBetaWarning',
|
||||
ShowNoAccountWarning = 'ShowNoAccountWarning',
|
||||
FilesNavigationEnabled = 'FilesNavigationEnabled',
|
||||
MasterStatePersistenceKey = 'master-persistence-key',
|
||||
}
|
||||
|
||||
export type StorageValue = {
|
||||
@@ -10,6 +13,7 @@ export type StorageValue = {
|
||||
[StorageKey.ShowBetaWarning]: boolean
|
||||
[StorageKey.ShowNoAccountWarning]: boolean
|
||||
[StorageKey.FilesNavigationEnabled]: boolean
|
||||
[StorageKey.MasterStatePersistenceKey]: PersistedStateValue
|
||||
}
|
||||
|
||||
export const storage = {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { addToast, ToastType } from '@standardnotes/toast'
|
||||
import { addToast, dismissToast, ToastType } from '@standardnotes/toast'
|
||||
|
||||
import { ToastServiceInterface } from './ToastServiceInterface'
|
||||
|
||||
export class ToastService implements ToastServiceInterface {
|
||||
showToast(type: ToastType, message: string): void {
|
||||
addToast({
|
||||
hideToast(toastId: string): void {
|
||||
dismissToast(toastId)
|
||||
}
|
||||
|
||||
showToast(type: ToastType, message: string): string {
|
||||
return addToast({
|
||||
type: type,
|
||||
message,
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ToastType } from '@standardnotes/toast'
|
||||
|
||||
export interface ToastServiceInterface {
|
||||
showToast(type: ToastType, message: string): void
|
||||
showToast(type: ToastType, message: string): string
|
||||
hideToast(toastId: string): void
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export * from './Route/Params/OnboardingParams'
|
||||
export * from './Route/Params/PurchaseParams'
|
||||
export * from './Route/Params/SettingsParams'
|
||||
export * from './Route/Params/SubscriptionInviteParams'
|
||||
export * from './Route/Params/UserRequestParams'
|
||||
export * from './Route/RootQueryParam'
|
||||
export * from './Route/RouteParser'
|
||||
export * from './Route/RouteParserInterface'
|
||||
@@ -20,3 +21,4 @@ export * from './Storage/LocalStorage'
|
||||
export * from './Theme/ThemeManager'
|
||||
export * from './Toast/ToastService'
|
||||
export * from './Toast/ToastServiceInterface'
|
||||
export * from './StatePersistence/StatePersistence'
|
||||
|
||||
@@ -3,6 +3,66 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.93.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.93.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* padding on files view on mobile ([#1934](https://github.com/standardnotes/app/issues/1934)) ([633d618](https://github.com/standardnotes/app/commit/633d6189fbbd2a470a74f8bfd352e8b3e7af684c))
|
||||
|
||||
## [3.93.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* archived/trashed items getting unselected when changed ([#1932](https://github.com/standardnotes/app/issues/1932)) ([2dd0586](https://github.com/standardnotes/app/commit/2dd0586bb16cb29c7c2a538e82aca48f247910e6))
|
||||
|
||||
## [3.93.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* unable to add children to favorited tag; remove expand-on-click; render arrow before title ([#1931](https://github.com/standardnotes/app/issues/1931)) ([5bb4435](https://github.com/standardnotes/app/commit/5bb4435c5b10de6e0fcb580472e028f76cd2a5bd))
|
||||
|
||||
## [3.93.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* "No modifications allowed" error on file drag in Firefox ([de4adca](https://github.com/standardnotes/app/commit/de4adca059b169e75267c8a29012e9988a607bc2))
|
||||
* files list not updating when new file is uploaded ([4036113](https://github.com/standardnotes/app/commit/40361132063e70e9851baf01eb772a0b3ff0aa1c))
|
||||
|
||||
## [3.93.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correctly refresh cta banner ([305ffdf](https://github.com/standardnotes/app/commit/305ffdf9847cfe67e2d022f84c1e2443b4bc2c76))
|
||||
* purchase flow close on complete ([c4dbc5c](https://github.com/standardnotes/app/commit/c4dbc5cbd2147b695403e6f57254a97d42337965))
|
||||
|
||||
# [3.93.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add sending user requests from UI ([#1927](https://github.com/standardnotes/app/issues/1927)) ([7ead0f6](https://github.com/standardnotes/app/commit/7ead0f655bb2572e5805dd26c297d520949d661a))
|
||||
|
||||
# [3.92.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
* prioritize loading latest selected items ([#1930](https://github.com/standardnotes/app/issues/1930)) ([a6ef658](https://github.com/standardnotes/app/commit/a6ef658daef9a01f5ddacc967fe30d22f580799a))
|
||||
|
||||
## [3.91.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* hide subscription marketing on iOS ([#1929](https://github.com/standardnotes/app/issues/1929)) ([35b21b2](https://github.com/standardnotes/app/commit/35b21b21ce2438540d799b5a2f04fe5616bf2c3e))
|
||||
|
||||
# [3.91.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
* dedicated files layout ([#1928](https://github.com/standardnotes/app/issues/1928)) ([dd821c9](https://github.com/standardnotes/app/commit/dd821c95e61c8cd82317ad9fb3788daff7241942))
|
||||
|
||||
## [3.90.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/web",
|
||||
"version": "3.90.11",
|
||||
"version": "3.93.6",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/app.js",
|
||||
"author": "Standard Notes.",
|
||||
|
||||
@@ -203,6 +203,14 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
return undefined
|
||||
}
|
||||
|
||||
isNativeIOS() {
|
||||
return this.isNativeMobileWeb() && this.platform === Platform.Ios
|
||||
}
|
||||
|
||||
get hideSubscriptionMarketing() {
|
||||
return this.isNativeIOS()
|
||||
}
|
||||
|
||||
mobileDevice(): MobileDeviceInterface {
|
||||
if (!this.isNativeMobileWeb()) {
|
||||
throw Error('Attempting to access device as mobile device on non mobile platform')
|
||||
|
||||
@@ -12,7 +12,7 @@ import { NavigationController } from '@/Controllers/Navigation/NavigationControl
|
||||
import { NotesController } from '@/Controllers/NotesController'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
import { SNTag } from '@standardnotes/snjs'
|
||||
import { ContentType, SNTag } from '@standardnotes/snjs'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -95,35 +95,44 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
[hideTags, selectedTag, application],
|
||||
)
|
||||
|
||||
const hasNotes = items.some((item) => item.content_type === ContentType.Note)
|
||||
|
||||
return (
|
||||
<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 pointer-coarse:md:overflow-y-auto',
|
||||
'md:hover:[overflow-y:_overlay]',
|
||||
'flex flex-wrap pb-2 md:hover:[overflow-y:_overlay]',
|
||||
hasNotes ? 'justify-center' : 'justify-center md:justify-start md:pl-1',
|
||||
)}
|
||||
id={ElementIds.ContentList}
|
||||
onScroll={onScroll}
|
||||
onKeyDown={onKeyDown}
|
||||
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<ContentListItem
|
||||
key={item.uuid}
|
||||
application={application}
|
||||
item={item}
|
||||
selected={selectedUuids.has(item.uuid)}
|
||||
hideDate={hideDate}
|
||||
hidePreview={hideNotePreview}
|
||||
hideTags={hideTags}
|
||||
hideIcon={hideEditorIcon}
|
||||
sortBy={sortBy}
|
||||
filesController={filesController}
|
||||
onSelect={selectItem}
|
||||
tags={getTagsForItem(item)}
|
||||
notesController={notesController}
|
||||
/>
|
||||
))}
|
||||
{items.map((item, index) => {
|
||||
const previousItem = items[index - 1]
|
||||
const nextItem = items[index + 1]
|
||||
return (
|
||||
<ContentListItem
|
||||
key={item.uuid}
|
||||
application={application}
|
||||
item={item}
|
||||
selected={selectedUuids.has(item.uuid)}
|
||||
hideDate={hideDate}
|
||||
hidePreview={hideNotePreview}
|
||||
hideTags={hideTags}
|
||||
hideIcon={hideEditorIcon}
|
||||
sortBy={sortBy}
|
||||
filesController={filesController}
|
||||
onSelect={selectItem}
|
||||
tags={getTagsForItem(item)}
|
||||
notesController={notesController}
|
||||
isPreviousItemTiled={previousItem?.content_type === ContentType.File}
|
||||
isNextItemTiled={nextItem?.content_type === ContentType.File}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { ContentType } from '@standardnotes/snjs'
|
||||
import { ContentType, FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import React, { FunctionComponent } from 'react'
|
||||
import FileListItem from './FileListItem'
|
||||
import NoteListItem from './NoteListItem'
|
||||
import { AbstractListItemProps, doListItemPropsMeritRerender } from './Types/AbstractListItemProps'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
|
||||
const ContentListItem: FunctionComponent<AbstractListItemProps> = (props) => {
|
||||
const ContentListItem: FunctionComponent<AbstractListItemProps<ListableContentItem>> = (props) => {
|
||||
switch (props.item.content_type) {
|
||||
case ContentType.Note:
|
||||
return <NoteListItem {...props} />
|
||||
return <NoteListItem {...props} item={props.item as SNNote} />
|
||||
case ContentType.File:
|
||||
return <FileListItem {...props} />
|
||||
return <FileListItem {...props} item={props.item as FileItem} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { KeyboardKey, KeyboardModifier } from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PANEL_NAME_NOTES } from '@/Constants/Constants'
|
||||
import { FileItem, PrefKey, SystemViewId } from '@standardnotes/snjs'
|
||||
import { FileItem, PrefKey } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import ContentList from '@/Components/ContentListView/ContentList'
|
||||
@@ -110,6 +110,7 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
renderedItems,
|
||||
items,
|
||||
searchBarElement,
|
||||
isCurrentNoteTemplate,
|
||||
} = itemListController
|
||||
|
||||
const { selectedUuids, selectNextItem, selectPreviousItem } = selectionController
|
||||
@@ -118,10 +119,7 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
|
||||
const icon = selectedTag?.iconString
|
||||
|
||||
const isFilesSmartView = useMemo(
|
||||
() => navigationController.selected?.uuid === SystemViewId.Files,
|
||||
[navigationController.selected?.uuid],
|
||||
)
|
||||
const isFilesSmartView = useMemo(() => navigationController.isInFilesView, [navigationController.isInFilesView])
|
||||
|
||||
const addNewItem = useCallback(async () => {
|
||||
if (isFilesSmartView) {
|
||||
@@ -215,10 +213,14 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
|
||||
const panelResizeFinishCallback: ResizeFinishCallback = useCallback(
|
||||
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
||||
application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
||||
if (selectedAsTag) {
|
||||
navigationController.setPanelWidthForTag(selectedAsTag, width)
|
||||
} else {
|
||||
application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
||||
}
|
||||
application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, isCollapsed)
|
||||
},
|
||||
[application],
|
||||
[application, selectedAsTag, navigationController],
|
||||
)
|
||||
|
||||
const addButtonLabel = useMemo(
|
||||
@@ -243,15 +245,26 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
[selectionController],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const hasEditorPane = selectedUuids.size > 0 || renderedItems.length === 0 || isCurrentNoteTemplate
|
||||
if (!hasEditorPane) {
|
||||
itemsViewPanelRef.current?.style.removeProperty('width')
|
||||
}
|
||||
}, [selectedUuids, itemsViewPanelRef, isCurrentNoteTemplate, renderedItems])
|
||||
|
||||
const hasEditorPane = selectedUuids.size > 0 || renderedItems.length === 0 || isCurrentNoteTemplate
|
||||
|
||||
return (
|
||||
<div
|
||||
id="items-column"
|
||||
className={classNames(
|
||||
'sn-component section app-column flex h-full flex-col overflow-hidden pt-safe-top',
|
||||
'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',
|
||||
hasEditorPane ? 'xl:w-[24rem] xsm-only:!w-full sm-only:!w-full' : 'w-full md:min-w-[400px]',
|
||||
hasEditorPane
|
||||
? 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}
|
||||
@@ -327,7 +340,7 @@ const ContentListView: FunctionComponent<Props> = ({
|
||||
) : null}
|
||||
<div className="absolute bottom-0 h-safe-bottom w-full" />
|
||||
</ResponsivePaneContent>
|
||||
{itemsViewPanelRef.current && (
|
||||
{hasEditorPane && itemsViewPanelRef.current && (
|
||||
<PanelResizer
|
||||
collapsable={true}
|
||||
hoverable={true}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { formatDateAndTimeForNote } from '@/Utils/DateUtils'
|
||||
import { SNTag } from '@standardnotes/snjs'
|
||||
import { isNote, SNTag } from '@standardnotes/snjs'
|
||||
import { ComponentPropsWithoutRef, forwardRef, FunctionComponent, Ref } from 'react'
|
||||
import ListItemFlagIcons from '../ListItemFlagIcons'
|
||||
import ListItemMetadata from '../ListItemMetadata'
|
||||
@@ -65,7 +65,7 @@ export const DailyItemCell = forwardRef(
|
||||
{item && (
|
||||
<>
|
||||
<ListItemTitle item={item} />
|
||||
<ListItemNotePreviewText hidePreview={hidePreview} item={item} lineLimit={5} />
|
||||
{isNote(item) && <ListItemNotePreviewText hidePreview={hidePreview} item={item} lineLimit={5} />}
|
||||
<ListItemMetadata item={item} hideDate={hideDate} sortBy={'created_at'} />
|
||||
<ListItemTags hideTags={hideTags} tags={tags} />
|
||||
</>
|
||||
|
||||
@@ -3,15 +3,16 @@ import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useRef } from 'react'
|
||||
import { getFileIconComponent } from '../FilePreview/getFileIconComponent'
|
||||
import ListItemConflictIndicator from './ListItemConflictIndicator'
|
||||
import ListItemFlagIcons from './ListItemFlagIcons'
|
||||
import ListItemTags from './ListItemTags'
|
||||
import ListItemMetadata from './ListItemMetadata'
|
||||
import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
|
||||
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
|
||||
const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
const FileListItem: FunctionComponent<DisplayableListItemProps<FileItem>> = ({
|
||||
application,
|
||||
filesController,
|
||||
hideDate,
|
||||
@@ -67,7 +68,7 @@ const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
const IconComponent = () =>
|
||||
getFileIconComponent(
|
||||
application.iconsController.getIconForFileType((item as FileItem).mimeType),
|
||||
'w-5 h-5 flex-shrink-0',
|
||||
'w-10 h-10 flex-shrink-0',
|
||||
)
|
||||
|
||||
useContextMenuEvent(listItemRef, openContextMenu)
|
||||
@@ -75,28 +76,41 @@ const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
return (
|
||||
<div
|
||||
ref={listItemRef}
|
||||
className={`content-list-item flex w-full cursor-pointer items-stretch text-text ${
|
||||
selected && 'selected border-l-2px border-solid border-info'
|
||||
}`}
|
||||
className={classNames('flex max-h-[300px] w-[190px] cursor-pointer px-1 pt-2 text-text md:w-[200px]')}
|
||||
id={item.uuid}
|
||||
onClick={onClick}
|
||||
>
|
||||
{!hideIcon ? (
|
||||
<div className="mr-0 flex flex-col items-center justify-between p-4.5 pr-3">
|
||||
<IconComponent />
|
||||
<div
|
||||
className={`flex flex-col justify-between overflow-hidden rounded bg-passive-5 pt-5 transition-all hover:bg-passive-4 ${
|
||||
selected ? 'border-[1px] border-solid border-info' : 'border-[1px] border-solid border-border'
|
||||
}`}
|
||||
>
|
||||
<div className={'px-5'}>
|
||||
{!hideIcon ? (
|
||||
<div className="mr-0">
|
||||
<IconComponent />
|
||||
</div>
|
||||
) : (
|
||||
<div className="pr-4" />
|
||||
)}
|
||||
<div className="min-w-0 flex-grow py-4 px-0">
|
||||
<div className="line-clamp-2 overflow-hidden text-editor font-semibold">
|
||||
<div className="break-word line-clamp-2 mr-2 overflow-hidden">{item.title}</div>
|
||||
</div>
|
||||
<ListItemMetadata item={item} hideDate={hideDate} sortBy={sortBy} />
|
||||
<ListItemTags hideTags={hideTags} tags={tags} />
|
||||
<ListItemConflictIndicator item={item} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pr-4" />
|
||||
)}
|
||||
<div className="min-w-0 flex-grow border-b border-solid border-border py-4 px-0">
|
||||
<div className="flex items-start justify-between overflow-hidden text-base font-semibold leading-[1.3]">
|
||||
<div className="break-word mr-2">{item.title}</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'border-t-[1px] border-solid border-border p-3 text-xs font-bold',
|
||||
selected ? 'bg-info text-info-contrast' : 'bg-passive-4 text-neutral',
|
||||
)}
|
||||
>
|
||||
{formatSizeToReadableString(item.decryptedSize)}
|
||||
</div>
|
||||
<ListItemMetadata item={item} hideDate={hideDate} sortBy={sortBy} />
|
||||
<ListItemTags hideTags={hideTags} tags={tags} />
|
||||
<ListItemConflictIndicator item={item} />
|
||||
</div>
|
||||
<ListItemFlagIcons item={item} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
+12
-9
@@ -118,7 +118,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
void changePreferences({ sortBy: sort })
|
||||
}
|
||||
},
|
||||
[preferences, changePreferences, toggleSortReverse],
|
||||
[preferences.sortBy, toggleSortReverse, changePreferences],
|
||||
)
|
||||
|
||||
const toggleSortByDateModified = useCallback(() => {
|
||||
@@ -212,14 +212,17 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
{!DailyEntryModeEnabled &&
|
||||
'Create powerful workflows and organizational layouts with per-tag display preferences.'}
|
||||
</p>
|
||||
<Button
|
||||
primary
|
||||
small
|
||||
className="col-start-1 col-end-3 mt-3 justify-self-start uppercase"
|
||||
onClick={() => application.openPurchaseFlow()}
|
||||
>
|
||||
Upgrade Features
|
||||
</Button>
|
||||
|
||||
{!application.hideSubscriptionMarketing && (
|
||||
<Button
|
||||
primary
|
||||
small
|
||||
className="col-start-1 col-end-3 mt-3 justify-self-start uppercase"
|
||||
onClick={() => application.openPurchaseFlow()}
|
||||
>
|
||||
Upgrade Features
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
@@ -11,11 +11,13 @@ type Props = {
|
||||
starred: ListableContentItem['starred']
|
||||
}
|
||||
hasFiles?: boolean
|
||||
hasBorder?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ListItemFlagIcons: FunctionComponent<Props> = ({ item, hasFiles = false }) => {
|
||||
const ListItemFlagIcons: FunctionComponent<Props> = ({ item, hasFiles = false, hasBorder = true, className }) => {
|
||||
return (
|
||||
<div className="flex items-start border-b border-solid border-border p-4 pl-0">
|
||||
<div className={`flex items-start ${hasBorder && 'border-b border-solid border-border'} ${className} pl-0`}>
|
||||
{item.locked && (
|
||||
<span className="flex items-center" title="Editing Disabled">
|
||||
<Icon ariaLabel="Editing Disabled" type="pencil-off" className="text-info" size="medium" />
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { sanitizeHtmlString } from '@standardnotes/snjs'
|
||||
import { sanitizeHtmlString, SNNote } from '@standardnotes/snjs'
|
||||
import { FunctionComponent } from 'react'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
|
||||
type Props = {
|
||||
item: ListableContentItem
|
||||
item: SNNote
|
||||
hidePreview: boolean
|
||||
lineLimit?: number
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { FunctionComponent } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
|
||||
type Props = {
|
||||
hideTags: boolean
|
||||
tags: DisplayableListItemProps['tags']
|
||||
tags: DisplayableListItemProps<ListableContentItem>['tags']
|
||||
}
|
||||
|
||||
const ListItemTags: FunctionComponent<Props> = ({ hideTags, tags }) => {
|
||||
|
||||
@@ -14,8 +14,9 @@ import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
|
||||
import ListItemNotePreviewText from './ListItemNotePreviewText'
|
||||
import { ListItemTitle } from './ListItemTitle'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
|
||||
const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
|
||||
application,
|
||||
notesController,
|
||||
onSelect,
|
||||
@@ -27,6 +28,8 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
selected,
|
||||
sortBy,
|
||||
tags,
|
||||
isPreviousItemTiled,
|
||||
isNextItemTiled,
|
||||
}) => {
|
||||
const { toggleAppPane } = useResponsiveAppPane()
|
||||
|
||||
@@ -73,12 +76,17 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
|
||||
log(LoggingDomain.ItemsList, 'Rendering note list item', item.title)
|
||||
|
||||
const hasOffsetBorder = !isNextItemTiled
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={listItemRef}
|
||||
className={`content-list-item flex w-full cursor-pointer items-stretch text-text ${
|
||||
selected && `selected border-l-2 border-solid border-accessory-tint-${tint}`
|
||||
}`}
|
||||
className={classNames(
|
||||
'content-list-item text-tex flex w-full cursor-pointer items-stretch',
|
||||
selected && `selected border-l-2 border-solid border-accessory-tint-${tint}`,
|
||||
isPreviousItemTiled && 'mt-3 border-t border-solid border-t-border',
|
||||
isNextItemTiled && 'mb-3 border-b border-solid border-b-border',
|
||||
)}
|
||||
id={item.uuid}
|
||||
onClick={onClick}
|
||||
>
|
||||
@@ -89,14 +97,14 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
) : (
|
||||
<div className="pr-4" />
|
||||
)}
|
||||
<div className="min-w-0 flex-grow border-b border-solid border-border py-4 px-0">
|
||||
<div className={`min-w-0 flex-grow ${hasOffsetBorder && 'border-b border-solid border-border'} py-4 px-0`}>
|
||||
<ListItemTitle item={item} />
|
||||
<ListItemNotePreviewText item={item} hidePreview={hidePreview} />
|
||||
<ListItemMetadata item={item} hideDate={hideDate} sortBy={sortBy} />
|
||||
<ListItemTags hideTags={hideTags} tags={tags} />
|
||||
<ListItemConflictIndicator item={item} />
|
||||
</div>
|
||||
<ListItemFlagIcons item={item} hasFiles={hasFiles} />
|
||||
<ListItemFlagIcons className="p-4" item={item} hasFiles={hasFiles} hasBorder={hasOffsetBorder} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
+15
-6
@@ -4,7 +4,9 @@ import { NotesController } from '@/Controllers/NotesController'
|
||||
import { SortableItem, SNTag, Uuids } from '@standardnotes/snjs'
|
||||
import { ListableContentItem } from './ListableContentItem'
|
||||
|
||||
export type AbstractListItemProps = {
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never
|
||||
|
||||
export type AbstractListItemProps<I extends ListableContentItem> = {
|
||||
application: WebApplication
|
||||
filesController: FilesController
|
||||
notesController: NotesController
|
||||
@@ -13,14 +15,19 @@ export type AbstractListItemProps = {
|
||||
hideIcon: boolean
|
||||
hideTags: boolean
|
||||
hidePreview: boolean
|
||||
item: ListableContentItem
|
||||
item: I
|
||||
selected: boolean
|
||||
sortBy: keyof SortableItem | undefined
|
||||
tags: SNTag[]
|
||||
isPreviousItemTiled?: boolean
|
||||
isNextItemTiled?: boolean
|
||||
}
|
||||
|
||||
export function doListItemPropsMeritRerender(previous: AbstractListItemProps, next: AbstractListItemProps): boolean {
|
||||
const simpleComparison: (keyof AbstractListItemProps)[] = [
|
||||
export function doListItemPropsMeritRerender(
|
||||
previous: AbstractListItemProps<ListableContentItem>,
|
||||
next: AbstractListItemProps<ListableContentItem>,
|
||||
): boolean {
|
||||
const simpleComparison: (keyof AbstractListItemProps<ListableContentItem>)[] = [
|
||||
'onSelect',
|
||||
'hideDate',
|
||||
'hideIcon',
|
||||
@@ -28,6 +35,8 @@ export function doListItemPropsMeritRerender(previous: AbstractListItemProps, ne
|
||||
'hidePreview',
|
||||
'selected',
|
||||
'sortBy',
|
||||
'isPreviousItemTiled',
|
||||
'isNextItemTiled',
|
||||
]
|
||||
|
||||
for (const key of simpleComparison) {
|
||||
@@ -83,7 +92,7 @@ function doesItemChangeMeritRerender(previous: ListableContentItem, next: Listab
|
||||
return true
|
||||
}
|
||||
|
||||
const propertiesMeritingRerender: (keyof ListableContentItem)[] = [
|
||||
const propertiesMeritingRerender: KeysOfUnion<ListableContentItem>[] = [
|
||||
'title',
|
||||
'protected',
|
||||
'updatedAtString',
|
||||
@@ -97,7 +106,7 @@ function doesItemChangeMeritRerender(previous: ListableContentItem, next: Listab
|
||||
]
|
||||
|
||||
for (const key of propertiesMeritingRerender) {
|
||||
if (previous[key] !== next[key]) {
|
||||
if (previous[key as keyof ListableContentItem] !== next[key as keyof ListableContentItem]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
import { SNTag } from '@standardnotes/snjs'
|
||||
import { AbstractListItemProps } from './AbstractListItemProps'
|
||||
import { ListableContentItem } from './ListableContentItem'
|
||||
|
||||
export type DisplayableListItemProps = AbstractListItemProps & {
|
||||
export type DisplayableListItemProps<I extends ListableContentItem> = AbstractListItemProps<I> & {
|
||||
tags: {
|
||||
uuid: SNTag['uuid']
|
||||
title: SNTag['title']
|
||||
|
||||
+2
-11
@@ -1,12 +1,3 @@
|
||||
import { DecryptedItem, ItemContent } from '@standardnotes/snjs'
|
||||
import { FileItem, SNNote } from '@standardnotes/snjs'
|
||||
|
||||
export type ListableContentItem = DecryptedItem<ItemContent> & {
|
||||
title: string
|
||||
protected: boolean
|
||||
updatedAtString?: string
|
||||
createdAtString?: string
|
||||
hidePreview?: boolean
|
||||
preview_html?: string
|
||||
preview_plain?: string
|
||||
text?: string
|
||||
}
|
||||
export type ListableContentItem = SNNote | FileItem
|
||||
|
||||
+17
-2
@@ -100,6 +100,20 @@ const FileDragNDropProvider = ({ application, children, featuresController, file
|
||||
[application],
|
||||
)
|
||||
|
||||
const handleDragStart = useCallback(
|
||||
(event: DragEvent) => {
|
||||
if (isHandlingFileDrag(event, application)) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.clearData()
|
||||
}
|
||||
}
|
||||
},
|
||||
[application],
|
||||
)
|
||||
|
||||
const handleDragIn = useCallback(
|
||||
(event: DragEvent) => {
|
||||
if (!isHandlingFileDrag(event, application)) {
|
||||
@@ -200,7 +214,6 @@ const FileDragNDropProvider = ({ application, children, featuresController, file
|
||||
}
|
||||
})
|
||||
|
||||
event.dataTransfer.clearData()
|
||||
dragCounter.current = 0
|
||||
}
|
||||
},
|
||||
@@ -208,18 +221,20 @@ const FileDragNDropProvider = ({ application, children, featuresController, file
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('dragstart', handleDragStart)
|
||||
window.addEventListener('dragenter', handleDragIn)
|
||||
window.addEventListener('dragleave', handleDragOut)
|
||||
window.addEventListener('dragover', handleDrag)
|
||||
window.addEventListener('drop', handleDrop)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('dragstart', handleDragStart)
|
||||
window.removeEventListener('dragenter', handleDragIn)
|
||||
window.removeEventListener('dragleave', handleDragOut)
|
||||
window.removeEventListener('dragover', handleDrag)
|
||||
window.removeEventListener('drop', handleDrop)
|
||||
}
|
||||
}, [handleDragIn, handleDrop, handleDrag, handleDragOut])
|
||||
}, [handleDragIn, handleDrop, handleDrag, handleDragOut, handleDragStart])
|
||||
|
||||
const contextValue = useMemo(() => {
|
||||
return {
|
||||
|
||||
@@ -386,6 +386,7 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
<UpgradeNow
|
||||
application={this.application}
|
||||
featuresController={this.viewControllerManager.featuresController}
|
||||
subscriptionContoller={this.viewControllerManager.subscriptionController}
|
||||
/>
|
||||
{this.state.showBetaWarning && (
|
||||
<Fragment>
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { SubscriptionController } from '@/Controllers/Subscription/SubscriptionController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { loadPurchaseFlowUrl } from '../PurchaseFlow/PurchaseFlowFunctions'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
featuresController: FeaturesController
|
||||
subscriptionContoller: SubscriptionController
|
||||
}
|
||||
|
||||
const UpgradeNow = ({ application, featuresController }: Props) => {
|
||||
const UpgradeNow = ({ application, featuresController, subscriptionContoller }: Props) => {
|
||||
const shouldShowCTA = !featuresController.hasFolders
|
||||
const hasAccount = application.hasAccount()
|
||||
const hasAccount = subscriptionContoller.hasAccount
|
||||
|
||||
if (hasAccount && subscriptionContoller.hideSubscriptionMarketing) {
|
||||
return null
|
||||
}
|
||||
|
||||
return shouldShowCTA ? (
|
||||
<div className="flex h-full items-center px-2">
|
||||
|
||||
@@ -286,11 +286,13 @@ const NotesOptions = ({
|
||||
|
||||
const switchClassNames = classNames(textClassNames, defaultClassNames, 'justify-between')
|
||||
|
||||
const firstItemClass = 'pt-4'
|
||||
|
||||
return (
|
||||
<>
|
||||
{notes.length === 1 && (
|
||||
<>
|
||||
<button className={defaultClassNames} onClick={openRevisionHistoryModal}>
|
||||
<button className={classNames(defaultClassNames, firstItemClass)} onClick={openRevisionHistoryModal}>
|
||||
<Icon type="history" className={iconClass} />
|
||||
Note history
|
||||
</button>
|
||||
|
||||
+8
-6
@@ -31,12 +31,14 @@ const NoSubscription: FunctionComponent<Props> = ({ application }) => {
|
||||
<Text>You don't have a Standard Notes subscription yet.</Text>
|
||||
{isLoadingPurchaseFlow && <Text>Redirecting you to the subscription page...</Text>}
|
||||
{purchaseFlowError && <Text className="text-danger">{purchaseFlowError}</Text>}
|
||||
<div className="flex">
|
||||
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
|
||||
{application.hasAccount() && (
|
||||
<Button className="mt-3 min-w-20" primary label="Subscribe" onClick={onPurchaseClick} />
|
||||
)}
|
||||
</div>
|
||||
{!application.hideSubscriptionMarketing && (
|
||||
<div className="flex">
|
||||
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
|
||||
{application.hasAccount() && (
|
||||
<Button className="mt-3 min-w-20" primary label="Subscribe" onClick={onPurchaseClick} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+9
-6
@@ -34,12 +34,15 @@ const NoProSubscription: FunctionComponent<Props> = ({ application }) => {
|
||||
</Text>
|
||||
{isLoadingPurchaseFlow && <Text>Redirecting you to the subscription page...</Text>}
|
||||
{purchaseFlowError && <Text className="text-danger">{purchaseFlowError}</Text>}
|
||||
<div className="flex">
|
||||
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
|
||||
{application.hasAccount() && (
|
||||
<Button className="mt-3 min-w-20" primary label="Upgrade" onClick={onPurchaseClick} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!application.hideSubscriptionMarketing && (
|
||||
<div className="flex">
|
||||
<LinkButton className="mt-3 mr-3 min-w-20" label="Learn More" link={window.plansUrl as string} />
|
||||
{application.hasAccount() && (
|
||||
<Button className="mt-3 min-w-20" primary label="Upgrade" onClick={onPurchaseClick} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,12 @@ const Persistence = ({ application }: Props) => {
|
||||
const toggleStatePersistence = (shouldPersist: boolean) => {
|
||||
application.setValue(ShouldPersistNoteStateKey, shouldPersist)
|
||||
setShouldPersistNoteState(shouldPersist)
|
||||
|
||||
if (shouldPersist) {
|
||||
application.getViewControllerManager().persistValues()
|
||||
} else {
|
||||
application.getViewControllerManager().clearPersistedValues()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
+11
-9
@@ -61,15 +61,17 @@ const PremiumFeaturesModal: FunctionComponent<Props> = ({
|
||||
To take advantage of <span className="font-semibold">{featureName}</span> and other advanced features,
|
||||
upgrade your current plan.
|
||||
</AlertDialogDescription>
|
||||
<div className="p-4">
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className="no-border w-full cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125"
|
||||
ref={plansButtonRef}
|
||||
>
|
||||
Upgrade
|
||||
</button>
|
||||
</div>
|
||||
{!application.hideSubscriptionMarketing && (
|
||||
<div className="p-4">
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className="no-border w-full cursor-pointer rounded bg-info py-2 font-bold text-info-contrast hover:brightness-125 focus:brightness-125"
|
||||
ref={plansButtonRef}
|
||||
>
|
||||
Upgrade
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</AlertDialog>
|
||||
|
||||
@@ -91,10 +91,15 @@ const CreateAccount: FunctionComponent<Props> = ({ viewControllerManager, applic
|
||||
|
||||
try {
|
||||
await application.register(email, password)
|
||||
loadPurchaseFlowUrl(application).catch((err) => {
|
||||
console.error(err)
|
||||
application.alertService.alert(err).catch(console.error)
|
||||
})
|
||||
|
||||
viewControllerManager.purchaseFlowController.closePurchaseFlow()
|
||||
|
||||
if (!application.hideSubscriptionMarketing) {
|
||||
loadPurchaseFlowUrl(application).catch((err) => {
|
||||
console.error(err)
|
||||
application.alertService.alert(err).catch(console.error)
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
application.alertService.alert(err as string).catch(console.error)
|
||||
|
||||
@@ -74,10 +74,14 @@ const SignIn: FunctionComponent<Props> = ({ viewControllerManager, application }
|
||||
if (response.error || response.data?.error) {
|
||||
throw new Error(response.error?.message || response.data?.error?.message)
|
||||
} else {
|
||||
loadPurchaseFlowUrl(application).catch((err) => {
|
||||
console.error(err)
|
||||
application.alertService.alert(err).catch(console.error)
|
||||
})
|
||||
viewControllerManager.purchaseFlowController.closePurchaseFlow()
|
||||
|
||||
if (!application.hideSubscriptionMarketing) {
|
||||
loadPurchaseFlowUrl(application).catch((err) => {
|
||||
console.error(err)
|
||||
application.alertService.alert(err).catch(console.error)
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
|
||||
@@ -2,6 +2,7 @@ import { WebApplication } from '@/Application/Application'
|
||||
import { FunctionComponent, MouseEventHandler, useCallback } from 'react'
|
||||
import Switch from '@/Components/Switch/Switch'
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -29,7 +30,11 @@ const FocusModeSwitch: FunctionComponent<Props> = ({ application, onToggle, onCl
|
||||
|
||||
return (
|
||||
<button
|
||||
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"
|
||||
className={classNames(
|
||||
'group flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left',
|
||||
'text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none disabled:bg-default disabled:text-passive-2',
|
||||
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
|
||||
)}
|
||||
onClick={toggle}
|
||||
>
|
||||
<div className="flex items-center">Focused Writing</div>
|
||||
|
||||
@@ -53,7 +53,7 @@ const PanelSettingsSection = ({ application }: Props) => {
|
||||
}, [application])
|
||||
|
||||
return (
|
||||
<div className="hidden text-sm md:block pointer-coarse:md-only:hidden pointer-coarse:lg-only:hidden">
|
||||
<div className="hidden md:block pointer-coarse:md-only:hidden pointer-coarse:lg-only:hidden">
|
||||
<MenuItem
|
||||
type={MenuItemType.SwitchButton}
|
||||
className="py-1 hover:bg-contrast focus:bg-info-backdrop"
|
||||
|
||||
@@ -21,6 +21,7 @@ import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { QuickSettingsController } from '@/Controllers/QuickSettingsController'
|
||||
import PanelSettingsSection from './PanelSettingsSection'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
|
||||
const focusModeAnimationDuration = 1255
|
||||
|
||||
@@ -173,7 +174,11 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = ({ application, quickSet
|
||||
<div className="my-1 px-3 text-sm font-semibold uppercase text-text">Tools</div>
|
||||
{toggleableComponents.map((component) => (
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-sm"
|
||||
className={classNames(
|
||||
'flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left',
|
||||
'text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
|
||||
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
|
||||
)}
|
||||
onClick={() => {
|
||||
toggleComponent(component)
|
||||
}}
|
||||
@@ -191,7 +196,11 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = ({ application, quickSet
|
||||
)}
|
||||
<div className="my-1 px-3 text-sm font-semibold uppercase text-text">Appearance</div>
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-sm"
|
||||
className={classNames(
|
||||
'flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-1.5 text-left',
|
||||
'text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none',
|
||||
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
|
||||
)}
|
||||
onClick={toggleDefaultTheme}
|
||||
ref={defaultThemeButtonRef}
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ThemeItem } from './ThemeItem'
|
||||
import RadioIndicator from '../Radio/RadioIndicator'
|
||||
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
|
||||
type Props = {
|
||||
item: ThemeItem
|
||||
@@ -54,9 +55,11 @@ const ThemesMenuButton: FunctionComponent<Props> = ({ application, item }) => {
|
||||
|
||||
return (
|
||||
<button
|
||||
className={
|
||||
'group flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none disabled:bg-default disabled:text-passive-2 md:text-sm'
|
||||
}
|
||||
className={classNames(
|
||||
'group flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-3 py-1.5',
|
||||
'text-left text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none disabled:bg-default disabled:text-passive-2',
|
||||
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
|
||||
)}
|
||||
onClick={toggleTheme}
|
||||
>
|
||||
{item.component?.isLayerable() ? (
|
||||
|
||||
@@ -72,11 +72,11 @@ const ResponsivePaneProvider = ({ paneController, children }: ProviderProps) =>
|
||||
useEffect(() => {
|
||||
if (previousSelectedPane) {
|
||||
const previousPaneElement = document.getElementById(ElementIds[previousSelectedPane])
|
||||
previousPaneElement?.classList.remove('selected')
|
||||
previousPaneElement?.removeAttribute('data-selected-pane')
|
||||
}
|
||||
|
||||
const currentPaneElement = document.getElementById(ElementIds[currentSelectedPane])
|
||||
currentPaneElement?.classList.add('selected')
|
||||
currentPaneElement?.setAttribute('data-selected-pane', '')
|
||||
}, [currentSelectedPane, previousSelectedPane])
|
||||
|
||||
const addAndroidBackHandler = useAndroidBackHandler()
|
||||
|
||||
@@ -69,7 +69,11 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
|
||||
label="Go to items list"
|
||||
icon="chevron-left"
|
||||
/>
|
||||
<UpgradeNow application={application} featuresController={viewControllerManager.featuresController} />
|
||||
<UpgradeNow
|
||||
application={application}
|
||||
subscriptionContoller={viewControllerManager.subscriptionController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<RoundIconButton
|
||||
className="ml-2.5 bg-default"
|
||||
onClick={() => {
|
||||
|
||||
@@ -50,7 +50,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
|
||||
}, [setTitle, view])
|
||||
|
||||
const selectCurrentTag = useCallback(async () => {
|
||||
await tagsState.setSelectedTag(view, {
|
||||
await tagsState.setSelectedTag(view, 'views', {
|
||||
userTriggered: true,
|
||||
})
|
||||
toggleAppPane(AppPaneId.Items)
|
||||
@@ -86,7 +86,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
|
||||
}, [inputRef, isEditing])
|
||||
|
||||
const onClickRename = useCallback(() => {
|
||||
tagsState.editingTag = view
|
||||
tagsState.setEditingTag(view)
|
||||
}, [tagsState, view])
|
||||
|
||||
const onClickSave = useCallback(() => {
|
||||
|
||||
@@ -40,8 +40,7 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
|
||||
|
||||
const onClickRename = useCallback(() => {
|
||||
navigationController.setContextMenuOpen(false)
|
||||
navigationController.editingFrom = navigationController.contextMenuOpenFrom
|
||||
navigationController.editingTag = selectedTag
|
||||
navigationController.setEditingTag(selectedTag)
|
||||
}, [navigationController, selectedTag])
|
||||
|
||||
const onClickDelete = useCallback(() => {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type TagListSectionType = 'all' | 'favorites'
|
||||
export type TagListSectionType = 'views' | 'all' | 'favorites'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user