Compare commits

...
Author SHA1 Message Date
StandardNotes CI 26fe530deb chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
2022-08-23 12:11:25 +00:00
StandardNotes CI 58f9789142 chore(release): components 2022-08-23 12:11:11 +00:00
Karol Sójko d2cebf930a chore(setup): fix running unit tests in all packages 2022-08-23 13:40:21 +02:00
Karol Sójko 7bff3dee24 fix(services): counting trashed and archived items 2022-08-23 13:20:34 +02:00
StandardNotes CI a2b6ec74b0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-08-23 10:05:48 +00:00
Karol Sójko 2bdd931f6d fix(web): archived and deleted counts on encryption panel (#1423)
* fix(web): archived and deleted counts on encryption panel

* fix(snjs): yarn build snjs before e2e test suite docker builds
2022-08-23 11:38:43 +02:00
StandardNotes CI a780fa4e87 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
2022-08-22 10:04:58 +00:00
StandardNotes CI becbe34233 chore(release): components 2022-08-22 10:04:37 +00:00
Vardan Hakobyan 2a976a0de4 fix: trigger the onChange event of qr code uploading input when clicking on the button (#1421) 2022-08-22 13:22:22 +04:00
StandardNotes CI e7acbd7de8 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-08-18 07:28:47 +00:00
Aman Harwara 779596b9ae fix: hidden file input being clickable (#1414) 2022-08-18 12:30:30 +05:30
StandardNotes CI b1f23d6076 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-08-17 15:16:44 +00:00
Aman Harwara 818c3066cc fix: context menu on longpress on ios safari (#1405) 2022-08-17 20:20:18 +05:30
StandardNotes CI 13343c9a56 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-08-17 14:09:08 +00:00
Aman Harwara 3d1dc8f38a feat: move search bar back to notes column (#1408) 2022-08-17 19:12:40 +05:30
StandardNotes CI 163cc8ce8a chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
2022-08-17 12:06:07 +00:00
Vardan Hakobyan a25923f084 fix: remove tablet specific responsive UI (#1411)
* fix: revert tablet responsiveness UI

* fix: don't let the "Saving..." text container hide the note title on tablets

* chore: better visibility of css classes list

* fix: fixed width of notes container column on Tablets
2022-08-17 15:39:56 +04:00
79 changed files with 823 additions and 476 deletions
+2
View File
@@ -26,6 +26,8 @@ jobs:
uses: actions/checkout@v3
- name: Install dependencies
run: yarn install --immutable
- name: Build
run: yarn build:snjs
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/api
## [1.4.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/api
## [1.4.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-05)
**Note:** Version bump only for package @standardnotes/api
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.4.7",
"version": "1.4.9",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -22,7 +22,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage"
},
"devDependencies": {
"@types/jest": "^28.1.5",
+10
View File
@@ -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.
## [2.7.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/components-meta
## [2.7.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
### Bug Fixes
* trigger the `onChange` event of qr code uploading input when clicking on the button ([#1421](https://github.com/standardnotes/app/issues/1421)) ([2a976a0](https://github.com/standardnotes/app/commit/2a976a0de499ba33186a52a6bd2c10a709b86fee))
## [2.7.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-14)
### Bug Fixes
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -80,9 +80,9 @@
"binary": "7c6f499b8107f80ec5fd84e591c2c33291d1fc6db85f098df4f8308a0cda901d"
},
"org.standardnotes.token-vault": {
"version": "2.2.3",
"base64": "bf5c3a2aae3f119043ee744ab5535de4705eeed67d5ecf9e1d7b95817f92bc19",
"binary": "8ef8a1eaf19d22b0ac279ad0c4d27b29c057b0f9b0d5c5f4cd6183eb506feaa3"
"version": "2.2.4",
"base64": "fb004e8cae73b351fdb225ef910102a1b7c9b616b92de52f99d2c82d7b4dd5a1",
"binary": "9b00f950c868e20e059a5fe7dbd0dd14eac5d94b31817b8bf3c58c88ab5ce236"
},
"org.standardnotes.standard-sheets": {
"version": "1.5.1",
@@ -90,9 +90,9 @@
"binary": "54a01414a21e46c67fa59c69189a3d34a6548de7539699f2cf0248f9c5059d37"
},
"org.standardnotes.advanced-checklist": {
"version": "0.2.3",
"base64": "2a2ce988902c9fdb7fe507c826bff13592adfe4fbe1c36e8468155730f507573",
"binary": "ae614e38946c480256157554d9af542deb0c9741630ad3242becce391991b994"
"version": "0.2.4",
"base64": "eaa8225677b18448cf6e8191cbf8a23c012590054d60434730cad20fd5b056f0",
"binary": "22db0aba7937234f8e0617a508a46de6a517c3e34be7deee87bc80533065f36c"
},
"org.standardnotes.file-safe": {
"version": "2.0.16",
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/components-meta",
"version": "2.7.7",
"version": "2.7.9",
"private": true,
"author": "Standard Notes.",
"main": "dist",
@@ -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.
## [0.2.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/advanced-checklist
## [0.2.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
### Bug Fixes
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/advanced-checklist",
"version": "0.2.3",
"version": "0.2.4",
"description": "A task editor with grouping functionality.",
"author": "Standard Notes.",
"keywords": [
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
### Bug Fixes
* trigger the `onChange` event of qr code uploading input when clicking on the button ([#1421](https://github.com/standardnotes/app/issues/1421)) ([2a976a0](https://github.com/standardnotes/app/commit/2a976a0de499ba33186a52a6bd2c10a709b86fee))
## [2.2.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
**Note:** Version bump only for package @standardnotes/authenticator
@@ -28,6 +28,12 @@ const convertToGrayScale = (imageData) => {
}
export default class QRCodeReader extends React.Component {
constructor() {
super()
this.fileInputRef = React.createRef(null)
}
onImageSelected = (evt) => {
const file = evt.target.files[0]
const url = URL.createObjectURL(file)
@@ -70,9 +76,9 @@ export default class QRCodeReader extends React.Component {
render() {
return (
<div className="qr-code-reader-container">
<div className="sk-button info">
<div className="sk-button info" onClick={() => this.fileInputRef.current.click()}>
<div className="sk-label">Upload QR Code</div>
<input type="file" style={{ display: 'none' }} onChange={this.onImageSelected} />
<input type="file" style={{ display: 'none' }} ref={this.fileInputRef} onChange={this.onImageSelected} />
</div>
</div>
)
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/authenticator",
"version": "2.2.3",
"version": "2.2.4",
"main": "dist/dist.js",
"author": "Standard Notes",
"private": true,
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.23.101](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.100](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.99](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.98](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-18)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.97](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.96](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.95](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.94](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-16)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.23.94",
"version": "3.23.101",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.14.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/encryption
## [1.14.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/encryption
# [1.14.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-05)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.14.0",
"version": "1.14.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.50.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/features
## [1.50.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
**Note:** Version bump only for package @standardnotes/features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/features",
"version": "1.50.4",
"version": "1.50.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -22,7 +22,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest"
"test": "jest"
},
"dependencies": {
"@standardnotes/auth": "^3.19.4",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.22.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.22.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/filepicker
# [1.22.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-05)
### Bug Fixes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.22.0",
"version": "1.22.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -20,7 +20,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest"
"test": "jest"
},
"devDependencies": {
"@types/jest": "^28.1.5",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.9.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/files
## [1.9.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/files
# [1.9.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-05)
### Bug Fixes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.9.0",
"version": "1.9.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -20,7 +20,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest"
"test": "jest"
},
"devDependencies": {
"@types/jest": "^28.1.5",
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.31.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/mobile
## [3.31.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/mobile
## [3.31.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
**Note:** Version bump only for package @standardnotes/mobile
## [3.31.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-18)
**Note:** Version bump only for package @standardnotes/mobile
## [3.31.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.31.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.31.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/mobile
## [3.31.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-16)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.31.10",
"version": "3.31.17",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+10
View File
@@ -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.16.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/models
## [1.16.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
### Bug Fixes
* **web:** archived and deleted counts on encryption panel ([#1423](https://github.com/standardnotes/app/issues/1423)) ([2bdd931](https://github.com/standardnotes/app/commit/2bdd931f6dcd876b1659c5d7d045effa26e18a20))
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-05)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.16.0",
"version": "1.16.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -20,7 +20,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest"
"test": "jest"
},
"devDependencies": {
"@types/jest": "^28.1.5",
@@ -1,4 +1,4 @@
export type StructuredItemsCount = {
export type ItemCounts = {
notes: number
tags: number
deleted: number
@@ -24,6 +24,7 @@ export * from './Mutator/TransactionalMutation'
export * from './Types/AppDataField'
export * from './Types/ConflictStrategy'
export * from './Types/DefaultAppDomain'
export * from './Types/ItemCounts'
export * from './Types/ItemStream'
export * from './Types/MutationType'
export * from './Types/SingletonStrategy'
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-16)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.3.16",
"version": "1.3.23",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.9.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/responses
## [1.9.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
### Bug Fixes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.9.3",
"version": "1.9.4",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -21,7 +21,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage --passWithNoTests"
"test": "jest spec --coverage --passWithNoTests"
},
"devDependencies": {
"@types/jest": "^28.1.5",
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.17.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
### Bug Fixes
* **services:** counting trashed and archived items ([7bff3de](https://github.com/standardnotes/app/commit/7bff3dee24d058be91cb278b09692b4ff4396106))
## [1.17.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
### Bug Fixes
* **web:** archived and deleted counts on encryption panel ([#1423](https://github.com/standardnotes/app/issues/1423)) ([2bdd931](https://github.com/standardnotes/app/commit/2bdd931f6dcd876b1659c5d7d045effa26e18a20))
# [1.17.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-05)
### Features
+8
View File
@@ -7,5 +7,13 @@ module.exports = {
'ts-jest': {
tsconfig: 'tsconfig.json',
},
},
coverageThreshold: {
global: {
branches: 9,
functions: 9,
lines: 16,
statements: 16
}
}
};
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.17.0",
"version": "1.17.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -20,7 +20,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage"
},
"dependencies": {
"@standardnotes/auth": "^3.19.4",
@@ -1,16 +1,16 @@
import {
InternalEventBusInterface,
SyncServiceInterface,
ItemManagerInterface,
AlertService,
ApiServiceInterface,
ChallengeServiceInterface,
} from '@standardnotes/services'
import { FileService } from './FileService'
import { PureCryptoInterface, StreamEncryptor } from '@standardnotes/sncrypto-common'
import { FileItem } from '@standardnotes/models'
import { EncryptionProvider } from '@standardnotes/encryption'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { ChallengeServiceInterface } from '../Challenge'
import { InternalEventBusInterface } from '..'
import { AlertService } from '../Alert/AlertService'
import { ApiServiceInterface } from '../Api/ApiServiceInterface'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { FileService } from './FileService'
describe('fileService', () => {
let apiService: ApiServiceInterface
let itemManager: ItemManagerInterface
@@ -0,0 +1,35 @@
import { ContentType } from '@standardnotes/common'
import { SNNote, SNTag } from '@standardnotes/models'
import { ItemCounter } from './ItemCounter'
describe('ItemCounter', () => {
const createCounter = () => new ItemCounter()
it('should count distinct item counts', () => {
const items = [
{
archived: true,
} as jest.Mocked<SNNote>,
{
trashed: true,
} as jest.Mocked<SNNote>,
{
archived: true,
trashed: true,
} as jest.Mocked<SNNote>,
{
content_type: ContentType.Note,
} as jest.Mocked<SNNote>,
{
content_type: ContentType.Tag,
} as jest.Mocked<SNTag>,
]
expect(createCounter().countNotesAndTags(items)).toEqual({
archived: 1,
deleted: 2,
notes: 1,
tags: 1,
})
})
})
@@ -0,0 +1,40 @@
import { ContentType } from '@standardnotes/common'
import { SNNote, SNTag, ItemCounts } from '@standardnotes/models'
import { ItemCounterInterface } from './ItemCounterInterface'
export class ItemCounter implements ItemCounterInterface {
countNotesAndTags(items: Array<SNNote | SNTag>): ItemCounts {
const counts: ItemCounts = {
notes: 0,
archived: 0,
deleted: 0,
tags: 0,
}
for (const item of items) {
if (item.trashed) {
counts.deleted++
continue
}
if (item.archived) {
counts.archived++
continue
}
if (item.content_type === ContentType.Note) {
counts.notes++
continue
}
if (item.content_type === ContentType.Tag) {
counts.tags++
continue
}
}
return counts
}
}
@@ -0,0 +1,5 @@
import { SNNote, SNTag, ItemCounts } from '@standardnotes/models'
export interface ItemCounterInterface {
countNotesAndTags(items: Array<SNNote | SNTag>): ItemCounts
}
+2
View File
@@ -50,6 +50,8 @@ export * from './Internal/InternalEventHandlerInterface'
export * from './Internal/InternalEventInterface'
export * from './Internal/InternalEventPublishStrategy'
export * from './Internal/InternalEventType'
export * from './Item/ItemCounter'
export * from './Item/ItemCounterInterface'
export * from './Item/ItemManagerInterface'
export * from './Item/ItemsClientInterface'
export * from './Item/ItemsServerInterface'
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.11.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/sncrypto-common
# 1.11.0 (2022-07-06)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/sncrypto-common",
"version": "1.11.0",
"version": "1.11.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -21,7 +21,7 @@
"build": "tsc -p tsconfig.json",
"watch": "tsc -p tsconfig.json --watch",
"lint": "eslint . --ext .ts",
"test:unit": "yarn lint"
"test": "yarn lint"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.30.0",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/sncrypto-web
## [1.12.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-06)
**Note:** Version bump only for package @standardnotes/sncrypto-web
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/sncrypto-web",
"version": "1.12.2",
"version": "1.12.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.125.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/snjs
## [2.125.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/snjs
# [2.125.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-05)
### Bug Fixes
+1 -1
View File
@@ -58,5 +58,5 @@ yarn start:server:e2e
From the root of the repository, run:
```
yarn run test:unit
yarn run test
```
+1 -1
View File
@@ -31,7 +31,7 @@ module.exports = {
branches: 13,
functions: 22,
lines: 27,
statements: 28,
statements: 27,
},
},
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.125.0",
"version": "2.125.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -25,7 +25,7 @@
"lint": "yarn lint:tsc && yarn lint:eslint",
"lint:eslint": "eslint --ext .ts lib/",
"lint:tsc": "tsc --noEmit --emitDeclarationOnly false --project lib/tsconfig.json",
"test:unit": "jest spec --coverage",
"test": "jest spec --coverage",
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand"
},
"devDependencies": {
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.1.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.1.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-11)
**Note:** Version bump only for package @standardnotes/ui-services
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.1.1",
"version": "1.1.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -20,7 +20,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage --passWithNoTests"
},
"dependencies": {
"@standardnotes/common": "^1.30.0",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.8.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/utils
## [1.8.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
### Bug Fixes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/utils",
"version": "1.8.2",
"version": "1.8.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -22,7 +22,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec"
"test": "jest spec"
},
"dependencies": {
"@standardnotes/common": "^1.23.1",
+38
View File
@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.45.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
**Note:** Version bump only for package @standardnotes/web
## [3.45.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-23)
### Bug Fixes
* **web:** archived and deleted counts on encryption panel ([#1423](https://github.com/standardnotes/app/issues/1423)) ([2bdd931](https://github.com/standardnotes/app/commit/2bdd931f6dcd876b1659c5d7d045effa26e18a20))
## [3.45.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
**Note:** Version bump only for package @standardnotes/web
## [3.45.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-18)
### Bug Fixes
* hidden file input being clickable ([#1414](https://github.com/standardnotes/app/issues/1414)) ([779596b](https://github.com/standardnotes/app/commit/779596b9ae2df7d70dac9cdd17c85647d85e16b2))
## [3.45.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
### Bug Fixes
* context menu on longpress on ios safari ([#1405](https://github.com/standardnotes/app/issues/1405)) ([818c306](https://github.com/standardnotes/app/commit/818c3066ccd92156dfc8c797074672cfb08de7bd))
# [3.45.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
### Features
* move search bar back to notes column ([#1408](https://github.com/standardnotes/app/issues/1408)) ([3d1dc8f](https://github.com/standardnotes/app/commit/3d1dc8f38a883685b6c107bece2e75afe8119bf5))
## [3.44.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-17)
### Bug Fixes
* remove tablet specific responsive UI ([#1411](https://github.com/standardnotes/app/issues/1411)) ([a25923f](https://github.com/standardnotes/app/commit/a25923f0843bfa18e21883a040f95636f23e45ab))
## [3.44.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-16)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.44.6",
"version": "3.45.5",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -194,6 +194,7 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
noteTagsController={viewControllerManager.noteTagsController}
notesController={viewControllerManager.notesController}
selectionController={viewControllerManager.selectionController}
searchOptionsController={viewControllerManager.searchOptionsController}
/>
<NoteGroupView application={application} />
</ResponsivePaneProvider>
@@ -175,7 +175,7 @@ const AttachedFilesPopover: FunctionComponent<Props> = ({
</div>
<input
type="file"
className="absolute top-0 left-0 opacity-0"
className="absolute top-0 left-0 -z-50 h-px w-px opacity-0"
multiple
ref={fileInputRef}
onChange={async (event) => {
@@ -21,6 +21,9 @@ import ResponsivePaneContent from '../ResponsivePane/ResponsivePaneContent'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { StreamingFileReader } from '@standardnotes/filepicker'
import SearchBar from '../SearchBar/SearchBar'
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
import { classNames } from '@/Utils/ConcatenateClassNames'
type Props = {
accountMenuController: AccountMenuController
@@ -32,6 +35,7 @@ type Props = {
noteTagsController: NoteTagsController
notesController: NotesController
selectionController: SelectedItemsController
searchOptionsController: SearchOptionsController
}
const ContentListView: FunctionComponent<Props> = ({
@@ -44,6 +48,7 @@ const ContentListView: FunctionComponent<Props> = ({
noteTagsController,
notesController,
selectionController,
searchOptionsController,
}) => {
const { toggleAppPane } = useResponsiveAppPane()
@@ -179,7 +184,10 @@ const ContentListView: FunctionComponent<Props> = ({
return (
<div
id="items-column"
className="sn-component section app-column flex flex-col border-b border-solid border-border xl:w-87.5 xsm-only:!w-full sm-only:!w-full md-only:!w-52 lg-only:!w-52"
className={classNames(
'sn-component section app-column flex flex-col border-b border-solid border-border ',
'xl:w-87.5 xsm-only:!w-full sm-only:!w-full md-only:!w-52 lg-only:!w-52',
)}
aria-label={'Notes & Files'}
ref={itemsViewPanelRef}
>
@@ -188,7 +196,7 @@ const ContentListView: FunctionComponent<Props> = ({
<div id="items-title-bar-container">
<input
type="file"
className="absolute top-0 left-0 opacity-0"
className="absolute top-0 left-0 -z-50 h-px w-px opacity-0"
multiple
ref={fileInputRef}
onChange={(event) => {
@@ -211,6 +219,7 @@ const ContentListView: FunctionComponent<Props> = ({
isFilesSmartView={isFilesSmartView}
optionsSubtitle={optionsSubtitle}
/>
<SearchBar itemListController={itemListController} searchOptionsController={searchOptionsController} />
<NoAccountWarning
accountMenuController={accountMenuController}
noAccountWarningController={noAccountWarningController}
@@ -1,6 +1,6 @@
import { FileItem } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback } from 'react'
import { FunctionComponent, useCallback, useRef } from 'react'
import { getFileIconComponent } from '../AttachedFilesPopover/getFileIconComponent'
import ListItemConflictIndicator from './ListItemConflictIndicator'
import ListItemFlagIcons from './ListItemFlagIcons'
@@ -9,6 +9,7 @@ import ListItemMetadata from './ListItemMetadata'
import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
application,
@@ -24,8 +25,11 @@ const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
}) => {
const { toggleAppPane } = useResponsiveAppPane()
const listItemRef = useRef<HTMLDivElement>(null)
const openFileContextMenu = useCallback(
(posX: number, posY: number) => {
filesController.setShowFileContextMenu(false)
filesController.setFileContextMenuLocation({
x: posX,
y: posY,
@@ -66,17 +70,16 @@ const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
'w-5 h-5 flex-shrink-0',
)
useContextMenuEvent(listItemRef, openContextMenu)
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'
}`}
id={item.uuid}
onClick={onClick}
onContextMenu={(event) => {
event.preventDefault()
void openContextMenu(event.clientX, event.clientY)
}}
>
{!hideIcon ? (
<div className="mr-0 flex flex-col items-center justify-between p-4.5 pr-3">
@@ -1,7 +1,7 @@
import { PLAIN_EDITOR_NAME } from '@/Constants/Constants'
import { sanitizeHtmlString, SNNote } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback } from 'react'
import { FunctionComponent, useCallback, useRef } from 'react'
import Icon from '@/Components/Icon/Icon'
import ListItemConflictIndicator from './ListItemConflictIndicator'
import ListItemFlagIcons from './ListItemFlagIcons'
@@ -10,6 +10,7 @@ import ListItemMetadata from './ListItemMetadata'
import { DisplayableListItemProps } from './Types/DisplayableListItemProps'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
application,
@@ -26,12 +27,15 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
}) => {
const { toggleAppPane } = useResponsiveAppPane()
const listItemRef = useRef<HTMLDivElement>(null)
const editorForNote = application.componentManager.editorForNote(item as SNNote)
const editorName = editorForNote?.name ?? PLAIN_EDITOR_NAME
const [icon, tint] = application.iconsController.getIconAndTintForNoteType(editorForNote?.package_info.note_type)
const hasFiles = application.items.getFilesForNote(item as SNNote).length > 0
const openNoteContextMenu = (posX: number, posY: number) => {
notesController.setContextMenuOpen(false)
notesController.setContextMenuClickLocation({
x: posX,
y: posY,
@@ -62,17 +66,16 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
}
}, [item.uuid, selectionController, toggleAppPane])
useContextMenuEvent(listItemRef, openContextMenu)
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-info'
}`}
id={item.uuid}
onClick={onClick}
onContextMenu={(event) => {
event.preventDefault()
void openContextMenu(event.clientX, event.clientY)
}}
>
{!hideIcon ? (
<div className="mr-0 flex flex-col items-center justify-between p-4 pr-4">
@@ -6,11 +6,9 @@ import { ApplicationEvent, PrefKey } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer'
import SearchBar from '@/Components/SearchBar/SearchBar'
import ResponsivePaneContent from '@/Components/ResponsivePane/ResponsivePaneContent'
import { AppPaneId } from '@/Components/ResponsivePane/AppPaneMetadata'
import { classNames } from '@/Utils/ConcatenateClassNames'
import Icon from '@/Components/Icon/Icon'
type Props = {
application: WebApplication
@@ -20,7 +18,6 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
const viewControllerManager = useMemo(() => application.getViewControllerManager(), [application])
const ref = useRef<HTMLDivElement>(null)
const [panelWidth, setPanelWidth] = useState<number>(0)
const [isPanelExpanded, setIsPanelExpanded] = useState(true)
useEffect(() => {
const removeObserver = application.addEventObserver(async () => {
@@ -51,37 +48,16 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
return (
<div
id="navigation"
className={classNames(
'sn-component section app-column xl:w-[220px] xsm-only:!w-full sm-only:!w-full md-only:transition-width lg-only:transition-width',
isPanelExpanded ? 'md-only:!w-[220px] lg-only:!w-[220px]' : 'md-only:!w-18 lg-only:!w-18',
)}
className={'sn-component section app-column w-[220px] xsm-only:!w-full sm-only:!w-full'}
ref={ref}
>
<ResponsivePaneContent paneId={AppPaneId.Navigation} contentElementId="navigation-content">
{isPanelExpanded && (
<SearchBar
itemListController={viewControllerManager.itemListController}
searchOptionsController={viewControllerManager.searchOptionsController}
selectedViewTitle={viewControllerManager.navigationController.selected?.title}
/>
)}
<div className={'flex justify-end'}>
<div className={classNames('section-title-bar block w-full xl:block', !isPanelExpanded && 'hidden')}>
<div className="section-title-bar-header">
<div className="title text-sm">
<span className="font-bold">Views</span>
</div>
<div className={'section-title-bar'}>
<div className="section-title-bar-header">
<div className="title text-sm">
<span className="font-bold">Views</span>
</div>
</div>
<div
className={classNames(
'hidden items-end self-end rounded-full rounded-tr-none rounded-br-none bg-default p-1 text-foreground md:flex xl:hidden',
isPanelExpanded ? 'mb-1 w-fit' : 'mt-4.5 mb-3 ml-3 w-full',
)}
onClick={() => setIsPanelExpanded(!isPanelExpanded)}
>
<Icon type="chevron-down" className={isPanelExpanded ? 'rotate-90' : '-rotate-90'} />
</div>
</div>
<div
className={classNames(
@@ -90,8 +66,8 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
'md:hover:[overflow-y:_overlay]',
)}
>
<SmartViewsSection viewControllerManager={viewControllerManager} isCollapsed={!isPanelExpanded} />
<TagsSection viewControllerManager={viewControllerManager} isCollapsed={!isPanelExpanded} />
<SmartViewsSection viewControllerManager={viewControllerManager} />
<TagsSection viewControllerManager={viewControllerManager} />
</div>
</ResponsivePaneContent>
{ref.current && (
@@ -36,6 +36,7 @@ import {
import { reloadFont } from './FontFunctions'
import { NoteViewProps } from './NoteViewProps'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
import { classNames } from '@/Utils/ConcatenateClassNames'
const MINIMUM_STATUS_DURATION = 400
const TEXTAREA_DEBOUNCE = 100
@@ -911,7 +912,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
{this.note && (
<div id="editor-title-bar" className="content-title-bar section-title-bar z-editor-title-bar w-full">
<div className="mb-2 flex flex-wrap items-center justify-between gap-2 md:mb-0 md:flex-nowrap md:gap-0">
<div className="mb-2 flex flex-wrap items-start justify-between gap-2 md:mb-0 md:flex-nowrap md:gap-0 xl:items-center">
<div className={(this.state.noteLocked ? 'locked' : '') + ' flex-grow'}>
<div className="title overflow-auto">
<input
@@ -929,9 +930,14 @@ class NoteView extends PureComponent<NoteViewProps, State> {
/>
</div>
</div>
<div className="flex flex-col flex-wrap items-start gap-3 md:flex-row md:flex-nowrap md:items-center">
<div
className={classNames(
'flex flex-col flex-wrap items-start gap-3 md:flex-col-reverse md:items-end',
'xl:flex-row xl:flex-nowrap xl:items-center',
)}
>
{this.state.noteStatus?.message?.length && (
<div id="save-status-container">
<div id="save-status-container" className={'xl:mr-5 xl:max-w-[16ch]'}>
<div id="save-status">
<div
className={
@@ -8,12 +8,11 @@ import DecoratedInput from '../Input/DecoratedInput'
import { observer } from 'mobx-react-lite'
type Props = {
selectedViewTitle?: string
itemListController: ItemListController
searchOptionsController: SearchOptionsController
}
const SearchBar = ({ itemListController, searchOptionsController, selectedViewTitle = 'Notes' }: Props) => {
const SearchBar = ({ itemListController, searchOptionsController }: Props) => {
const searchInputRef = useRef<HTMLInputElement>(null)
const { noteFilterText, setNoteFilterText, clearFilterText, onFilterEnter } = itemListController
@@ -45,15 +44,14 @@ const SearchBar = ({ itemListController, searchOptionsController, selectedViewTi
}, [clearFilterText])
return (
<div className="px-2.5 pt-4 pb-0.5" role="search">
<div className="pt-3 pb-0.5" role="search">
<DecoratedInput
autocomplete={false}
title="Searches notes and files in the currently selected tag"
className={{
container: 'px-1',
input: 'placeholder:text-passive-0',
}}
placeholder={`Search in ${selectedViewTitle}`}
placeholder={'Search...'}
value={noteFilterText}
ref={searchInputRef}
onBlur={onSearchBlur}
@@ -5,10 +5,9 @@ import SmartViewsListItem from './SmartViewsListItem'
type Props = {
viewControllerManager: ViewControllerManager
isCollapsed: boolean
}
const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }: Props) => {
const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager }: Props) => {
const allViews = viewControllerManager.navigationController.smartViews
return (
@@ -20,7 +19,6 @@ const SmartViewsList: FunctionComponent<Props> = ({ viewControllerManager, isCol
view={view}
tagsState={viewControllerManager.navigationController}
features={viewControllerManager.featuresController}
isCollapsed={isCollapsed}
/>
)
})}
@@ -21,7 +21,6 @@ type Props = {
view: SmartView
tagsState: NavigationController
features: FeaturesController
isCollapsed: boolean
}
const PADDING_BASE_PX = 14
@@ -47,7 +46,7 @@ const smartViewIconType = (view: SmartView, isSelected: boolean): IconType => {
return 'hashtag'
}
const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, isCollapsed }) => {
const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState }) => {
const { toggleAppPane } = useResponsiveAppPane()
const [title, setTitle] = useState(view.title || '')
@@ -113,31 +112,19 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, isColla
return (
<>
<div
className={classNames(
'tag',
isSelected && 'selected',
isFaded && 'opacity-50',
isCollapsed && '!bg-transparent',
)}
className={classNames('tag', isSelected && 'selected', isFaded && 'opacity-50')}
onClick={selectCurrentTag}
style={{
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}}
>
<div className="tag-info relative">
<div
className={classNames(
isCollapsed
? `relative flex h-[40px] w-[40px] items-center justify-center
${isSelected ? 'transparent-info-color-background' : 'transparent-info-color-background-hover'}`
: 'tag-icon mr-2',
)}
>
<div className="tag-info">
<div className={'tag-icon mr-2'}>
<Icon type={iconType} className={isSelected ? 'text-info' : 'text-neutral'} />
</div>
{isEditing ? (
<input
className={classNames('title editing', isCollapsed ? 'hidden' : 'block')}
className={'title editing'}
id={`react-tag-${view.uuid}`}
onBlur={onBlur}
onInput={onInput}
@@ -147,19 +134,14 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, isColla
ref={inputRef}
/>
) : (
<div
className={classNames('title overflow-hidden text-left', isCollapsed ? 'hidden' : 'block')}
id={`react-tag-${view.uuid}`}
>
<div className={'title overflow-hidden text-left'} id={`react-tag-${view.uuid}`}>
{title}
</div>
)}
<div className={classNames('count', isCollapsed ? 'absolute top-0 right-0' : '')}>
{view.uuid === SystemViewId.AllNotes && tagsState.allNotesCount}
</div>
<div className={'count'}>{view.uuid === SystemViewId.AllNotes && tagsState.allNotesCount}</div>
</div>
{!isSystemView(view) && !isCollapsed && (
{!isSystemView(view) && (
<div className="meta">
{view.conflictOf && (
<div className="danger text-[0.625rem] font-bold">Conflicted Copy {view.conflictOf}</div>
@@ -5,13 +5,12 @@ import SmartViewsList from './SmartViewsList'
type Props = {
viewControllerManager: ViewControllerManager
isCollapsed: boolean
}
const SmartViewsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }) => {
const SmartViewsSection: FunctionComponent<Props> = ({ viewControllerManager }) => {
return (
<section>
<SmartViewsList viewControllerManager={viewControllerManager} isCollapsed={isCollapsed} />
<SmartViewsList viewControllerManager={viewControllerManager} />
</section>
)
}
@@ -9,10 +9,9 @@ import { TagsListItem } from './TagsListItem'
type Props = {
viewControllerManager: ViewControllerManager
isCollapsed: boolean
}
const TagsList: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }: Props) => {
const TagsList: FunctionComponent<Props> = ({ viewControllerManager }: Props) => {
const tagsState = viewControllerManager.navigationController
const allTags = tagsState.allLocalRootTags
@@ -53,7 +52,6 @@ const TagsList: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed
tagsState={tagsState}
features={viewControllerManager.featuresController}
onContextMenu={onContextMenu}
isCollapsed={isCollapsed}
/>
)
})}
@@ -22,7 +22,6 @@ import { useDrag, useDrop } from 'react-dnd'
import { DropItem, DropProps, ItemTypes } from './DragNDrop'
import { useResponsiveAppPane } from '../ResponsivePane/ResponsivePaneProvider'
import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
import { classNames } from '@/Utils/ConcatenateClassNames'
type Props = {
tag: SNTag
@@ -30,306 +29,287 @@ type Props = {
features: FeaturesController
level: number
onContextMenu: (tag: SNTag, posX: number, posY: number) => void
isCollapsed: boolean
}
const PADDING_BASE_PX = 14
const PADDING_BASE_WHEN_COLLAPSED_PX = 6
const PADDING_PER_LEVEL_PX = 21
const PADDING_PER_LEVEL_WHEN_COLLAPSED_PX = 10
export const TagsListItem: FunctionComponent<Props> = observer(
({ tag, features, tagsState, level, onContextMenu, isCollapsed }) => {
const { toggleAppPane } = useResponsiveAppPane()
export const TagsListItem: FunctionComponent<Props> = observer(({ tag, features, tagsState, level, onContextMenu }) => {
const { toggleAppPane } = useResponsiveAppPane()
const [title, setTitle] = useState(tag.title || '')
const [subtagTitle, setSubtagTitle] = useState('')
const inputRef = useRef<HTMLInputElement>(null)
const subtagInputRef = useRef<HTMLInputElement>(null)
const menuButtonRef = useRef<HTMLAnchorElement>(null)
const [title, setTitle] = useState(tag.title || '')
const [subtagTitle, setSubtagTitle] = useState('')
const inputRef = useRef<HTMLInputElement>(null)
const subtagInputRef = useRef<HTMLInputElement>(null)
const menuButtonRef = useRef<HTMLAnchorElement>(null)
const isSelected = tagsState.selected === tag
const isEditing = tagsState.editingTag === tag
const isAddingSubtag = tagsState.addingSubtagTo === tag
const noteCounts = computed(() => tagsState.getNotesCount(tag))
const isSelected = tagsState.selected === tag
const isEditing = tagsState.editingTag === tag
const isAddingSubtag = tagsState.addingSubtagTo === tag
const noteCounts = computed(() => tagsState.getNotesCount(tag))
const childrenTags = computed(() => tagsState.getChildren(tag)).get()
const hasChildren = childrenTags.length > 0
const childrenTags = computed(() => tagsState.getChildren(tag)).get()
const hasChildren = childrenTags.length > 0
const hasFolders = features.hasFolders
const hasAtLeastOneFolder = tagsState.hasAtLeastOneFolder
const hasFolders = features.hasFolders
const hasAtLeastOneFolder = tagsState.hasAtLeastOneFolder
const premiumModal = usePremiumModal()
const premiumModal = usePremiumModal()
const [showChildren, setShowChildren] = useState(tag.expanded)
const [hadChildren, setHadChildren] = useState(hasChildren)
const [showChildren, setShowChildren] = useState(tag.expanded)
const [hadChildren, setHadChildren] = useState(hasChildren)
useEffect(() => {
if (!hadChildren && hasChildren) {
setShowChildren(true)
}
setHadChildren(hasChildren)
}, [hadChildren, hasChildren])
useEffect(() => {
if (!hadChildren && hasChildren) {
setShowChildren(true)
}
setHadChildren(hasChildren)
}, [hadChildren, hasChildren])
useEffect(() => {
setTitle(tag.title || '')
}, [setTitle, tag])
useEffect(() => {
setTitle(tag.title || '')
}, [setTitle, tag])
const toggleChildren: MouseEventHandler = useCallback(
(e) => {
e.stopPropagation()
setShowChildren((x) => {
tagsState.setExpanded(tag, !x)
return !x
})
},
[setShowChildren, tag, tagsState],
)
const toggleChildren: MouseEventHandler = useCallback(
(e) => {
e.stopPropagation()
setShowChildren((x) => {
tagsState.setExpanded(tag, !x)
return !x
})
},
[setShowChildren, tag, tagsState],
)
const selectCurrentTag = useCallback(async () => {
await tagsState.setSelectedTag(tag)
toggleAppPane(AppPaneId.Items)
}, [tagsState, tag, toggleAppPane])
const selectCurrentTag = useCallback(async () => {
await tagsState.setSelectedTag(tag)
toggleAppPane(AppPaneId.Items)
}, [tagsState, tag, toggleAppPane])
const onBlur = useCallback(() => {
tagsState.save(tag, title).catch(console.error)
setTitle(tag.title)
}, [tagsState, tag, title, setTitle])
const onBlur = useCallback(() => {
tagsState.save(tag, title).catch(console.error)
setTitle(tag.title)
}, [tagsState, tag, title, setTitle])
const onInput: FormEventHandler = useCallback(
(e) => {
const value = (e.target as HTMLInputElement).value
setTitle(value)
},
[setTitle],
)
const onKeyDown: KeyboardEventHandler = useCallback(
(e) => {
if (e.key === KeyboardKey.Enter) {
inputRef.current?.blur()
e.preventDefault()
}
},
[inputRef],
)
useEffect(() => {
if (isEditing) {
inputRef.current?.focus()
}
}, [inputRef, isEditing])
const onSubtagInput = useCallback((e) => {
const onInput: FormEventHandler = useCallback(
(e) => {
const value = (e.target as HTMLInputElement).value
setSubtagTitle(value)
}, [])
setTitle(value)
},
[setTitle],
)
const onSubtagInputBlur = useCallback(() => {
tagsState.createSubtagAndAssignParent(tag, subtagTitle).catch(console.error)
setSubtagTitle('')
}, [subtagTitle, tag, tagsState])
const onSubtagKeyDown: KeyboardEventHandler = useCallback(
(e) => {
if (e.key === KeyboardKey.Enter) {
e.preventDefault()
subtagInputRef.current?.blur()
}
},
[subtagInputRef],
)
useEffect(() => {
if (isAddingSubtag) {
subtagInputRef.current?.focus()
const onKeyDown: KeyboardEventHandler = useCallback(
(e) => {
if (e.key === KeyboardKey.Enter) {
inputRef.current?.blur()
e.preventDefault()
}
}, [subtagInputRef, isAddingSubtag])
},
[inputRef],
)
const [, dragRef] = useDrag(
() => ({
type: ItemTypes.TAG,
item: { uuid: tag.uuid },
canDrag: () => {
return true
},
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
useEffect(() => {
if (isEditing) {
inputRef.current?.focus()
}
}, [inputRef, isEditing])
const onSubtagInput = useCallback((e) => {
const value = (e.target as HTMLInputElement).value
setSubtagTitle(value)
}, [])
const onSubtagInputBlur = useCallback(() => {
tagsState.createSubtagAndAssignParent(tag, subtagTitle).catch(console.error)
setSubtagTitle('')
}, [subtagTitle, tag, tagsState])
const onSubtagKeyDown: KeyboardEventHandler = useCallback(
(e) => {
if (e.key === KeyboardKey.Enter) {
e.preventDefault()
subtagInputRef.current?.blur()
}
},
[subtagInputRef],
)
useEffect(() => {
if (isAddingSubtag) {
subtagInputRef.current?.focus()
}
}, [subtagInputRef, isAddingSubtag])
const [, dragRef] = useDrag(
() => ({
type: ItemTypes.TAG,
item: { uuid: tag.uuid },
canDrag: () => {
return true
},
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
[tag],
)
}),
[tag],
)
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
() => ({
accept: ItemTypes.TAG,
canDrop: (item) => {
return tagsState.isValidTagParent(tag, item as SNTag)
},
drop: (item) => {
if (!hasFolders) {
premiumModal.activate(TAG_FOLDERS_FEATURE_NAME)
return
}
tagsState.assignParent(item.uuid, tag.uuid).catch(console.error)
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
}),
[tag, tagsState, hasFolders, premiumModal],
)
const readyToDrop = isOver && canDrop
const toggleContextMenu: MouseEventHandler<HTMLAnchorElement> = useCallback(
(event) => {
event.preventDefault()
event.stopPropagation()
if (!menuButtonRef.current) {
const [{ isOver, canDrop }, dropRef] = useDrop<DropItem, void, DropProps>(
() => ({
accept: ItemTypes.TAG,
canDrop: (item) => {
return tagsState.isValidTagParent(tag, item as SNTag)
},
drop: (item) => {
if (!hasFolders) {
premiumModal.activate(TAG_FOLDERS_FEATURE_NAME)
return
}
const contextMenuOpen = tagsState.contextMenuOpen
const menuButtonRect = menuButtonRef.current?.getBoundingClientRect()
if (contextMenuOpen) {
tagsState.setContextMenuOpen(false)
} else {
onContextMenu(tag, menuButtonRect.right, menuButtonRect.top)
}
tagsState.assignParent(item.uuid, tag.uuid).catch(console.error)
},
[onContextMenu, tagsState, tag],
)
collect: (monitor) => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop(),
}),
}),
[tag, tagsState, hasFolders, premiumModal],
)
return (
<>
<button
className={`tag focus:shadow-inner ${isSelected ? 'selected' : ''} ${readyToDrop ? 'is-drag-over' : ''}`}
onClick={selectCurrentTag}
ref={dragRef}
style={{
paddingLeft: `${
isCollapsed
? level * PADDING_PER_LEVEL_WHEN_COLLAPSED_PX + PADDING_BASE_WHEN_COLLAPSED_PX
: level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX
}px`,
}}
onContextMenu={(e) => {
e.preventDefault()
onContextMenu(tag, e.clientX, e.clientY)
}}
>
<div className="tag-info" title={title} ref={dropRef}>
{hasAtLeastOneFolder && (
<div className="tag-fold-container">
<a
role="button"
className={`tag-fold focus:shadow-inner ${showChildren ? 'opened' : 'closed'} ${
!hasChildren ? 'invisible' : ''
}`}
onClick={hasChildren ? toggleChildren : undefined}
>
<Icon className={'text-neutral'} type={showChildren ? 'menu-arrow-down-alt' : 'menu-arrow-right'} />
</a>
</div>
)}
<div
className={classNames('tag-icon draggable mr-2', isCollapsed ? 'md-only:!hidden lg-only:!hidden' : '')}
ref={dragRef}
>
<Icon type="hashtag" className={`${isSelected ? 'text-info' : 'text-neutral'}`} />
</div>
{isEditing ? (
<input
className={classNames(
'title editing focus:shadow-none focus:outline-none',
isCollapsed ? 'md-only:!w-min lg-only:!w-min' : '',
)}
id={`react-tag-${tag.uuid}`}
onBlur={onBlur}
onInput={onInput}
value={title}
onKeyDown={onKeyDown}
spellCheck={false}
ref={inputRef}
/>
) : (
<div
className={classNames(
'title overflow-hidden text-left focus:shadow-none focus:outline-none',
isCollapsed ? 'md-only:!w-min lg-only:!w-min' : '',
)}
id={`react-tag-${tag.uuid}`}
>
{title}
</div>
)}
<div className="flex items-center">
const readyToDrop = isOver && canDrop
const toggleContextMenu: MouseEventHandler<HTMLAnchorElement> = useCallback(
(event) => {
event.preventDefault()
event.stopPropagation()
if (!menuButtonRef.current) {
return
}
const contextMenuOpen = tagsState.contextMenuOpen
const menuButtonRect = menuButtonRef.current?.getBoundingClientRect()
if (contextMenuOpen) {
tagsState.setContextMenuOpen(false)
} else {
onContextMenu(tag, menuButtonRect.right, menuButtonRect.top)
}
},
[onContextMenu, tagsState, tag],
)
return (
<>
<button
className={`tag focus:shadow-inner ${isSelected ? 'selected' : ''} ${readyToDrop ? 'is-drag-over' : ''}`}
onClick={selectCurrentTag}
ref={dragRef}
style={{
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}}
onContextMenu={(e) => {
e.preventDefault()
onContextMenu(tag, e.clientX, e.clientY)
}}
>
<div className="tag-info" title={title} ref={dropRef}>
{hasAtLeastOneFolder && (
<div className="tag-fold-container">
<a
role="button"
className={`mr-2 cursor-pointer border-0 bg-transparent hover:bg-contrast focus:shadow-inner ${
isSelected ? 'visible' : 'invisible'
className={`tag-fold focus:shadow-inner ${showChildren ? 'opened' : 'closed'} ${
!hasChildren ? 'invisible' : ''
}`}
onClick={toggleContextMenu}
ref={menuButtonRef}
onClick={hasChildren ? toggleChildren : undefined}
>
<Icon type="more" className="text-neutral" />
<Icon className={'text-neutral'} type={showChildren ? 'menu-arrow-down-alt' : 'menu-arrow-right'} />
</a>
<div className="count">{noteCounts.get()}</div>
</div>
)}
<div className={'tag-icon draggable mr-2'} ref={dragRef}>
<Icon type="hashtag" className={`${isSelected ? 'text-info' : 'text-neutral'}`} />
</div>
{isEditing ? (
<input
className={'title editing focus:shadow-none focus:outline-none'}
id={`react-tag-${tag.uuid}`}
onBlur={onBlur}
onInput={onInput}
value={title}
onKeyDown={onKeyDown}
spellCheck={false}
ref={inputRef}
/>
) : (
<div
className={'title overflow-hidden text-left focus:shadow-none focus:outline-none'}
id={`react-tag-${tag.uuid}`}
>
{title}
</div>
)}
<div className="flex items-center">
<a
role="button"
className={`mr-2 cursor-pointer border-0 bg-transparent hover:bg-contrast focus:shadow-inner ${
isSelected ? 'visible' : 'invisible'
}`}
onClick={toggleContextMenu}
ref={menuButtonRef}
>
<Icon type="more" className="text-neutral" />
</a>
<div className="count">{noteCounts.get()}</div>
</div>
</div>
<div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}>
{tag.conflictOf && <div className="danger text-[0.625rem] font-bold">Conflicted Copy {tag.conflictOf}</div>}
</div>
</button>
{isAddingSubtag && (
<div
className="tag overflow-hidden"
style={{
paddingLeft: `${(level + 1) * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}}
>
<div className="tag-info">
<div className="flex h-full min-w-[22px] items-center border-0 bg-transparent p-0" />
<div className="tag-icon mr-1">
<Icon type="hashtag" className="mr-1 text-neutral" />
</div>
<input
className="title w-full focus:shadow-none focus:outline-none"
type="text"
ref={subtagInputRef}
onBlur={onSubtagInputBlur}
onKeyDown={onSubtagKeyDown}
value={subtagTitle}
onInput={onSubtagInput}
/>
<div className={`meta ${hasAtLeastOneFolder ? 'with-folders' : ''}`}>
{tag.conflictOf && <div className="danger text-[0.625rem] font-bold">Conflicted Copy {tag.conflictOf}</div>}
</div>
</button>
{isAddingSubtag && (
<div
className="tag overflow-hidden"
style={{
paddingLeft: `${(level + 1) * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}}
>
<div className="tag-info">
<div className="flex h-full min-w-[22px] items-center border-0 bg-transparent p-0" />
<div className="tag-icon mr-1">
<Icon type="hashtag" className="mr-1 text-neutral" />
</div>
<input
className="title w-full focus:shadow-none focus:outline-none"
type="text"
ref={subtagInputRef}
onBlur={onSubtagInputBlur}
onKeyDown={onSubtagKeyDown}
value={subtagTitle}
onInput={onSubtagInput}
/>
</div>
)}
{showChildren && (
<>
{childrenTags.map((tag) => {
return (
<TagsListItem
level={level + 1}
key={tag.uuid}
tag={tag}
tagsState={tagsState}
features={features}
onContextMenu={onContextMenu}
isCollapsed={isCollapsed}
/>
)
})}
</>
)}
</>
)
},
)
</div>
)}
{showChildren && (
<>
{childrenTags.map((tag) => {
return (
<TagsListItem
level={level + 1}
key={tag.uuid}
tag={tag}
tagsState={tagsState}
features={features}
onContextMenu={onContextMenu}
/>
)
})}
</>
)}
</>
)
})
TagsListItem.displayName = 'TagsListItem'
@@ -5,14 +5,12 @@ import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
import TagsSectionAddButton from './TagsSectionAddButton'
import TagsSectionTitle from './TagsSectionTitle'
import { classNames } from '@/Utils/ConcatenateClassNames'
type Props = {
viewControllerManager: ViewControllerManager
isCollapsed: boolean
}
const TagsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollapsed }) => {
const TagsSection: FunctionComponent<Props> = ({ viewControllerManager }) => {
const [hasMigration, setHasMigration] = useState<boolean>(false)
const checkIfMigrationNeeded = useCallback(() => {
@@ -55,7 +53,7 @@ const TagsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollap
return (
<section>
<div className={classNames('section-title-bar', isCollapsed ? 'md-only:hidden lg-only:hidden' : '')}>
<div className={'section-title-bar'}>
<div className="section-title-bar-header">
<TagsSectionTitle
features={viewControllerManager.featuresController}
@@ -68,13 +66,7 @@ const TagsSection: FunctionComponent<Props> = ({ viewControllerManager, isCollap
/>
</div>
</div>
<div
className={classNames(
'hidden',
isCollapsed ? 'mt-6 mb-7 border border-border md-only:block lg-only:block' : '',
)}
/>
<TagsList viewControllerManager={viewControllerManager} isCollapsed={isCollapsed} />
<TagsList viewControllerManager={viewControllerManager} />
</section>
)
}
@@ -7,16 +7,15 @@ import { FunctionComponent } from 'react'
type Props = {
tags: NavigationController
features: FeaturesController
className?: string
}
const TagsSectionAddButton: FunctionComponent<Props> = ({ tags, className = '' }) => {
const TagsSectionAddButton: FunctionComponent<Props> = ({ tags }) => {
return (
<IconButton
focusable={true}
icon="add"
title="Create a new tag"
className={`p-0 text-neutral ${className}`}
className="p-0 text-neutral"
onClick={() => tags.createNewTemplate()}
/>
)
@@ -9,10 +9,9 @@ type Props = {
features: FeaturesController
hasMigration: boolean
onClickMigration: () => void
className?: string
}
const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, onClickMigration, className = '' }) => {
const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, onClickMigration }) => {
const entitledToFolders = features.hasFolders
const modal = usePremiumModal()
@@ -23,7 +22,7 @@ const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, on
if (entitledToFolders) {
return (
<>
<div className={`title text-sm ${className}`}>
<div className="title text-sm">
<span className="font-bold">Folders</span>
{hasMigration && (
<label className="ml-1 cursor-pointer font-bold text-info" onClick={onClickMigration}>
@@ -37,7 +36,7 @@ const TagsSectionTitle: FunctionComponent<Props> = ({ features, hasMigration, on
return (
<>
<div className={`title text-sm ${className}`}>
<div className="title text-sm">
<span className="font-bold">Tags</span>
<Tooltip label={TAG_FOLDERS_FEATURE_TOOLTIP}>
<label className="ml-1 cursor-pointer font-bold text-passive-2" onClick={showPremiumAlert}>
@@ -1,10 +1,17 @@
import { destroyAllObjectProperties, isDev } from '@/Utils'
import { action, computed, makeObservable, observable, runInAction } from 'mobx'
import { ApplicationEvent, ContentType, InternalEventBus, SNNote, SNTag } from '@standardnotes/snjs'
import {
ApplicationEvent,
ContentType,
InternalEventBus,
SNNote,
SNTag,
ItemCounterInterface,
ItemCounts,
} from '@standardnotes/snjs'
import { WebApplication } from '@/Application/Application'
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
import { AbstractViewController } from '../Abstract/AbstractViewController'
import { StructuredItemsCount } from './StructuredItemsCount'
export class AccountMenuController extends AbstractViewController {
show = false
@@ -28,7 +35,7 @@ export class AccountMenuController extends AbstractViewController {
destroyAllObjectProperties(this)
}
constructor(application: WebApplication, eventBus: InternalEventBus) {
constructor(application: WebApplication, eventBus: InternalEventBus, private itemCounter: ItemCounterInterface) {
super(application, eventBus)
makeObservable(this, {
@@ -152,30 +159,7 @@ export class AccountMenuController extends AbstractViewController {
return this.notesAndTags.length
}
get structuredNotesAndTagsCount(): StructuredItemsCount {
const count: StructuredItemsCount = {
notes: 0,
archived: 0,
deleted: 0,
tags: 0,
}
for (const item of this.notesAndTags) {
if (item.archived) {
count.archived++
}
if (item.trashed) {
count.deleted++
}
if (item.content_type === ContentType.Note) {
count.notes++
}
if (item.content_type === ContentType.Tag) {
count.tags++
}
}
return count
get structuredNotesAndTagsCount(): ItemCounts {
return this.itemCounter.countNotesAndTags(this.notesAndTags)
}
}
@@ -2,7 +2,14 @@ import { storage, StorageKey } from '@standardnotes/ui-services'
import { WebApplication } from '@/Application/Application'
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
import { destroyAllObjectProperties } from '@/Utils'
import { ApplicationEvent, DeinitSource, WebOrDesktopDeviceInterface, InternalEventBus } from '@standardnotes/snjs'
import {
ApplicationEvent,
DeinitSource,
WebOrDesktopDeviceInterface,
InternalEventBus,
ItemCounterInterface,
ItemCounter,
} from '@standardnotes/snjs'
import { action, makeObservable, observable } from 'mobx'
import { ActionsMenuController } from './ActionsMenuController'
import { FeaturesController } from './FeaturesController'
@@ -52,10 +59,13 @@ export class ViewControllerManager {
private appEventObserverRemovers: (() => void)[] = []
private eventBus: InternalEventBus
private itemCounter: ItemCounterInterface
constructor(public application: WebApplication, private device: WebOrDesktopDeviceInterface) {
this.eventBus = new InternalEventBus()
this.itemCounter = new ItemCounter()
this.selectionController = new SelectedItemsController(application, this.eventBus)
this.noteTagsController = new NoteTagsController(application, this.eventBus)
@@ -90,7 +100,7 @@ export class ViewControllerManager {
this.noAccountWarningController = new NoAccountWarningController(application, this.eventBus)
this.accountMenuController = new AccountMenuController(application, this.eventBus)
this.accountMenuController = new AccountMenuController(application, this.eventBus, this.itemCounter)
this.subscriptionController = new SubscriptionController(application, this.eventBus)
@@ -0,0 +1,38 @@
import { isIOS } from '@/Utils'
import { RefObject, useCallback, useEffect } from 'react'
import { useLongPressEvent } from './useLongPress'
export const useContextMenuEvent = (elementRef: RefObject<HTMLElement>, listener: (x: number, y: number) => void) => {
const { attachEvents, cleanupEvents } = useLongPressEvent(elementRef, listener)
const handleContextMenuEvent = useCallback(
(event: MouseEvent) => {
event.preventDefault()
listener(event.clientX, event.clientY)
},
[listener],
)
useEffect(() => {
const element = elementRef.current
if (!element) {
return
}
const shouldUseLongPress = isIOS()
element.addEventListener('contextmenu', handleContextMenuEvent)
if (shouldUseLongPress) {
attachEvents()
}
return () => {
element.removeEventListener('contextmenu', handleContextMenuEvent)
if (shouldUseLongPress) {
cleanupEvents()
}
}
}, [attachEvents, cleanupEvents, elementRef, handleContextMenuEvent, listener])
}
@@ -0,0 +1,59 @@
import { RefObject, useCallback, useMemo, useRef } from 'react'
// According to https://reactnative.dev/docs/touchablewithoutfeedback#onlongpress
const ReactNativeLongpressDelay = 370
export const useLongPressEvent = (
elementRef: RefObject<HTMLElement>,
listener: (x: number, y: number) => void,
delay = ReactNativeLongpressDelay,
) => {
const longPressTimeout = useRef<number>()
const clearLongPressTimeout = useCallback(() => {
if (longPressTimeout.current) {
clearTimeout(longPressTimeout.current)
}
}, [])
const createLongPressTimeout = useCallback(
(event: PointerEvent) => {
clearLongPressTimeout()
longPressTimeout.current = window.setTimeout(() => {
const x = event.clientX
const y = event.clientY
listener(x, y)
}, delay)
},
[clearLongPressTimeout, delay, listener],
)
const attachEvents = useCallback(() => {
if (!elementRef.current) {
return
}
elementRef.current.addEventListener('pointerdown', createLongPressTimeout)
elementRef.current.addEventListener('pointercancel', clearLongPressTimeout)
}, [clearLongPressTimeout, createLongPressTimeout, elementRef])
const cleanupEvents = useCallback(() => {
if (!elementRef.current) {
return
}
elementRef.current.removeEventListener('pointerdown', createLongPressTimeout)
elementRef.current.removeEventListener('pointercancel', clearLongPressTimeout)
}, [clearLongPressTimeout, createLongPressTimeout, elementRef])
const memoizedReturn = useMemo(
() => ({
attachEvents,
cleanupEvents,
}),
[attachEvents, cleanupEvents],
)
return memoizedReturn
}
+6 -4
View File
@@ -172,6 +172,11 @@ export const convertStringifiedBooleanToBoolean = (value: string) => {
return value !== 'false'
}
// https://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885#9039885
export const isIOS = () =>
(/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) ||
(navigator.userAgent.includes('Mac') && 'ontouchend' in document && navigator.maxTouchPoints > 1)
// https://stackoverflow.com/a/57527009/2504429
export const disableIosTextFieldZoom = () => {
const addMaximumScaleToMetaViewport = () => {
@@ -194,10 +199,7 @@ export const disableIosTextFieldZoom = () => {
}
}
// https://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885#9039885
const checkIsIOS = () => /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
if (checkIsIOS()) {
if (isIOS()) {
addMaximumScaleToMetaViewport()
}
}
@@ -67,11 +67,6 @@ $heading-height: 75px;
position: relative;
min-width: 16ch;
overflow: visible;
margin-right: 20px;
@media screen and (min-width: 768px) {
max-width: 16ch;
}
}
#save-status {
-15
View File
@@ -132,21 +132,6 @@
@include DimmedBackground(var(--sn-stylekit-info-color), 0.08);
}
.transparent-info-color-background {
&::after {
@include DimmedBackground(var(--sn-stylekit-info-color), 0.12);
border-radius: 100px;
}
}
.transparent-info-color-background-hover {
&:hover {
&::after {
@extend .transparent-info-color-background;
}
}
}
svg.sk-circular-progress {
$pi: 3.14159265358979;