Compare commits

...

113 Commits

Author SHA1 Message Date
Ammar Ahmed
a6f42d1419 mobile: fix scrolling broken in reminder -> repeat -> month 2024-05-01 10:49:39 +05:00
Abdullah Atta
7b5fa09c71 web: fix all users are premium 2024-04-30 13:15:54 +05:00
Ammar Ahmed
d85974ed7a mobile: fix editor crash in v3 2024-04-30 12:09:15 +05:00
Ammar Ahmed
9598ea7542 mobile: fix wrong version in update sheet 2024-04-30 10:10:17 +05:00
Ammar Ahmed
42048ad53a mobile: fix multi-selection ux 2024-04-30 00:42:27 +05:00
Ammar Ahmed
1fbd2efb92 mobile: fix crash during backup 2024-04-30 00:17:25 +05:00
Ammar Ahmed
fe91ef79a0 global: update logo 2024-04-29 13:24:54 +05:00
Abdullah Atta
583d585b2f desktop: re-enable arm64 builds 2024-04-29 11:53:09 +05:00
Abdullah Atta
94b28f86ca desktop: remove channel when publishing 2024-04-29 11:38:31 +05:00
Abdullah Atta
f927f05f86 desktop: fix spawn EINVAL error on build 2024-04-29 11:15:43 +05:00
Abdullah Atta
bde8dea048 global: update package lockfiles 2024-04-29 11:15:21 +05:00
Abdullah Atta
511bea9b23 editor: update snapshots 2024-04-28 10:54:10 +05:00
Abdullah Atta
eda95530de desktop: set generateUpdatesFilesForAllChannels to false 2024-04-26 15:59:42 +05:00
Abdullah Atta
5173decaa5 crypto: upgrade sodium-native to 4.1.1 2024-04-26 15:48:42 +05:00
Abdullah Atta
00dcb55f6e ci: migrate to TrustedSigning from AzureCodeSigning 2024-04-26 15:44:29 +05:00
Abdullah Atta
6dbe1073cd ci: use AzureCodeSigning version 0.3.4 2024-04-26 15:30:53 +05:00
Abdullah Atta
9be3e4fcfa desktop: install node-gyp-build 2024-04-26 15:30:03 +05:00
Abdullah Atta
f9f0a866bd setup: fix infinite loop in postinstall 2024-04-26 15:18:49 +05:00
Abdullah Atta
daafeb2fa8 setup: retry postinstall commands 3 times 2024-04-26 15:17:49 +05:00
Abdullah Atta
a92b34b81c ci: back to node@20.x 2024-04-26 15:14:25 +05:00
Abdullah Atta
e29b6838b3 ci: use node@18.x 2024-04-26 14:53:23 +05:00
Ammar Ahmed
16ff7c92e4 mobile: update android version 2024-04-26 14:46:56 +05:00
Abdullah Atta
07e1ea6edf ci: use yarn instead of npx when executing electron-builder 2024-04-26 14:41:28 +05:00
Abdullah Atta
b26ede95fb desktop: use prebuilds on linux-arm 2024-04-26 14:01:32 +05:00
Abdullah Atta
971793a3ad desktop: use net.fetch for handling protocol requests 2024-04-26 14:01:32 +05:00
Abdullah Atta
6a2b5e1499 desktop: upgrade electron to 29 2024-04-26 14:01:32 +05:00
Abdullah Atta
d18b775d06 web: use same logic for uploading attachment on desktop & web 2024-04-26 14:01:32 +05:00
Abdullah Atta
d6cc899d95 web: close note after removing attachment permanently 2024-04-26 14:01:32 +05:00
Abdullah Atta
ea10058276 web: fix linked notes not showing in attachment context menu 2024-04-26 14:01:32 +05:00
Abdullah Atta
568c88be3e fs: fix out of bounds error when reading chunks 2024-04-26 14:01:32 +05:00
Abdullah Atta
f047552f18 core: fix attachment not getting detached from note's content 2024-04-26 14:01:32 +05:00
Ammar Ahmed
9a58430b5d mobile: release v3.0.0 2024-04-26 11:29:59 +05:00
Ammar Ahmed
205885edf5 mobile: disable app lock when picking file to restore backup 2024-04-26 11:29:59 +05:00
Abdullah Atta
8ffb49b700 desktop: fix Unexpected token 'with' on build 2024-04-26 10:30:44 +05:00
Abdullah Atta
25b18a2edd ci: pin node version to 20.2.0 2024-04-26 10:24:54 +05:00
Abdullah Atta
cfea85c8fb web: add v3 feature highlights 2024-04-26 09:35:06 +05:00
Abdullah Atta
82fc6d31c3 web: fix new notes not getting assigned to default notebook 2024-04-26 09:15:35 +05:00
Abdullah Atta
fb2de1753e web: fix full screen test 2024-04-26 09:06:00 +05:00
Ammar Ahmed
ceb5ce5cdd mobile: add quick settings tile on android 2024-04-26 08:49:38 +05:00
Ammar Ahmed
98d443001c mobile: fix no placeholder text on share widget 2024-04-26 08:49:38 +05:00
Abdullah Atta
5115dee069 core: fix crash when converting html to text 2024-04-26 08:28:32 +05:00
Abdullah Atta
0dc29c7026 core: refactor reminder upcoming time sql 2024-04-26 07:49:47 +05:00
Muhammad Ali
a5eb5e9df3 core: fix incorrect upcoming time in weekly/monthly reminders (#4464) 2024-04-25 16:22:34 +05:00
Ammar Ahmed
b680575328 mobile: fix padding of internal editors on mobile 2024-04-25 15:50:17 +05:00
Ammar Ahmed
c94ef616a5 mobile: improve ui of side menu user section 2024-04-25 15:31:10 +05:00
Ammar Ahmed
3ae40c09fc mobile: fix db init in background failing 2024-04-25 15:31:10 +05:00
Ammar Ahmed
5de4e00ce2 mobile: enable background sync by default 2024-04-25 15:31:10 +05:00
Ammar Ahmed
99e4e61a55 mobile: fix notebook sheet does not remember last position 2024-04-25 15:31:10 +05:00
Abdullah Atta
1ad7bfb82f web: fix settings dialog side bar transparent from bottom 2024-04-25 14:43:21 +05:00
Abdullah Atta
30ef679432 web: fix empty editor after unlocking app 2024-04-25 13:40:11 +05:00
Abdullah Atta
bb97f5e757 clipper(web): update app logo 2024-04-25 13:00:29 +05:00
Abdullah Atta
6cd5a04470 desktop: update app logo 2024-04-25 13:00:17 +05:00
Abdullah Atta
0c32ebf163 web: update app logo 2024-04-25 13:00:07 +05:00
Abdullah Atta
e150e08f29 editor: fix crash on dragging a react node 2024-04-25 10:38:40 +05:00
Ammar Ahmed
bbd603f10c docs: update logo 2024-04-25 10:23:05 +05:00
Ammar Ahmed
677944cba9 docs: remove mentions of topic 2024-04-25 10:23:05 +05:00
Ammar Ahmed
dd2cee53d3 mobile: update app logo 2024-04-25 10:23:05 +05:00
Ammar Ahmed
60c8d5ac89 mobile: fix notes in notifications not launch on app start 2024-04-25 10:23:05 +05:00
Ammar Ahmed
59276de406 mobile: fix background sync 2024-04-25 10:23:05 +05:00
Abdullah Atta
67981be516 desktop: clonableCryptoKey is always true on desktop 2024-04-25 10:17:37 +05:00
Abdullah Atta
77dfcec08c web: fix attachments ui in properties 2024-04-24 11:45:15 +05:00
Abdullah Atta
c4360ab95f common: fix exports 2024-04-24 11:45:00 +05:00
Abdullah Atta
17c4b72381 web: minor ui fix 2024-04-23 09:35:01 +05:00
Ammar Ahmed
e91cc2bdca mobile: support pinned editor tabs 2024-04-22 15:55:10 +05:00
Abdullah Atta
811f2305ec core: disable transactions for now 2024-04-22 12:23:43 +05:00
Abdullah Atta
98a707b5f5 setup: remove --prefer-offline 2024-04-22 09:52:26 +05:00
Abdullah Atta
aa9b24fa10 core: do not run auto sync on changes in notehistory & sessioncontent collections 2024-04-22 09:37:54 +05:00
Abdullah Atta
0b3060e4c0 web: improve error reporting if a query fails 2024-04-22 09:14:10 +05:00
Ammar Ahmed
d523db84cc mobile: fix back gesture handler broken in editor 2024-04-22 08:38:32 +05:00
Abdullah Atta
6a6a9ae6b3 ci: install @zip.js/zip.js before running web tests 2024-04-20 19:48:18 +05:00
Abdullah Atta
b1bc28e069 ci: install dayjs before running web tests 2024-04-20 19:41:47 +05:00
Abdullah Atta
f95c068bc1 web: fix all tests 2024-04-20 16:42:11 +05:00
Abdullah Atta
0e4cd087d4 core: add tests for virtualized grouping 2024-04-20 16:42:11 +05:00
Ammar Ahmed
f217b8cfec mobile: add borders to dialogs & sheet 2024-04-19 12:26:25 +05:00
Ammar Ahmed
d3787efbf0 web: fix wording of trash interval 2024-04-19 12:26:09 +05:00
Ammar Ahmed
e279878b12 mobile: improve move note sheet ux 2024-04-19 12:00:13 +05:00
Ammar Ahmed
154a227be0 mobile: fix placeholder item height in compact mode 2024-04-19 10:32:53 +05:00
Ammar Ahmed
756259c907 mobile: minor improvement in list 2024-04-19 10:23:12 +05:00
Ammar Ahmed
453ed4820a mobile: remove beta tag from reminders 2024-04-19 10:23:12 +05:00
Ammar Ahmed
10ab612366 mobile: fix section header title for rtl lang 2024-04-19 10:23:12 +05:00
Ammar Ahmed
8dc4f7ed3d mobile: remove v3 beta notice on login page 2024-04-19 10:23:12 +05:00
Ammar Ahmed
a73538bafc mobile: use border color for borders 2024-04-19 10:23:12 +05:00
Abdullah Atta
014887c7dc core: fix virtualized grouping showing duplicate groups 2024-04-19 10:14:22 +05:00
Abdullah Atta
246e49bd8c web: remove beta tag 2024-04-18 15:48:47 +05:00
Ammar Ahmed
146ac12935 mobile: fix search not update on changing item property 2024-04-18 14:48:37 +05:00
Abdullah Atta
4f5245adcf web: fix changing readonly doesn't update editor 2024-04-18 13:26:59 +05:00
Abdullah Atta
0bc0cc5729 Merge branch 'v3-beta' 2024-04-18 13:19:33 +05:00
Ammar Ahmed
1136072eff mobile: improve search experience 2024-04-18 13:11:43 +05:00
Ammar Ahmed
4cc8a538a4 mobile: make side bar order stateful 2024-04-18 13:11:43 +05:00
Abdullah Atta
271913ba20 core: fix migrate to sqlite progress reporting 2024-04-18 12:53:16 +05:00
Abdullah Atta
3efe541869 web: fix nav menu items order not respected 2024-04-18 12:17:31 +05:00
Abdullah Atta
b3bcdd4f9c web: fix status bar hidden 2024-04-18 12:06:01 +05:00
Abdullah Atta
13f739763c desktop: use secure dns by default 2024-04-18 11:26:22 +05:00
Abdullah Atta
bde2b26133 web: bring back publish button in top action bar 2024-04-18 11:26:01 +05:00
Abdullah Atta
6eef65952d editor: fix simple checklist not working in firefox 2024-04-18 10:23:23 +05:00
Abdullah Atta
3d85795216 common: add toolbar migration for addInternalLink tool 2024-04-18 10:00:55 +05:00
Ammar Ahmed
f1a1989990 misc: update deps 2024-04-18 09:49:56 +05:00
Ammar Ahmed
76ff5eddd0 mobile: fix tests 2024-04-18 09:49:56 +05:00
Ammar Ahmed
068ee8dc68 mobile: support drag & drop images on iOS 2024-04-18 09:49:56 +05:00
Ammar Ahmed
bbf0fb641b mobile: fix attachment upload from base64 2024-04-18 09:49:56 +05:00
Ammar Ahmed
3cae22faf1 core: fix upload attachment from content 2024-04-18 09:49:56 +05:00
Abdullah Atta
34a4a27ba0 web: reduce batch size to 100 2024-04-18 09:43:18 +05:00
Abdullah Atta
d96a78c741 web: middle click should close tab even if there is scroll 2024-04-18 09:40:41 +05:00
Abdullah Atta
c9e5ee9ddb core: use database logger instead of console.error 2024-04-18 09:37:21 +05:00
Abdullah Atta
f43c2a40f5 web: do not jump to focus the same item again 2024-04-18 08:52:12 +05:00
Abdullah Atta
b12a0e979c web: send contentId when saving a note's content 2024-04-17 16:14:20 +05:00
Abdullah Atta
d0372b82db core: add content noteId to sqlite index 2024-04-17 16:14:00 +05:00
Abdullah Atta
dac1441add web: fix various crashes 2024-04-17 14:53:05 +05:00
Abdullah Atta
a65ebec7ae web: replace allotment with react-resizable-panels 2024-04-17 14:23:19 +05:00
Abdullah Atta
d5cf65e568 web: fix potential crash when saving note 2024-04-16 23:36:38 +05:00
Ammar Ahmed
424073298e mobile: fix quick note widget not working 2024-04-16 23:24:50 +05:00
Licaon_Kter
c54759e766 Remove gradle property unused in Java 17
Signed-off-by: Licaon_Kter <licaon-kter@users.noreply.github.com>
2024-03-22 23:17:45 +05:00
Ammar Ahmed
f15446b1d6 mobile: bump version code 2024-03-21 11:45:03 +05:00
345 changed files with 70011 additions and 2845 deletions

View File

@@ -147,7 +147,7 @@ jobs:
CSC_KEY_PASSWORD: ${{ secrets.mac_certs_password }}
run: |
npx nx run release --project @notesnook/desktop -- --variant=mas
npx electron-builder --mac mas --universal -p never
yarn electron-builder --mac mas --universal -p never
working-directory: ./apps/desktop
- name: Build zip and dmg
@@ -161,9 +161,9 @@ jobs:
run: |
npx nx run release --project @notesnook/desktop
if [ ${{ inputs.publish-github }} == true ]; then
npx electron-builder --mac zip dmg --arm64 --x64 -p always
yarn electron-builder --mac zip dmg --arm64 --x64 -p always
else
npx electron-builder --mac zip dmg --arm64 --x64 -p never
yarn electron-builder --mac zip dmg --arm64 --x64 -p never
fi
working-directory: ./apps/desktop
@@ -219,7 +219,7 @@ jobs:
- name: Build snap
if: inputs.publish-snap
run: |
npx electron-builder --linux snap:x64 -p never
yarn electron-builder --linux snap:x64 -p never
working-directory: ./apps/desktop
- name: Build AppImage
@@ -227,9 +227,9 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ ${{ inputs.publish-github }} == true ]; then
npx electron-builder --linux AppImage:x64 AppImage:arm64 -p always
yarn electron-builder --linux AppImage:x64 AppImage:arm64 -p always
else
npx electron-builder --linux AppImage:x64 AppImage:arm64 -p never
yarn electron-builder --linux AppImage:x64 AppImage:arm64 -p never
fi
working-directory: ./apps/desktop
@@ -254,8 +254,8 @@ jobs:
with:
dotnet-version: 6.0.x
- name: Install AzureCodeSigning
run: Install-Module -Name AzureCodeSigning -RequiredVersion 0.3.0 -Force -Repository PSGallery
- name: Install TrustedSigning
run: Install-Module -Name TrustedSigning -RequiredVersion 0.3.15 -Force -Repository PSGallery
shell: pwsh
- name: Setup Node

View File

@@ -67,7 +67,7 @@ jobs:
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm install -D @playwright/test otplib
npm install -D @playwright/test otplib dayjs @zip.js/zip.js
- name: Validate build extraction
run: ls -ld ./apps/web/build/index.html

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.0.14-beta",
"version": "3.0.0",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
@@ -14,11 +14,11 @@
"@notesnook/crypto": "file:../../packages/crypto",
"@trpc/client": "10.38.3",
"@trpc/server": "10.38.3",
"better-sqlite3-multiple-ciphers": "^9.4.0",
"better-sqlite3-multiple-ciphers": "^9.5.0",
"electron-trpc": "0.5.2",
"electron-updater": "6.1.4",
"icojs": "^0.17.1",
"sodium-native": "^4.0.6",
"sodium-native": "^4.1.1",
"typed-emitter": "^2.1.0",
"yargs": "^17.6.2",
"zod": "^3.21.4"
@@ -27,12 +27,13 @@
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "^28.2.1",
"electron-builder": "^24.9.1",
"electron": "^29.3.1",
"electron-builder": "^24.13.3",
"esbuild": "^0.20.0",
"prebuildify": "^6.0.0",
"node-gyp-build": "^4.8.0",
"prebuildify": "^6.0.1",
"tree-kill": "^1.2.2",
"undici": "^6.6.1"
"undici": "^6.14.1"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"
@@ -56,8 +57,8 @@
"productName": "Notesnook",
"copyright": "Copyright © 2023 Streetwriters (Private) Limited",
"artifactName": "notesnook_${os}_${arch}.${ext}",
"asar": false,
"generateUpdatesFilesForAllChannels": true,
"asar": false,
"files": [
"!*.chunk.js.map",
"!*.chunk.js.LICENSE.txt",
@@ -220,8 +221,7 @@
{
"provider": "github",
"repo": "notesnook",
"owner": "streetwriters",
"channel": "beta"
"owner": "streetwriters"
}
]
}

View File

@@ -1,60 +0,0 @@
diff --git a/node_modules/prebuildify/index.js b/node_modules/prebuildify/index.js
index ca9829a..7729aaf 100644
--- a/node_modules/prebuildify/index.js
+++ b/node_modules/prebuildify/index.js
@@ -1,5 +1,4 @@
var proc = require('child_process')
-var execspawn = require('execspawn')
var os = require('os')
var path = require('path')
var fs = require('fs')
@@ -179,11 +178,12 @@ function copySharedLibs (builds, folder, opts, cb) {
function run (cmd, opts, cb) {
if (!cmd) return cb()
- var child = execspawn(cmd, {
+ var child = proc.spawn(cmd, [], {
cwd: opts.cwd,
env: opts.env,
stdio: 'inherit',
- shell: opts.shell
+ shell: opts.shell || true,
+ windowsHide: true
})
child.on('exit', function (code) {
@@ -230,6 +230,8 @@ function build (target, runtime, opts, cb) {
var child = proc.spawn(opts.nodeGyp, args, {
cwd: opts.cwd,
env: opts.env,
+ shell: opts.shell,
+ windowsHide: true,
stdio: opts.quiet ? 'ignore' : 'inherit'
})
@@ -266,7 +268,11 @@ function strip (file, opts, cb) {
if (!opts.strip || (platform !== 'darwin' && platform !== 'linux')) return cb()
var args = platform === 'darwin' ? [file, '-Sx'] : [file, '--strip-all']
- var child = proc.spawn(opts.stripBin, args, { stdio: 'ignore' })
+ var child = proc.spawn(opts.stripBin, args, {
+ stdio: 'ignore',
+ shell: opts.shell,
+ windowsHide: true
+ })
child.on('exit', function (code) {
if (code) return cb(spawnError(opts.stripBin, code))
@@ -300,7 +306,11 @@ function npmbin (name) {
}
function shell () {
- return os.platform() === 'android' ? 'sh' : undefined
+ switch (os.platform()) {
+ case 'win32': return true
+ case 'android': return 'sh'
+ default: return undefined
+ }
}
function resolveTargets (targets, all, napi, electronCompat) {

View File

@@ -19,27 +19,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import path from "path";
import fs, { readFile, writeFile } from "fs/promises";
import { existsSync } from "fs";
import { existsSync, readFileSync } from "fs";
import yargs from "yargs-parser";
import os from "os";
import * as childProcess from "child_process";
import { fileURLToPath } from "url";
import packageJson from "../package.json" with {type: "json"};
const args = yargs(process.argv);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJson = JSON.parse(
readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
);
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
await fs.rm("./build/", { force: true, recursive: true });
if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
await exec(`cd ${webAppPath} && npm run build:desktop`);
await exec(
"npx nx build:desktop @notesnook/web",
path.join(__dirname, "..", "..", "..")
);
}
// temporary until there's support for prebuilt binaries for windows ARM & linux ARM
await patchBetterSQLite3();
// temporary until there's support for prebuilt binaries for linux ARM
if (os.platform() === "linux") await patchBetterSQLite3();
if (os.platform() === "win32")
await exec(
@@ -89,7 +94,7 @@ async function patchBetterSQLite3() {
);
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
json.version = "9.4.1";
json.version = "9.5.1";
json.homepage = "https://github.com/thecodrr/better-sqlite3-multiple-ciphers";
json.repository.url =
"git://github.com/thecodrr/better-sqlite3-multiple-ciphers.git";

View File

@@ -33,7 +33,7 @@ module.exports = async function (configuration) {
const DescriptionUrl = "https://notesnook.com/";
const FilesCatalog = createCatalog(configuration.path);
const command = `Invoke-AzureCodeSigning -Endpoint "${Endpoint}" -CodeSigningAccountName "${CodeSigningAccountName}" -CertificateProfileName "${CertificateProfileName}" -FileDigest "${FileDigest}" -TimestampRfc3161 "${TimestampRfc3161}" -TimestampDigest "${TimestampDigest}" -Description "${Description}" -DescriptionUrl "${DescriptionUrl}" -FilesCatalog "${FilesCatalog}"`;
const command = `Invoke-TrustedSigning -Endpoint "${Endpoint}" -CodeSigningAccountName "${CodeSigningAccountName}" -CertificateProfileName "${CertificateProfileName}" -FileDigest "${FileDigest}" -TimestampRfc3161 "${TimestampRfc3161}" -TimestampDigest "${TimestampDigest}" -Description "${Description}" -DescriptionUrl "${DescriptionUrl}" -FilesCatalog "${FilesCatalog}"`;
console.debug("Signing", configuration.path, "using command", command);

View File

@@ -151,6 +151,15 @@ async function createWindow() {
app.once("ready", async () => {
console.info("App ready. Opening window.");
app.configureHostResolver({
secureDnsServers: [
"https://mozilla.cloudflare-dns.com/dns-query",
"https://dns.quad9.net/dns-query"
],
enableBuiltInResolver: true,
secureDnsMode: "automatic"
});
if (!isDevelopment()) registerProtocol();
await createWindow();
configureAutoUpdater();

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { protocol } from "electron";
import { net, protocol } from "electron";
import { isDevelopment } from "./index";
import { createReadStream } from "fs";
import { extname, normalize } from "path";
@@ -62,26 +62,7 @@ function registerProtocol() {
headers: { "Content-Type": extensionToMimeType[fileExtension] }
});
} else {
if (request.headers.has("X-Content-Length")) {
request.headers.set(
"Content-Length",
request.headers.get("X-Content-Length") || "0"
);
request.headers.delete("X-Content-Length");
}
const headers = Object.fromEntries(request.headers.entries());
return await fetch(request.url, {
signal: request.signal,
mode: request.mode,
headers,
method: request.method,
body: request.body,
credentials: request.credentials,
referrer: (request as any).referrer,
duplex: "half",
redirect: "manual"
});
return net.fetch(request, { bypassCustomProtocolHandlers: true });
}
});
console.info(`${SCHEME} protocol inteception "successful"`);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -67,7 +67,7 @@ export async function hashBase64(data) {
};
}
export async function writeEncryptedBase64({ data, key }) {
export async function writeEncryptedBase64(data, key) {
await createCacheDir();
let filepath = cacheDir + `/${getRandomId("imagecache_")}`;
await RNFetchBlob.fs.writeFile(filepath, data, "base64");
@@ -77,6 +77,8 @@ export async function writeEncryptedBase64({ data, key }) {
});
RNFetchBlob.fs.unlink(filepath).catch(console.log);
console.log("encrypted file output: ", output);
output.size = output.length;
delete output.length;
return {
...output,
alg: "xcha-stream"

View File

@@ -221,7 +221,7 @@ const Actions = ({
<View
style={{
borderBottomWidth: 1,
borderBottomColor: colors.secondary.background,
borderBottomColor: colors.primary.border,
marginBottom: notes && notes.length > 0 ? 0 : 12
}}
>
@@ -295,7 +295,7 @@ const Actions = ({
<View
style={{
borderBottomWidth: 1,
borderBottomColor: colors.secondary.background,
borderBottomColor: colors.primary.border,
marginBottom: 12,
paddingVertical: 12
}}

View File

@@ -296,11 +296,6 @@ export const Login = ({ changeMode }) => {
</Paragraph>
</TouchableOpacity>
) : null}
<Notice
type="alert"
text="After logging in from V3 beta, your account data will be migrated and you won't be able to use V2 clients to sync your notes."
/>
</View>
</View>
</View>

View File

@@ -21,8 +21,10 @@ import React, { PropsWithChildren, useEffect, useRef, useState } from "react";
import { Dimensions, Platform, View, useWindowDimensions } from "react-native";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import useKeyboard from "../../hooks/use-keyboard";
import { useThemeColors } from "@notesnook/theme";
export const Container = ({ children }: PropsWithChildren) => {
const { colors } = useThemeColors();
const insets = useGlobalSafeAreaInsets();
const keyboard = useKeyboard();
const [height, setHeight] = useState(0);
@@ -43,7 +45,8 @@ export const Container = ({ children }: PropsWithChildren) => {
paddingTop: Platform.OS === "android" ? 0 : insets.top,
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom,
height: height || "100%",
width: "100%"
width: "100%",
backgroundColor: colors.primary.background
}}
onLayout={(event) => {
const height = event.nativeEvent.layout.height;

View File

@@ -45,8 +45,8 @@ const DialogButtons = ({
{
backgroundColor: colors.secondary.background,
height: 60,
borderBottomRightRadius: 10,
borderBottomLeftRadius: 10,
borderBottomRightRadius: 5,
borderBottomLeftRadius: 5,
paddingHorizontal: 12,
borderTopWidth: 0.7,
borderTopColor: getColorLinearShade(

View File

@@ -36,6 +36,7 @@ import DialogButtons from "./dialog-buttons";
import DialogHeader from "./dialog-header";
import { useCallback } from "react";
import { Button } from "../ui/button";
import { getContainerBorder } from "../../utils/colors";
export const Dialog = ({ context = "global" }) => {
const { colors } = useThemeColors();
@@ -121,7 +122,8 @@ export const Dialog = ({ context = "global" }) => {
maxHeight: 450,
borderRadius: 5,
backgroundColor: colors.primary.background,
paddingTop: 12
paddingTop: 12,
...getContainerBorder(colors.primary.border, 0.5)
};
return visible ? (

View File

@@ -146,12 +146,16 @@ const ColorPicker = ({
onChangeText={(value) => {
title.current = value;
}}
testID="color-title-input"
defaultValue={title.current}
placeholder={title.current || "Color title"}
/>
<Button
title="Add color"
style={{
marginBottom: 10
}}
onPress={async () => {
if (!selectedColor)
return ToastManager.error(

View File

@@ -71,7 +71,7 @@ export const RightMenus = ({
testID={notesnook.ids.default.addBtn}
icon={renderedInRoute === "Trash" ? "delete" : "plus"}
iconSize={SIZE.xl}
type="shade"
type="accent"
hitSlop={{
top: 10,
right: 10,
@@ -82,7 +82,7 @@ export const RightMenus = ({
marginLeft: 10,
width: 32,
height: 32,
borderRadius: 5,
borderRadius: 100,
paddingHorizontal: 0,
borderWidth: 1,
borderColor: colors.primary.accent

View File

@@ -99,7 +99,6 @@ export const SectionHeader = React.memo<
color={color || colors.primary.accent}
size={SIZE.sm}
style={{
minWidth: 60,
alignSelf: "center",
textAlignVertical: "center"
}}

View File

@@ -28,13 +28,12 @@ import {
hideSheet,
presentSheet
} from "../../../services/event-manager";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { eOnLoadNote, eShowMergeDialog } from "../../../utils/events";
import { tabBarRef } from "../../../utils/global-refs";
import { NotebooksWithDateEdited, TagsWithDateEdited } from "@notesnook/common";
import NotePreview from "../../note-history/preview";
import SelectionWrapper from "../selection-wrapper";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
export const openNote = async (
item: Note,
@@ -47,21 +46,8 @@ export const openNote = async (
if (!isTrash) {
note = (await db.notes.note(item.id)) as Note;
}
if (useSelectionStore.getState().selectionMode === item.type) {
const {
selectedItemsList,
selectionMode,
clearSelection,
setSelectedItem
} = useSelectionStore.getState();
if (selectedItemsList.length > 0 && selectionMode === item.type) {
setSelectedItem(note.id);
} else {
clearSelection();
}
return;
}
if (selectItem(item)) return;
if (note.conflicted) {
eSendEvent(eShowMergeDialog, note);

View File

@@ -27,18 +27,12 @@ import Navigation from "../../../services/navigation";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { useTrashStore } from "../../../stores/use-trash-store";
import { presentDialog } from "../../dialog/functions";
import SelectionWrapper from "../selection-wrapper";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
export const openNotebook = (item: Notebook | BaseTrashItem<Notebook>) => {
const isTrash = item.type === "trash";
const { selectedItemsList, setSelectedItem, selectionMode, clearSelection } =
useSelectionStore.getState();
if (selectedItemsList.length > 0 && selectionMode === item.type) {
setSelectedItem(item.id);
return;
} else {
clearSelection();
}
if (selectItem(item)) return;
if (isTrash) {
presentDialog({

View File

@@ -22,7 +22,6 @@ import React from "react";
import { View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { SIZE } from "../../../utils/size";
import { Properties } from "../../properties";
import ReminderSheet from "../../sheets/reminder";
@@ -30,7 +29,7 @@ import { IconButton } from "../../ui/icon-button";
import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import SelectionWrapper from "../selection-wrapper";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
const ReminderItem = React.memo(
({
@@ -44,18 +43,7 @@ const ReminderItem = React.memo(
}) => {
const { colors } = useThemeColors();
const openReminder = () => {
const {
selectedItemsList,
setSelectedItem,
selectionMode,
clearSelection
} = useSelectionStore.getState();
if (selectedItemsList.length > 0 && selectionMode === item.type) {
setSelectedItem(item.id);
return;
} else {
clearSelection();
}
if (selectItem(item)) return;
ReminderSheet.present(item, undefined, isSheet);
};

View File

@@ -26,6 +26,23 @@ import { Filler } from "./back-fill";
import { SelectionIcon } from "./selection";
import { Item, TrashItem } from "@notesnook/core";
export function selectItem(item: Item) {
if (useSelectionStore.getState().selectionMode === item.type) {
const { selectionMode, clearSelection, setSelectedItem } =
useSelectionStore.getState();
if (selectionMode === item.type) {
setSelectedItem(item.id);
}
if (useSelectionStore.getState().selectedItemsList.length === 0) {
clearSelection();
}
return true;
}
return false;
}
type SelectionWrapperProps = PropsWithChildren<{
item: Item;
onPress: () => void;

View File

@@ -23,13 +23,12 @@ import React from "react";
import { View } from "react-native";
import { notesnook } from "../../../../e2e/test.ids";
import { TaggedNotes } from "../../../screens/notes/tagged";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { SIZE } from "../../../utils/size";
import { Properties } from "../../properties";
import { IconButton } from "../../ui/icon-button";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import SelectionWrapper from "../selection-wrapper";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
const TagItem = React.memo(
({
@@ -43,18 +42,7 @@ const TagItem = React.memo(
}) => {
const { colors } = useThemeColors();
const onPress = () => {
const {
selectedItemsList,
setSelectedItem,
selectionMode,
clearSelection
} = useSelectionStore.getState();
if (selectedItemsList.length > 0 && selectionMode === item.type) {
setSelectedItem(item.id);
return;
} else {
clearSelection();
}
if (selectItem(item)) return;
TaggedNotes.navigate(item, true);
};

View File

@@ -48,6 +48,7 @@ import { NoteWrapper } from "../list-items/note/wrapper";
import { NotebookWrapper } from "../list-items/notebook/wrapper";
import ReminderItem from "../list-items/reminder";
import TagItem from "../list-items/tag";
import { useIsCompactModeEnabled } from "../../hooks/use-is-compact-mode-enabled";
type ListItemWrapperProps<TItem = Item> = {
group?: GroupingKey;
@@ -75,6 +76,7 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
const refreshTimeout = useRef<NodeJS.Timeout>();
const currentItemId = useRef<string>();
const locked = useRef(false);
const compactMode = useIsCompactModeEnabled(props.dataType as ItemType);
const refreshItem = useCallback((resolvedItem: any) => {
if (!resolvedItem || !resolvedItem.data) {
@@ -117,14 +119,24 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
}
currentItemId.current = resolvedItem.item.id;
setItem(resolvedItem.item);
}
if (resolvedItem?.group) {
setGroupHeader(resolvedItem.group);
} else {
setGroupHeader(undefined);
}
}, []);
if (previousIndex.current !== index) {
previousIndex.current = index;
const resolvedItem = items?.cacheItem(index);
refreshItem(resolvedItem);
if (resolvedItem) {
refreshItem(resolvedItem);
} else {
setItem(undefined);
setGroupHeader(undefined);
}
}
useEffect(() => {
@@ -132,12 +144,15 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
try {
clearTimeout(refreshTimeout.current);
const idx = index;
refreshTimeout.current = setTimeout(async () => {
if (idx !== previousIndex.current) return;
const resolvedItem = await items?.item(idx, resolveItems);
if (idx !== previousIndex.current) return;
refreshItem(resolvedItem);
}, 100);
refreshTimeout.current = setTimeout(
async () => {
if (idx !== previousIndex.current) return;
const resolvedItem = await items?.item(idx, resolveItems);
if (idx !== previousIndex.current) return;
refreshItem(resolvedItem);
},
items?.cacheItem(index) ? 100 : 0
);
} catch (e) {
console.log("Error", e);
}
@@ -148,7 +163,7 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
return (
<View
style={{
height: 120,
height: compactMode ? 50 : 120,
width: "100%"
}}
/>

View File

@@ -313,7 +313,7 @@ const MergeConflicts = () => {
height: height / 2 - (50 + insets.top / 2),
backgroundColor: colors.primary.background,
borderBottomWidth: 1,
borderBottomColor: colors.secondary.background
borderBottomColor: colors.primary.border
}}
>
<ReadonlyEditor

View File

@@ -48,7 +48,7 @@ export const Synced = ({ item, close }) => {
paddingTop: 10,
marginTop: 10,
borderTopWidth: 1,
borderTopColor: colors.secondary.background
borderTopColor: colors.primary.border
}}
>
<Icon

View File

@@ -214,7 +214,7 @@ export const SelectionHeader = React.memo(
size={SIZE.xl}
/>
{renderedInRoute === "Notebooks" ? (
{renderedInRoute === "Notebooks" && selectedItemsList.length ? (
<IconButton
style={{
marginLeft: 10
@@ -231,7 +231,7 @@ export const SelectionHeader = React.memo(
/>
) : null}
{type !== "note" ? null : (
{type !== "note" || !selectedItemsList.length ? null : (
<>
<IconButton
onPress={async () => {
@@ -278,16 +278,14 @@ export const SelectionHeader = React.memo(
</>
)}
{renderedInRoute === "Notebook" ? (
{renderedInRoute === "Notebook" && selectedItemsList.length ? (
<IconButton
onPress={async () => {
if (selectedItemsList.length > 0) {
if (!id) return;
await db.notes.removeFromNotebook(id, ...selectedItemsList);
updateNotebook(id);
Navigation.queueRoutesForUpdate();
clearSelection();
}
if (!id) return;
await db.notes.removeFromNotebook(id, ...selectedItemsList);
updateNotebook(id);
Navigation.queueRoutesForUpdate();
clearSelection();
}}
style={{
marginLeft: 10
@@ -301,7 +299,7 @@ export const SelectionHeader = React.memo(
/>
) : null}
{focusedRouteId === "Favorites" ? (
{focusedRouteId === "Favorites" && selectedItemsList.length ? (
<IconButton
onPress={addToFavorite}
style={{
@@ -315,7 +313,7 @@ export const SelectionHeader = React.memo(
/>
) : null}
{type === "trash" ? null : (
{type === "trash" || !selectedItemsList.length ? null : (
<IconButton
style={{
marginLeft: 10
@@ -337,7 +335,7 @@ export const SelectionHeader = React.memo(
/>
)}
{type === "trash" ? (
{type === "trash" && selectedItemsList.length ? (
<>
<IconButton
style={{

View File

@@ -20,7 +20,7 @@ import { Note } from "@notesnook/core";
import { EVENTS } from "@notesnook/core/dist/common";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect } from "react";
import { Platform, View } from "react-native";
import { View } from "react-native";
import { FlatList } from "react-native-actions-sheet";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../../common/database";
@@ -43,6 +43,7 @@ type TabItem = {
locked?: boolean;
noteLocked?: boolean;
readonly?: boolean;
pinned?: boolean;
};
const TabItemComponent = (props: {
@@ -140,24 +141,50 @@ const TabItemComponent = (props: {
</Paragraph>
</View>
<IconButton
name="close"
size={SIZE.lg}
color={colors.primary.icon}
onPress={() => {
const isLastTab = useTabStore.getState().tabs.length === 1;
useTabStore.getState().removeTab(props.tab.id);
// The last tab is not actually removed, it is just cleaned up.
if (isLastTab) {
editorController.current?.reset(props.tab.id, true, true);
props.close?.();
}
<View
style={{
flexDirection: "row",
gap: 5,
paddingRight: 12
}}
top={0}
left={0}
right={20}
bottom={0}
/>
>
<IconButton
name="pin"
size={SIZE.lg}
color={props.tab.pinned ? colors.primary.accent : colors.primary.icon}
onPress={() => {
useTabStore.getState().updateTab(props.tab.id, {
pinned: !props.tab.pinned,
previewTab: false
});
}}
top={0}
left={0}
right={20}
bottom={0}
/>
{!props.tab?.pinned ? (
<IconButton
name="close"
size={SIZE.lg}
color={colors.primary.icon}
onPress={() => {
const isLastTab = useTabStore.getState().tabs.length === 1;
useTabStore.getState().removeTab(props.tab.id);
// The last tab is not actually removed, it is just cleaned up.
if (isLastTab) {
editorController.current?.reset(props.tab.id, true, true);
props.close?.();
}
}}
top={0}
left={0}
right={20}
bottom={0}
/>
) : null}
</View>
</Pressable>
);
};
@@ -225,7 +252,16 @@ export default function EditorTabs({
/>
</View>
<FlatList windowSize={3} data={tabs} renderItem={renderTabItem} />
<FlatList
windowSize={3}
data={tabs.sort((t1, t2) => {
if (t1.pinned && t2.pinned) return 0;
if (t1.pinned) return -1;
if (t2.pinned) return 1;
return 0;
})}
renderItem={renderTabItem}
/>
</View>
);
}

View File

@@ -141,7 +141,7 @@ Logged in: ${user ? "yes" : "no"}`,
defaultValue={title.current}
style={{
borderWidth: 1,
borderColor: colors.secondary.background,
borderColor: colors.primary.border,
borderRadius: 5,
padding: 12,
fontFamily: "OpenSans-Regular",
@@ -178,7 +178,7 @@ For example:
}}
style={{
borderWidth: 1,
borderColor: colors.secondary.background,
borderColor: colors.primary.border,
borderRadius: 5,
padding: 12,
fontFamily: "OpenSans-Regular",

View File

@@ -159,13 +159,14 @@ export default function Migrate() {
}}
>
Migrating database{progress ? `(${progress?.collection})` : null}
{progress ? `(${progress.current}/${progress.total}) ` : null}...
please wait
{progress ? `(${progress.current}/${progress.total}) ` : null}
... please wait
</Paragraph>
<View
style={{
width: 200
width: 200,
alignSelf: "center"
}}
>
<ProgressBarComponent

View File

@@ -20,8 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { VirtualizedGrouping } from "@notesnook/core";
import { Note, Notebook } from "@notesnook/core/dist/types";
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useEffect, useState } from "react";
import { View } from "react-native";
import React, { RefObject, useEffect, useRef, useState } from "react";
import { ActivityIndicator, TextInput, View } from "react-native";
import { ActionSheetRef } from "react-native-actions-sheet";
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
import { db } from "../../../common/database";
@@ -35,6 +35,7 @@ import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import { Pressable } from "../../ui/pressable";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
@@ -50,39 +51,60 @@ export const MoveNotes = ({
}) => {
const { colors } = useThemeColors();
const currentNotebook = notebook;
const inputRef = useRef<TextInput>(null);
const [loading, setLoading] = useState(false);
const selectionCount = useItemSelectionStore(
(state) =>
Object.keys(state.selection).filter(
(k) =>
state.initialState?.[k] !== "selected" &&
state.selection[k] !== "deselected"
state.selection?.[k] === "selected" ||
state.selection[k] === "deselected"
)?.length > 0
);
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
const loadNotes = React.useCallback(
async (query?: string) => {
setLoading(true);
const notes = query
? db.lookup.notes(query).sorted()
: db.notes?.all.sorted(db.settings.getGroupOptions("notes") as any);
notes
.then(async (notes) => {
await notes.item(0);
setNotes(notes);
setLoading(false);
})
.catch(() => {
setLoading(false);
});
db.relations
.from(currentNotebook, "note")
.get()
.then((existingNotes) => {
const selection: { [name: string]: any } = {};
existingNotes.forEach((rel) => {
selection[rel.toId] = "selected";
});
useItemSelectionStore.setState({
selection: selection,
initialState: selection
});
});
},
[currentNotebook]
);
useEffect(() => {
db.notes?.all.sorted(db.settings.getGroupOptions("notes")).then((notes) => {
setNotes(notes);
});
db.relations
.from(currentNotebook, "note")
.get()
.then((existingNotes) => {
const selection: { [name: string]: any } = {};
existingNotes.forEach((rel) => {
selection[rel.toId] = "selected";
});
useItemSelectionStore.setState({
selection: selection,
initialState: selection
});
});
loadNotes();
return () => {
useItemSelectionStore.getState().reset();
};
}, [currentNotebook]);
}, [currentNotebook, loadNotes]);
const renderItem = React.useCallback(
({ index }: { item: boolean; index: number }) => {
@@ -96,9 +118,7 @@ export const MoveNotes = ({
style={{
paddingHorizontal: 12,
maxHeight: "100%",
height: notes?.placeholders.length
? 50 * (notes?.placeholders.length + 2)
: 300
height: "100%"
}}
>
<Dialog context="local" />
@@ -109,6 +129,22 @@ export const MoveNotes = ({
/>
<Seperator />
<Input
button={{
icon: "magnify",
color: colors.primary.accent,
size: SIZE.lg,
onPress: () => {}
}}
testID="search-input"
fwdRef={inputRef}
autoCapitalize="none"
onChangeText={(v) => {
loadNotes(v && v.trim() === "" ? undefined : v.trim());
}}
placeholder="Search a note"
/>
<FlashList
ListEmptyComponent={
<View
@@ -118,13 +154,17 @@ export const MoveNotes = ({
alignItems: "center"
}}
>
<Paragraph color={colors.secondary.paragraph}>
No notes to show
</Paragraph>
{loading ? (
<ActivityIndicator size="large" color={colors.primary.accent} />
) : (
<Paragraph color={colors.secondary.paragraph}>
No notes to show
</Paragraph>
)}
</View>
}
estimatedItemSize={50}
data={notes?.placeholders}
data={loading ? [] : notes?.placeholders}
renderItem={renderItem}
/>
@@ -135,6 +175,12 @@ export const MoveNotes = ({
currentNotebook.id,
...useItemSelectionStore.getState().getSelectedItemIds()
);
await db.notes?.removeFromNotebook(
currentNotebook.id,
...useItemSelectionStore.getState().getDeselectedItemIds()
);
updateNotebook(currentNotebook.id);
Navigation.queueRoutesForUpdate();
fwdRef?.current?.hide();
@@ -161,11 +207,12 @@ const SelectableNoteItem = React.memo(
const selected = useItemSelectionStore((state) =>
item?.id ? state.selection[item.id] === "selected" : false
);
const exists = useItemSelectionStore((state) =>
item?.id ? state.initialState[item.id] === "selected" : false
);
return exists ? null : (
return (
<Pressable
testID="listitem.select"
onPress={() => {
@@ -205,8 +252,14 @@ const SelectableNoteItem = React.memo(
? "check-circle-outline"
: "checkbox-blank-circle-outline"
}
type="selected"
color={selected ? colors.selected.icon : colors.primary.icon}
type="plain"
color={
selected
? colors.selected.icon
: exists && !selected
? colors.static.red
: colors.primary.icon
}
/>
<View

View File

@@ -17,13 +17,14 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { Platform, View } from "react-native";
import { ScrollView } from "react-native-actions-sheet";
import { getVersion } from "react-native-device-info";
import { features } from "../../../features";
import { eSendEvent, presentSheet } from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { useThemeColors } from "@notesnook/theme";
import { eCloseSheet } from "../../../utils/events";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
@@ -36,7 +37,13 @@ export type FeatureType = {
platform?: "ios" | "android";
};
const NewFeature = ({ features }: { features: FeatureType[] }) => {
const NewFeature = ({
features,
version
}: {
features: FeatureType[];
version?: string | null;
}) => {
const { colors } = useThemeColors();
return (
@@ -44,30 +51,33 @@ const NewFeature = ({ features }: { features: FeatureType[] }) => {
style={{
alignItems: "center",
paddingHorizontal: 12,
paddingTop: 12
paddingTop: 12,
maxHeight: "100%"
}}
>
<Heading color={colors.secondary.heading} size={SIZE.md}>
New Version Highlights 🎉
{!version ? "New version " : `v${version} `}Highlights 🎉
</Heading>
<Seperator />
{features.map((item) => (
<View
key={item.title}
style={{
backgroundColor: colors.secondary.background,
padding: 12,
borderRadius: 10,
width: "100%",
marginBottom: 10
}}
>
<Heading size={SIZE.lg - 2}>{item.title}</Heading>
<Paragraph>{item.body}</Paragraph>
</View>
))}
<ScrollView>
{features.map((item) => (
<View
key={item.title}
style={{
backgroundColor: colors.secondary.background,
padding: 12,
borderRadius: 10,
width: "100%",
marginBottom: 10
}}
>
<Heading size={SIZE.lg - 2}>{item.title}</Heading>
<Paragraph selectable>{item.body}</Paragraph>
</View>
))}
</ScrollView>
<Seperator />
<Button
@@ -108,7 +118,13 @@ NewFeature.present = () => {
);
if (_features.length === 0) return;
presentSheet({
component: <NewFeature features={features} />
component: (
<NewFeature
features={features}
version={SettingsService.getProperty("version")}
/>
),
disableClosing: true
});
return true;
};

View File

@@ -139,7 +139,7 @@ export const NotebookSheet = () => {
currentItem.current = nextRoot;
const snapPoint = NotebookSheetConfig.get({
type: "notebook",
id: nextRoot as string
id: focusedRouteId as string
});
if (ref.current?.isOpen()) {

View File

@@ -159,7 +159,7 @@ export default function ReminderNotify({
? 160 * references?.placeholders?.length
: 500,
borderTopWidth: 1,
borderTopColor: colors.secondary.background,
borderTopColor: colors.primary.border,
marginTop: 5,
paddingTop: 5
}}

View File

@@ -18,7 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useRef, useState } from "react";
import { Platform, TextInput, View } from "react-native";
import {
Platform,
TextInput,
View,
ScrollView as RNScrollView
} from "react-native";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import {
@@ -364,7 +369,7 @@ export default function ReminderSheet({
))}
</View>
<ScrollView showsHorizontalScrollIndicator={false} horizontal>
<RNScrollView showsHorizontalScrollIndicator={false} horizontal>
{recurringMode === RecurringModes.Daily ||
recurringMode === RecurringModes.Year
? null
@@ -425,7 +430,7 @@ export default function ReminderSheet({
}}
/>
))}
</ScrollView>
</RNScrollView>
</View>
) : null}

View File

@@ -48,6 +48,7 @@ import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import Paragraph from "../../ui/typography/paragraph";
import { useUserStore } from "../../../stores/use-user-store";
const RestoreDataSheet = () => {
const [visible, setVisible] = useState(false);
@@ -246,7 +247,6 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
await db.backup.import(backup, password, key);
});
await db.initCollections();
initialize();
refreshAllStores();
ToastManager.show({
heading: "Backup restored successfully.",
@@ -384,10 +384,20 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
return;
}
try {
useUserStore.setState({
disableAppLockRequests: true
});
console.log("disabled...");
const file = await DocumentPicker.pickSingle({
copyTo: "cachesDirectory"
});
setTimeout(() => {
useUserStore.setState({
disableAppLockRequests: false
});
}, 1000);
if (file.name.endsWith(".nnbackupz")) {
setRestoring(true);
await restoreFromZip(file.fileCopyUri, true);
@@ -399,6 +409,11 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
}
} catch (e) {
console.log("error", e.stack);
setTimeout(() => {
useUserStore.setState({
disableAppLockRequests: false
});
}, 1000);
setRestoring(false);
backupError(e);
}
@@ -415,7 +430,7 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
borderRadius: 0,
flexDirection: "row",
borderBottomWidth: 0.5,
borderBottomColor: colors.secondary.background
borderBottomColor: colors.primary.border
}}
>
<View

View File

@@ -127,7 +127,7 @@ const Sort = ({ type, screen }) => {
justifyContent: "flex-start",
flexWrap: "wrap",
borderBottomWidth: isTopicSheet ? 0 : 1,
borderBottomColor: colors.secondary.background,
borderBottomColor: colors.primary.border,
marginBottom: 12,
paddingHorizontal: 12,
paddingBottom: 12,

View File

@@ -170,6 +170,7 @@ export const Update = ({ version: appVersion, fwdRef }) => {
lineHeight: 20,
marginTop: 10
}}
selectable
>
{version.body}
</Paragraph>
@@ -181,6 +182,7 @@ export const Update = ({ version: appVersion, fwdRef }) => {
style={{
marginBottom: 5
}}
selectable
>
{item}
</Paragraph>

View File

@@ -43,6 +43,10 @@ export const ColorSection = React.memo(
]);
const loading = useSettingStore((state) => state.isAppLoading);
const setColorNotes = useMenuStore((state) => state.setColorNotes);
const [order, hiddensItems] = useMenuStore((state) => [
state.order["colors"],
state.hiddenItems["colors"]
]);
useEffect(() => {
if (!loading) {
@@ -59,8 +63,8 @@ export const ColorSection = React.memo(
db.settings.setSideBarHiddenItems("colors", data);
}}
disableDefaultDrag={true}
itemOrder={db.settings.getSideBarOrder("colors")}
hiddenItems={db.settings.getSideBarHiddenItems("colors")}
itemOrder={order}
hiddenItems={hiddensItems}
alwaysBounceVertical={false}
data={colorNotes}
style={{

View File

@@ -27,6 +27,7 @@ import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { DDS } from "../../services/device-detection";
import { eSendEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import { useMenuStore } from "../../stores/use-menu-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
@@ -50,6 +51,10 @@ export const SideMenu = React.memo(
);
const isAppLoading = useSettingStore((state) => state.isAppLoading);
const dragging = useSideBarDraggingStore((state) => state.dragging);
const [order, hiddensItems] = useMenuStore((state) => [
state.order["routes"],
state.hiddenItems["routes"]
]);
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
@@ -93,8 +98,8 @@ export const SideMenu = React.memo(
onHiddenItemsChanged={(data) => {
db.settings.setSideBarHiddenItems("routes", data);
}}
itemOrder={db.settings.getSideBarOrder("routes")}
hiddenItems={db.settings.getSideBarHiddenItems("routes")}
itemOrder={order}
hiddenItems={hiddensItems}
alwaysBounceVertical={false}
data={MenuItemsList}
style={{
@@ -150,26 +155,6 @@ export const SideMenu = React.memo(
subscriptionType === SUBSCRIPTION_STATUS.BASIC ? (
<MenuItem testID={pro.name} key={pro.name} item={pro} index={0} />
) : null}
{BottomItemsList.slice(DDS.isLargeTablet() ? 0 : 1, 3).map(
(item, index) => (
<MenuItem
testID={
item.name == "Night mode"
? notesnook.ids.menu.nightmode
: item.name
}
key={item.name}
item={item}
index={index}
rightBtn={
DDS.isLargeTablet() || item.name === "Notesnook Pro"
? undefined
: BottomItemsList[0]
}
/>
)
)}
</View>
{dragging ? (
@@ -201,14 +186,7 @@ export const SideMenu = React.memo(
/>
</View>
) : (
<View
style={{
width: "100%",
paddingHorizontal: 0
}}
>
<UserStatus />
</View>
<UserStatus />
)}
</View>
</View>

View File

@@ -44,7 +44,7 @@ export const PinnedSection = React.memo(
const menuPins = useMenuStore((state) => state.menuPins);
const loading = useSettingStore((state) => state.isAppLoading);
const setMenuPins = useMenuStore((state) => state.setMenuPins);
const [order] = useMenuStore((state) => [state.order["shortcuts"]]);
useEffect(() => {
if (!loading) {
setMenuPins();
@@ -61,7 +61,13 @@ export const PinnedSection = React.memo(
Navigation.closeDrawer();
});
};
const renderItem = ({ item }: { item: Notebook | Tag; index: number }) => {
const renderItem = ({
item,
index
}: {
item: Notebook | Tag;
index: number;
}) => {
return <PinItem item={item} onPress={onPress} />;
};
@@ -77,7 +83,7 @@ export const PinnedSection = React.memo(
}}
onHiddenItemsChanged={(data) => {}}
canHideItems={false}
itemOrder={db.settings.getSideBarOrder("shortcuts")}
itemOrder={order}
hiddenItems={[]}
alwaysBounceVertical={false}
data={menuPins}

View File

@@ -20,7 +20,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useThemeColors } from "@notesnook/theme";
import { useNetInfo } from "@react-native-community/netinfo";
import React from "react";
import { ActivityIndicator, Image, Platform, View } from "react-native";
import {
ActivityIndicator,
Image,
Platform,
TouchableOpacity,
View
} from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import useSyncProgress from "../../hooks/use-sync-progress";
@@ -33,6 +39,7 @@ import { SIZE } from "../../utils/size";
import { Pressable } from "../ui/pressable";
import { TimeSince } from "../ui/time-since";
import Paragraph from "../ui/typography/paragraph";
import Navigation from "../../services/navigation";
export const UserStatus = () => {
const { colors } = useThemeColors();
@@ -53,7 +60,8 @@ export const UserStatus = () => {
alignSelf: "center",
paddingBottom: Platform.OS === "ios" ? insets.bottom / 2 : null,
borderTopWidth: 1,
borderTopColor: colors.secondary.background
borderTopColor: colors.primary.border,
backgroundColor: colors.primary.background
}}
>
<View
@@ -64,13 +72,9 @@ export const UserStatus = () => {
}}
>
<Pressable
onPress={async () => {
if (user) {
Sync.run();
} else {
tabBarRef.current?.closeDrawer();
eSendEvent(eOpenLoginDialog);
}
onPress={() => {
Navigation.navigate("Settings");
tabBarRef.current.closeDrawer();
}}
type="plain"
style={{
@@ -78,7 +82,8 @@ export const UserStatus = () => {
justifyContent: "flex-start",
padding: 12,
borderRadius: 0,
alignItems: "center"
alignItems: "center",
gap: 10
}}
>
{userProfile?.profilePicture ? (
@@ -89,8 +94,7 @@ export const UserStatus = () => {
style={{
width: 35,
height: 35,
borderRadius: 100,
marginRight: 10
borderRadius: 100
}}
/>
) : null}
@@ -102,9 +106,7 @@ export const UserStatus = () => {
}}
>
<Paragraph
style={{
flexWrap: "wrap"
}}
numberOfLines={1}
size={SIZE.sm}
color={colors.primary.heading}
>
@@ -113,9 +115,7 @@ export const UserStatus = () => {
: lastSyncStatus === SyncStatus.Failed
? "Sync failed, tap to retry"
: syncing
? `Syncing your notes${
progress ? ` (${progress.current})` : ""
}`
? `Syncing ${progress ? `(${progress.current})` : ""}`
: !userProfile?.fullName
? "Tap to sync"
: userProfile.fullName}
@@ -162,25 +162,56 @@ export const UserStatus = () => {
</Paragraph>
</View>
{user ? (
syncing ? (
<ActivityIndicator color={colors.primary.accent} size={SIZE.xl} />
) : lastSyncStatus === SyncStatus.Failed ? (
<Icon
color={colors.error.icon}
name="sync-alert"
size={SIZE.lg}
allowFontScaling
/>
<Pressable
style={{
borderRadius: 100,
width: 40,
height: 40
}}
hitSlop={{
top: 10,
bottom: 10,
left: 20
}}
onPress={() => {
if (user) {
Sync.run();
} else {
tabBarRef.current?.closeDrawer();
eSendEvent(eOpenLoginDialog);
}
}}
>
{user ? (
syncing ? (
<ActivityIndicator
color={colors.primary.accent}
size={SIZE.xl}
/>
) : lastSyncStatus === SyncStatus.Failed ? (
<Icon
color={colors.error.icon}
name="sync-alert"
size={SIZE.lg}
allowFontScaling
/>
) : (
<Icon
allowFontScaling
color={colors.primary.accent}
name="sync"
size={SIZE.lg}
/>
)
) : (
<Icon
allowFontScaling
color={colors.primary.accent}
name="sync"
size={SIZE.lg}
name="login"
/>
)
) : null}
)}
</Pressable>
</Pressable>
</View>
</View>

View File

@@ -121,7 +121,12 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
} else {
if (prevWidths.current?.sidebar !== widths.sidebar) {
translateX.value =
!appState || appState?.movedAway ? widths.sidebar : editorPosition;
!appState || (appState && appState?.movedAway)
? widths.sidebar
: editorPosition;
if (translateX.value === editorPosition) {
onChangeTab?.({ i: 2, from: 1 });
}
}
}
isLoaded.current = true;

View File

@@ -28,6 +28,7 @@ import { Toast } from "../../toast";
import { useAppState } from "../../../hooks/use-app-state";
import SettingsService from "../../../services/settings";
import { useUserStore } from "../../../stores/use-user-store";
import { getContainerBorder } from "../../../utils/colors";
/**
*
@@ -73,9 +74,17 @@ const SheetWrapper = ({
borderTopLeftRadius: 15,
alignSelf: "center",
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0
borderBottomLeftRadius: 0,
...getContainerBorder(colors.primary.border, 0.5),
borderBottomWidth: 0
};
}, [colors.primary.background, largeTablet, smallTablet, width]);
}, [
colors.primary.background,
colors.primary.border,
largeTablet,
smallTablet,
width
]);
const _onOpen = () => {
if (lockEvents.current) return;

View File

@@ -19,4 +19,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { FeatureType } from "./components/sheets/new-feature";
export const features: FeatureType[] = [];
export const features: FeatureType[] = [
{
title: "Bi-directional note linking",
body: "Now you can link notes to each other in both directions."
},
{
title: "Tabs",
body: "Tabs allow you to have multiple notes open at the same time. You can switch between them easily."
},
{
title: "Nested notebooks",
body: "You can now create notebooks inside other notebooks."
},
{
title: "At-rest encryption",
body: "Your notes are now encrypted when stored on the device."
},
{
title: "Material You themed icon",
body: "The app now has a Material You themed icon that changes color based on the wallpaper.",
platform: "android"
},
{
title: "New note quick settings tile",
body: "You can now add a new note tile to quickly create notes from quick settings panel.",
platform: "android"
},
{
title: "And so much more...",
body: "V3 packs so much that it's hard to put everything here, check out our blog at Notesnook V3 is here, and it's packed with new features and improvements such as bi-directional note linking, better syncing, at-rest encryption, nested notebooks, editor tabs, better app lock and so much more. https://blog.notesnook.com/introducing-notesnook-v3."
}
];

View File

@@ -748,41 +748,45 @@ export const useActions = ({
}
async function copyContent() {
if (processingId.current === "copyContent") {
ToastManager.show({
heading: "Please wait...",
message: "We are preparing your note for copy to clipboard",
context: "local"
});
return;
}
if (!checkItemSynced()) return;
if (locked) {
close();
await sleep(300);
openVault({
copyNote: true,
novault: true,
locked: true,
item: item,
title: "Copy note",
description: "Unlock note to copy to clipboard."
});
} else {
processingId.current = "copyContent";
const text = await convertNoteToText(item as Note, true);
if (!text) {
ToastManager.error(new Error(Errors.export("text")));
try {
if (processingId.current === "copyContent") {
ToastManager.show({
heading: "Please wait...",
message: "We are preparing your note for copy to clipboard",
context: "local"
});
return;
}
Clipboard.setString(text);
processingId.current = undefined;
ToastManager.show({
heading: "Note copied to clipboard",
type: "success",
context: "local"
});
if (locked) {
close();
await sleep(300);
openVault({
copyNote: true,
novault: true,
locked: true,
item: item,
title: "Copy note",
description: "Unlock note to copy to clipboard."
});
} else {
processingId.current = "copyContent";
const text = await convertNoteToText(item as Note, true);
if (!text) {
ToastManager.error(new Error(Errors.export("text")));
return;
}
Clipboard.setString(text);
processingId.current = undefined;
ToastManager.show({
heading: "Note copied to clipboard",
type: "success",
context: "local"
});
}
} catch (e) {
console.error(e);
ToastManager.error(e as Error);
}
}

View File

@@ -307,9 +307,6 @@ export const useAppEvents = () => {
initAfterSync();
setLastSynced(await db.lastSynced());
eSendEvent(eCloseSheet, "sync_progress");
useUserStore.setState({
profile: db.settings.getProfile()
});
}, [setLastSynced]);
useEffect(() => {
@@ -682,6 +679,9 @@ export const useAppEvents = () => {
if (db.isInitialized) {
Notifications.setupReminders(true);
if (SettingsService.get().notifNotes) {
Notifications.pinQuickNote(false);
}
useSettingStore.getState().setAppLoading(false);
DatabaseLogger.info("Database initialized");
}

View File

@@ -64,6 +64,7 @@ import { useSettingStore } from "../stores/use-setting-store";
import {
eClearEditor,
eCloseFullscreenEditor,
eOnEnterEditor,
eOnLoadNote,
eOpenFullscreenEditor,
eUnlockNote
@@ -164,9 +165,7 @@ const _TabsHolder = () => {
_deviceMode === "smallTablet"
? dimensions.width -
valueLimiter(dimensions.width * 0.4, 300, 450)
: dimensions.width > 1100
? dimensions.width * 0.55
: dimensions.width * 0.5,
: dimensions.width * 0.48,
zIndex: null,
paddingHorizontal: 0
}
@@ -257,9 +256,7 @@ const _TabsHolder = () => {
position: "relative",
width:
current === "tablet"
? size.width > 1100
? size.width * 0.55
: size.width * 0.5
? size.width * 0.48
: current === "smallTablet"
? size.width - valueLimiter(size.width * 0.4, 300, 450)
: size.width,
@@ -371,15 +368,9 @@ const _TabsHolder = () => {
editor: dimensions.width - valueLimiter(dimensions.width * 0.4, 300, 450)
},
tablet: {
sidebar:
dimensions.width > 1100
? dimensions.width * 0.15
: dimensions.width * 0.2,
sidebar: dimensions.width * 0.22,
list: dimensions.width * 0.3,
editor:
dimensions.width > 1100
? dimensions.width * 0.55
: dimensions.width * 0.5
editor: dimensions.width * 0.48
}
};
@@ -515,11 +506,12 @@ const _TabsHolder = () => {
};
export const TabHolder = React.memo(_TabsHolder, () => true);
const onChangeTab = async (obj) => {
if (obj.i === 2) {
const onChangeTab = async (event) => {
if (event.i === 2) {
editorState().movedAway = false;
editorState().isFocused = true;
activateKeepAwake();
if (!useTabStore.getState().getCurrentNoteId()) {
eSendEvent(eOnLoadNote, {
newNote: true
@@ -529,13 +521,11 @@ const onChangeTab = async (obj) => {
useTabStore.getState().getTab(useTabStore.getState().currentTab).locked
) {
eSendEvent(eUnlockNote);
} else {
editorState().currentlyEditing = true;
eSendEvent(eOnLoadNote);
}
eSendEvent(eOnEnterEditor);
}
} else {
if (obj.from === 2) {
if (event.from === 2) {
deactivateKeepAwake();
editorState().movedAway = true;
editorState().isFocused = false;

View File

@@ -60,6 +60,7 @@ import {
eClearEditor,
eCloseFullscreenEditor,
eEditorTabFocused,
eOnEnterEditor,
eOnLoadNote,
eOpenFullscreenEditor,
eOpenLoginDialog,
@@ -269,14 +270,13 @@ export const useEditorEvents = (
}, [editor, deviceMode, fullscreen]);
const onHardwareBackPress = useCallback(() => {
if (editorState().currentlyEditing) {
if (tabBarRef.current?.page === 2) {
onBackPress();
return true;
}
}, [onBackPress]);
const onLoadNote = useCallback(async () => {
console.log("Loading...");
const onEnterEditor = useCallback(async () => {
InteractionManager.runAfterInteractions(() => {
if (!DDS.isTab) {
handleBack.current = BackHandler.addEventListener(
@@ -331,16 +331,16 @@ export const useEditorEvents = (
}, [fullscreen, onHardwareBackPress]);
useEffect(() => {
eSubscribeEvent(eOnLoadNote + editor.editorId, onLoadNote);
eSubscribeEvent(eOnEnterEditor, onEnterEditor);
eSubscribeEvent(
eClearEditor + editor.editorId,
onClearEditorSessionRequest
);
return () => {
eUnSubscribeEvent(eClearEditor, onClearEditorSessionRequest);
eUnSubscribeEvent(eOnLoadNote, onLoadNote);
eUnSubscribeEvent(eOnEnterEditor, onEnterEditor);
};
}, [editor.editorId, onClearEditorSessionRequest, onLoadNote]);
}, [editor.editorId, onClearEditorSessionRequest, onEnterEditor]);
const onMessage = useCallback(
async (event: WebViewMessageEvent) => {

View File

@@ -76,6 +76,7 @@ export type TabItem = {
readonly?: boolean;
locked?: boolean;
noteLocked?: boolean;
pinned?: boolean;
};
const history = new History();

View File

@@ -29,7 +29,6 @@ import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store";
import { useNotes } from "../../stores/use-notes-store";
import { openEditor } from "../notes/common";
import LinkNote from "../../components/sheets/link-note";
export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
const [notes, loading] = useNotes();
@@ -80,7 +79,9 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
loading: "Loading your notes"
}}
/>
<FloatingButton title="Create a new note" onPress={openEditor} />
{!notes || !notes.placeholders?.length ? null : (
<FloatingButton title="Create a new note" onPress={openEditor} />
)}
</DelayLayout>
</>
);

View File

@@ -65,7 +65,6 @@ export const Reminders = ({
route: route.name
});
}}
isBeta
id={route.name}
onPressDefaultRightButton={() => {
ReminderSheet.present();

View File

@@ -19,11 +19,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Item, Note, VirtualizedGrouping } from "@notesnook/core";
import React, { useEffect, useRef, useState } from "react";
import { db } from "../../common/database";
import { DatabaseLogger, db } from "../../common/database";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import {
ToastManager,
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
@@ -37,6 +38,7 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
const [loading, setLoading] = useState(false);
const [searchStatus, setSearchStatus] = useState<string>();
const currentQuery = useRef<string>();
const timer = useRef<NodeJS.Timeout>();
const isFocused = useNavigationFocus(navigation, {
onFocus: (prev) => {
useNavigationStore.getState().setFocusedRouteId(route.name);
@@ -68,6 +70,8 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
console.log(
`Found ${results.placeholders?.length} results for ${query}`
);
if (currentQuery.current !== query) return;
await results.item(0);
setResults(results);
if (results.placeholders?.length === 0) {
setSearchStatus(`No results found for ${query}`);
@@ -76,7 +80,8 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
}
setLoading(false);
} catch (e) {
console.log(e);
ToastManager.error(e as Error);
DatabaseLogger.error(e);
}
},
[route.params?.items, route.params.type]
@@ -84,12 +89,11 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
useEffect(() => {
const onRefreshSearch = (type: string) => {
if (type === route.params?.type) {
if (type === undefined || type === route.params?.type) {
onSearch(currentQuery.current);
}
};
eSubscribeEvent(eOnRefreshSearch, onRefreshSearch);
return () => {
eUnSubscribeEvent(eOnRefreshSearch, onRefreshSearch);
};
@@ -103,18 +107,26 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
type={route.params?.type}
renderedInRoute={route.name}
/>
<SearchBar onChangeText={onSearch} loading={loading} />
<SearchBar
onChangeText={(query) => {
clearTimeout(timer.current);
timer.current = setTimeout(() => {
onSearch(query);
}, 300);
}}
loading={loading}
/>
<List
data={results}
dataType={route.params?.type}
renderedInRoute={route.name}
loading={false}
loading={loading}
placeholder={{
title: route.name,
paragraph:
searchStatus ||
`Type a keyword to search in ${route.params?.title}`,
loading: "Searching..."
loading: `Searching for ${currentQuery.current}...`
}}
/>
</>

View File

@@ -77,7 +77,7 @@ const AppLock = () => {
alignSelf: "center",
minHeight: 125,
borderBottomWidth: 1,
borderBottomColor: colors.secondary.background
borderBottomColor: colors.primary.border
}}
>
<Icon

View File

@@ -100,7 +100,7 @@ export default function DebugLogs() {
backgroundColor: background,
flexShrink: 1,
borderBottomWidth: 1,
borderBottomColor: colors.secondary.background
borderBottomColor: colors.primary.border
}}
>
<Paragraph

View File

@@ -49,7 +49,7 @@ export const Licenses = () => {
alignSelf: "flex-start",
padding: 12,
borderBottomWidth: 1,
borderBottomColor: colors.secondary.background,
borderBottomColor: colors.primary.border,
borderRadius: 0
}}
onPress={() => {

View File

@@ -456,7 +456,7 @@ export const settingsGroups: SettingSection[] = [
},
{
id: "background-sync",
name: "Background sync (Experimental)",
name: "Background sync (experimental)",
description:
"Periodically wake up the app in background to sync your notes from other devices.",
type: "switch",

View File

@@ -104,7 +104,7 @@ const onChangePicture = () => {
const SettingsUserSection = ({ item }) => {
const { colors } = useThemeColors();
const user = useUserStore((state) => state.user);
const [user, premium] = useUserStore((state) => [state.user, state.premium]);
const lastSynced = useUserStore((state) => state.lastSynced);
const lastSyncStatus = useUserStore((state) => state.lastSyncStatus);
const { isInternetReachable } = useNetInfo();
@@ -121,7 +121,7 @@ const SettingsUserSection = ({ item }) => {
paddingTop: 50,
borderBottomWidth: 1,
paddingBottom: 20,
borderColor: colors.secondary.background
borderColor: colors.primary.border
}}
>
<View
@@ -172,17 +172,20 @@ const SettingsUserSection = ({ item }) => {
alignItems: "center"
}}
>
<Heading color={colors.primary.accent} size={SIZE.sm}>
{SUBSCRIPTION_STATUS_STRINGS[
user.subscription?.type
]?.toUpperCase() || "Basic"}
</Heading>
{premium ? (
<Heading color={colors.primary.accent} size={SIZE.sm}>
{SUBSCRIPTION_STATUS_STRINGS[
user.subscription?.type
]?.toUpperCase() || "Basic"}
</Heading>
) : null}
<Paragraph
onPress={() => {
presentDialog({
title: "Set name",
paragraph: "Set your full name",
title: "Set your full name",
paragraph:
"Your name is end-to-end encrypted and only visible to you.",
positiveText: "Save",
input: true,
inputPlaceholder: "Enter your full name",

View File

@@ -22,7 +22,7 @@ import {
endBackgroundTask
} from "react-native-begin-background-task";
import BackgroundFetch from "@ammarahmed/react-native-background-fetch";
import { DatabaseLogger, db } from "../common/database";
import { DatabaseLogger, db, setupDatabase } from "../common/database";
import { AppState, AppRegistry } from "react-native";
import Notifications from "./notifications";
import SettingsService from "./settings";
@@ -110,11 +110,15 @@ BackgroundFetch.registerHeadlessTask(task);
async function onBackgroundSyncStarted() {
try {
if (!db.isInitialized) {
await setupDatabase();
await db.init();
}
const user = await db.user?.getUser();
if (user) {
await db.sync(true, false);
await db.sync({
type: "full",
force: false
});
}
await Notifications.setupReminders();
DatabaseLogger.info("BACKGROUND SYNC COMPLETE");

View File

@@ -252,7 +252,7 @@ async function run(progress = false, context) {
} catch (e) {
ToastManager.error(e, "Backup failed", context || "global");
if (e.message.includes("android.net.Uri") && androidBackupDirectory) {
if (e?.message?.includes("android.net.Uri") && androidBackupDirectory) {
SettingsService.setProperty("backupDirectoryAndroid", null);
return run(progress, context);
}

View File

@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { db } from "../common/database";
import Navigation from "../services/navigation";
import Notifications from "../services/notifications";
import { useFavoriteStore } from "./use-favorite-store";
@@ -27,6 +28,7 @@ import { useRelationStore } from "./use-relation-store";
import { useReminderStore } from "./use-reminder-store";
import { useTagStore } from "./use-tag-store";
import { useTrashStore } from "./use-trash-store";
import { useUserStore } from "./use-user-store";
export function initAfterSync() {
Navigation.queueRoutesForUpdate();
@@ -36,6 +38,9 @@ export function initAfterSync() {
useRelationStore.getState().update();
useMenuStore.getState().setColorNotes();
useMenuStore.getState().setMenuPins();
useUserStore.setState({
profile: db.settings.getProfile()
});
}
export async function initialize() {}

View File

@@ -32,6 +32,7 @@ export interface SelectionStore extends State {
reset: () => void;
enabled?: boolean;
getSelectedItemIds: () => string[];
getDeselectedItemIds: () => string[];
}
export function createItemSelectionStore(
@@ -84,6 +85,16 @@ export function createItemSelectionStore(
}
}
return selected;
},
getDeselectedItemIds: () => {
const deselected: string[] = [];
if (!get().selection) return deselected;
for (const item in get().selection) {
if (get().selection[item] === "deselected") {
deselected.push(item);
}
}
return deselected;
}
}));
}

View File

@@ -17,7 +17,13 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Color, Notebook, Tag } from "@notesnook/core/dist/types";
import {
Color,
Notebook,
SideBarHideableSection,
SideBarSection,
Tag
} from "@notesnook/core/dist/types";
import create, { State } from "zustand";
import { db } from "../common/database";
@@ -29,17 +35,56 @@ export interface MenuStore extends State {
clearAll: () => void;
loadingShortcuts: boolean;
loadingColors: boolean;
order: Record<SideBarSection, string[]>;
hiddenItems: Record<SideBarHideableSection, string[]>;
}
export const useMenuStore = create<MenuStore>((set) => ({
export const useMenuStore = create<MenuStore>((set, get) => ({
menuPins: [],
colorNotes: [],
loadingShortcuts: true,
loadingColors: true,
order: {
colors: [],
shortcuts: [],
routes: []
},
hiddenItems: {
colors: [],
routes: []
},
setMenuPins: () => {
db.shortcuts.resolved().then((shortcuts) => {
set({ menuPins: [...(shortcuts as [])], loadingShortcuts: false });
});
const sections = ["colors", "shortcuts", "routes"];
const order: Record<SideBarSection, string[]> = {
colors: [],
shortcuts: [],
routes: []
};
const hiddenItems: Record<SideBarHideableSection, string[]> = {
colors: [],
routes: []
};
for (const section of sections) {
order[section as SideBarSection] = db.settings.getSideBarOrder(
section as SideBarSection
);
hiddenItems[section as SideBarHideableSection] =
db.settings.getSideBarHiddenItems("colors");
}
if (
JSON.stringify(get().order || {}) !== JSON.stringify(order || {}) ||
JSON.stringify(get().hiddenItems || {}) !==
JSON.stringify(hiddenItems || {})
) {
set({
order: order,
hiddenItems: hiddenItems
});
}
},
setColorNotes: () => {
db.colors?.all

View File

@@ -168,7 +168,8 @@ export const defaultSettings: SettingStore["settings"] = {
darkTheme: ThemeDark,
markdownShortcuts: true,
biometricsAuthEnabled: false,
appLockHasPasswordSecurity: false
appLockHasPasswordSecurity: false,
backgroundSync: true
};
export const useSettingStore = create<SettingStore>((set, get) => ({

View File

@@ -172,3 +172,4 @@ export const eOnChangeFluidTab = "617";
export const eUnlockWithBiometrics = "618";
export const eUnlockWithPassword = "619";
export const eUpdateNoteInEditor = "620";
export const eOnEnterEditor = "621";

View File

@@ -47,8 +47,7 @@ export const MenuItemsList = [
id: "reminders",
name: "Reminders",
icon: "bell",
close: true,
isBeta: true
close: true
},
{
id: "monographs",

View File

@@ -28,7 +28,9 @@ import {
notVisibleById,
sleep,
exitEditor,
tapByText
tapByText,
elementByText,
elementById
} from "./utils";
describe("NOTE TESTS", () => {
@@ -40,15 +42,15 @@ describe("NOTE TESTS", () => {
it("Open and close a note", async () => {
await prepare();
await createNote();
await tapById(notesnook.ids.note.get(1));
await tapById(notesnook.ids.note.get(0));
await exitEditor();
});
it("Notes properties should show", async () => {
it.only("Notes properties should show", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await visibleByText("Created at:");
await waitFor(elementByText("Created at:")).toBeVisible().withTimeout(500);
});
it("Favorite and unfavorite a note", async () => {
@@ -94,29 +96,27 @@ describe("NOTE TESTS", () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await sleep(1000);
await waitFor(elementById("icon-copy")).toBeVisible().withTimeout(500);
await tapById("icon-copy");
await visibleByText("Note copied to clipboard");
});
it("Export note dialog should show", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-export");
await visibleByText("PDF");
});
it("Assign colors to a note", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await tapById(notesnook.ids.dialogs.actionsheet.color("red"));
await sleep(1000);
await tapByText("Add color");
await sleep(500);
await elementById("color-title-input").typeText("Test color");
await tapByText("Add color");
await sleep(3000);
await visibleById("icon-check");
await tapById(notesnook.ids.dialogs.actionsheet.color("red"));
await tapById("icon-color-#efefef");
await notVisibleById("icon-check");
await tapById(notesnook.ids.dialogs.actionsheet.color("green"));
await tapById("icon-color-#efefef");
await device.pressBack();
await navigate("Green");
await navigate("Test color");
await visibleByText(note.body);
});
@@ -124,12 +124,15 @@ describe("NOTE TESTS", () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await sleep(500);
await tapById("icon-trash");
await navigate("Trash");
await sleep(500);
await tapById(notesnook.listitem.menu);
await sleep(500);
await tapByText("Restore note");
await device.pressBack();
await sleep(500);
await visibleByText(
"Test note description that is very long and should not fit in text."
);

View File

@@ -85,7 +85,9 @@ async function createNote(_title, _body) {
await webview.element(by.web.className("ProseMirror")).tap();
await webview.element(by.web.className("ProseMirror")).typeText(body, true);
await exitEditor();
await expect(element(by.text(body))).toBeVisible();
await waitFor(element(by.text(body)))
.toBeVisible()
.withTimeout(500);
return { title, body };
}

View File

@@ -111,7 +111,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 3006
versionCode 3008
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -52,7 +52,6 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/BootTheme"
android:largeHeap="true"
android:supportsRtl="false"
@@ -141,6 +140,17 @@
<service android:name="com.streetwriters.notesnook.BootTaskService" />
<service
android:name=".NotesnookTileService"
android:icon="@drawable/add_note"
android:label="New note"
android:exported="true"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE"/>
</intent-filter>
</service>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
@@ -167,6 +177,8 @@
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
</application>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,19 @@
package com.streetwriters.notesnook;
import android.content.Intent;
import android.os.Build;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.N)
public class NotesnookTileService extends TileService {
@Override
public void onClick() {
super.onClick();
Intent intent = new Intent(this.getApplicationContext(), ShareActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityAndCollapse(intent);
}
}

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
<group android:scaleX="0.12527233"
android:scaleY="0.12527233"
android:translateX="5.3605666"
android:translateY="-1.5294118">
<group android:translateY="151.2">
<path android:pathData="M95.904,0L75.744,0L30.096,-68.976L30.096,0L9.936,0L9.936,-100.656L30.096,-100.656L75.744,-31.536L75.744,-100.656L95.904,-100.656Z"
android:fillColor="#000000"/>
</group>
</group>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 B

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,16h-3v3h-2v-3L8,16v-2h3v-3h2v3h3v2zM13,9L13,3.5L18.5,9L13,9z"/>
</vector>

View File

@@ -1,74 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
</adaptive-icon>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More