mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 03:29:21 +02:00
Compare commits
23 Commits
fix-releas
...
release/v3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f97e54afa | ||
|
|
8205aafe65 | ||
|
|
8e4e9c9e4c | ||
|
|
a3c2d50d64 | ||
|
|
41a6cfaa29 | ||
|
|
96ca8598ae | ||
|
|
c9cd7d1fad | ||
|
|
0c2781ee32 | ||
|
|
97c5fd881f | ||
|
|
5a636f94df | ||
|
|
edfdf84e46 | ||
|
|
5bc16a8e3a | ||
|
|
2a6db1b06f | ||
|
|
f3193ba3bc | ||
|
|
7f883da94c | ||
|
|
e6385a09ae | ||
|
|
46bf058bfc | ||
|
|
74691f299f | ||
|
|
a653f28b44 | ||
|
|
37d364d893 | ||
|
|
69aea1a909 | ||
|
|
10044162e0 | ||
|
|
2c858a1310 |
2
.github/workflows/android.publish.yml
vendored
2
.github/workflows/android.publish.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
ccache -p
|
||||
|
||||
- name: Cache gradle
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
|
||||
7
.github/workflows/ios.publish.yml
vendored
7
.github/workflows/ios.publish.yml
vendored
@@ -4,7 +4,7 @@ on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
@@ -14,6 +14,11 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: "16.1"
|
||||
|
||||
- name: Install node modules
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
|
||||
@@ -58,10 +58,10 @@ This will compile and run the app in production mode but it won't generate any p
|
||||
npm run release -- --rebuild
|
||||
|
||||
# For macOS
|
||||
npx electron-builder --config=electron-builder.config.js --mac dmg --arm64 --x64 -publish never
|
||||
npx electron-builder --config=electron-builder.config.js --mac dmg --arm64 --x64 --publish never
|
||||
|
||||
# For Linux (AppImage)
|
||||
npx electron-builder --config=electron-builder.config.js --linux AppImage:x64 AppImage:arm64 -publish never
|
||||
npx electron-builder --config=electron-builder.config.js --linux AppImage:x64 AppImage:arm64 --publish never
|
||||
|
||||
# For Windows
|
||||
npx electron-builder --config=electron-builder.config.js --win --publish never
|
||||
|
||||
4
apps/desktop/package-lock.json
generated
4
apps/desktop/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
|
||||
@@ -302,7 +302,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
const gesture = Gesture.Pan()
|
||||
.maxPointers(1)
|
||||
.enabled(enabled && !disabled)
|
||||
.activeOffsetX([-10, 10])
|
||||
.activeOffsetX([-5, 5])
|
||||
.failOffsetY([-10, 10])
|
||||
.onBegin((event) => {
|
||||
locked.value = false;
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function List(props: ListProps) {
|
||||
? "home"
|
||||
: props.renderedInRoute === "Favorites"
|
||||
? "favorites"
|
||||
: props.renderedInRoute === "Trash"
|
||||
: props.renderedInRoute === "Trash" || props.dataType === "trash"
|
||||
? "trash"
|
||||
: `${props.dataType}s`;
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ const Line = ({ top = 6, bottom = 6 }) => {
|
||||
|
||||
export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const isColor = !!ColorValues[item.title];
|
||||
if (!item || !item.id) {
|
||||
return (
|
||||
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
||||
|
||||
@@ -56,7 +56,7 @@ const Sort = ({
|
||||
db.settings.getGroupOptions(
|
||||
screen === "Notes"
|
||||
? "home"
|
||||
: screen === "Trash"
|
||||
: screen === "Trash" || type === "trash"
|
||||
? "trash"
|
||||
: ((type + "s") as GroupingKey)
|
||||
)
|
||||
@@ -65,10 +65,11 @@ const Sort = ({
|
||||
const groupType =
|
||||
screen === "Notes"
|
||||
? "home"
|
||||
: screen === "Trash"
|
||||
: screen === "Trash" || type === "trash"
|
||||
? "trash"
|
||||
: screen === "Favorites"
|
||||
? "favorites"
|
||||
: ((type + "s") as GroupingKey);
|
||||
console.log("updateGroupOptions for group", groupType, "in", screen);
|
||||
await db.settings.setGroupOptions(groupType, _groupOptions);
|
||||
setGroupOptions(_groupOptions);
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -19,13 +19,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { FeatureType } from "./components/sheets/new-feature";
|
||||
|
||||
export const features: FeatureType[] = [
|
||||
{
|
||||
title: "Notebook Tree View",
|
||||
body: "Notebooks are now displayed in a tree view for better organization and user experience."
|
||||
},
|
||||
{
|
||||
title: "New SideBar",
|
||||
body: "Sidebar now contains tabs for Notebooks and Tags for easy navigation."
|
||||
}
|
||||
];
|
||||
export const features: FeatureType[] = [];
|
||||
|
||||
@@ -32,7 +32,7 @@ export function useGroupOptions(type: any) {
|
||||
const onUpdate = (groupType: string) => {
|
||||
if (groupType !== type) return;
|
||||
const options = db.settings?.getGroupOptions(type) as any;
|
||||
|
||||
if (!options) return;
|
||||
if (
|
||||
groupOptions?.groupBy !== options.groupBy ||
|
||||
groupOptions?.sortBy !== options.sortBy ||
|
||||
|
||||
@@ -177,7 +177,7 @@ export const useEditorEvents = (
|
||||
useEffect(() => {
|
||||
const handleKeyboardDidShow: KeyboardEventListener = () => {
|
||||
editor.commands.keyboardShown(true);
|
||||
//editor.postMessage(NativeEvents.keyboardShown, undefined);
|
||||
editor.postMessage(NativeEvents.keyboardShown, undefined);
|
||||
};
|
||||
const handleKeyboardDidHide: KeyboardEventListener = () => {
|
||||
editor.commands.keyboardShown(false);
|
||||
|
||||
@@ -352,7 +352,7 @@ export const useEditor = (
|
||||
DatabaseLogger.log(`Note saved: ${id}...`);
|
||||
|
||||
clearTimeout(saveTimer);
|
||||
|
||||
const oldNote = currentNotes.current[id];
|
||||
if (id) {
|
||||
currentNotes.current[id] = await db.notes?.note(id);
|
||||
}
|
||||
@@ -376,11 +376,16 @@ export const useEditor = (
|
||||
}
|
||||
}
|
||||
|
||||
postMessage(
|
||||
NativeEvents.title,
|
||||
currentNotes.current[id]?.title,
|
||||
tabId
|
||||
);
|
||||
if (
|
||||
oldNote?.title !== currentNotes.current[id]?.title &&
|
||||
!noteData.title
|
||||
) {
|
||||
postMessage(
|
||||
NativeEvents.title,
|
||||
currentNotes.current[id]?.title,
|
||||
tabId
|
||||
);
|
||||
}
|
||||
|
||||
if (Notifications.isNotePinned(id as string)) {
|
||||
Notifications.pinNote(id as string);
|
||||
|
||||
@@ -121,7 +121,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3049
|
||||
versionCode 3050
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" tools:node="remove" />
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
- v3.1.0 brings new features and improvements to enhance your Notesnook experience.
|
||||
- Notebooks and Tags are not located in the Sidebar
|
||||
- Tree view for notebooks
|
||||
- Improved list view UI for better user experience
|
||||
- Improved notebook related actions by integrating tree view in most places
|
||||
- You can set any notebook, tag or colors as home screen
|
||||
- Bug fixes
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -1063,7 +1063,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2133;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1137,7 +1137,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.1.1;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1168,7 +1168,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2133;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1242,7 +1242,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.1.1;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1401,7 +1401,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2133;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1413,7 +1413,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.1.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1444,7 +1444,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2133;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1457,7 +1457,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.1.1;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1487,7 +1487,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2133;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1561,7 +1561,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.1.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1592,7 +1592,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2132;
|
||||
CURRENT_PROJECT_VERSION = 2133;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1667,7 +1667,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.0;
|
||||
MARKETING_VERSION = 3.1.1;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -56,4 +56,4 @@
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
4
apps/web/package-lock.json
generated
4
apps/web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.1",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.tabsScroll,
|
||||
.titlebarLogo,
|
||||
.theme-scope-titleBar,
|
||||
.navigation-menu-header,
|
||||
.route-container-header {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
@@ -164,8 +164,11 @@ function DesktopAppContents() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isListPaneVisible) {
|
||||
navPane.current?.expand(1);
|
||||
navPane.current?.reset(1);
|
||||
if (navPane.current?.hasExpandedSize(1)) {
|
||||
navPane.current?.expand(1);
|
||||
} else {
|
||||
navPane.current?.reset(1);
|
||||
}
|
||||
} else {
|
||||
navPane.current?.collapse(1);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ function TitleBox(props: TitleBoxProps) {
|
||||
|
||||
withSelectionPersist(inputRef.current, (input) => {
|
||||
input.value = title || "";
|
||||
requestAnimationFrame(() => resizeTextarea(input));
|
||||
setTimeout(() => resizeTextarea(input));
|
||||
});
|
||||
}, [sessionType, id, sessionTitle]);
|
||||
|
||||
|
||||
@@ -272,7 +272,9 @@ function NavigationMenu({ onExpand }: { onExpand?: () => void }) {
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
className="navigation-menu-header"
|
||||
sx={{
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 1
|
||||
|
||||
@@ -62,6 +62,7 @@ export type SplitPaneImperativeHandle = {
|
||||
expand: (index: number) => void;
|
||||
reset: (index: number) => void;
|
||||
isCollapsed: (index: number) => boolean;
|
||||
hasExpandedSize: (index: number) => boolean;
|
||||
};
|
||||
export const SplitPane = React.forwardRef<
|
||||
SplitPaneImperativeHandle,
|
||||
@@ -281,8 +282,9 @@ export const SplitPane = React.forwardRef<
|
||||
const currentPane = paneSizes.current[index];
|
||||
if (!currentPane || !currentPane.collapsed) return;
|
||||
currentPane.collapsed = false;
|
||||
currentPane.size =
|
||||
currentPane.expandedSize ?? currentPane.initialSize;
|
||||
currentPane.size = !!currentPane.expandedSize
|
||||
? currentPane.expandedSize
|
||||
: currentPane.initialSize;
|
||||
currentPane.expandedSize = undefined;
|
||||
setSizes(paneSizes.current, wrapSize.current);
|
||||
},
|
||||
@@ -296,6 +298,9 @@ export const SplitPane = React.forwardRef<
|
||||
},
|
||||
isCollapsed: (index: number) => {
|
||||
return paneSizes.current[index].collapsed;
|
||||
},
|
||||
hasExpandedSize: (index: number) => {
|
||||
return paneSizes.current[index].expandedSize !== undefined;
|
||||
}
|
||||
};
|
||||
},
|
||||
@@ -375,6 +380,19 @@ export const SplitPane = React.forwardRef<
|
||||
nextPane.nextSize = undefined;
|
||||
currentPane.nextSize = undefined;
|
||||
|
||||
[currentPane, nextPane].forEach((pane) => {
|
||||
if (pane.size === 0) {
|
||||
// if pane size is 0, collapsed should be true
|
||||
pane.collapsed = true;
|
||||
}
|
||||
if (pane.min === 0 && pane.size < pane.snap) {
|
||||
// if pane min is 0, and size is less than snap, collapsed should be true
|
||||
pane.collapsed = true;
|
||||
// and size should be set to 0
|
||||
pane.size = 0;
|
||||
}
|
||||
});
|
||||
|
||||
setSizes(paneSizes.current, wrapSize.current);
|
||||
},
|
||||
[paneSizes, setSizes, splitAxis]
|
||||
|
||||
@@ -39,6 +39,26 @@
|
||||
<title>Notesnook</title>
|
||||
|
||||
<style id="theme-colors"></style>
|
||||
<script>
|
||||
(async function validateServiceWorkerTrack() {
|
||||
const cookie = document.cookie.match(/release\-track=([^;]+)/);
|
||||
const releaseTrack = cookie ? cookie[1] : "stable";
|
||||
const expectedPrefix =
|
||||
releaseTrack === "beta" ? "notesnook-beta" : "notesnook";
|
||||
|
||||
const cacheKeys = await caches.keys();
|
||||
const mismatched = cacheKeys.some(
|
||||
(key) => !key.startsWith(expectedPrefix)
|
||||
);
|
||||
|
||||
if (mismatched) {
|
||||
const regs = await navigator.serviceWorker.getRegistrations();
|
||||
await Promise.all(regs.map((r) => r.unregister()));
|
||||
await Promise.all(cacheKeys.map((key) => caches.delete(key)));
|
||||
window.location.reload();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" src="./index.ts"></script>
|
||||
<style>
|
||||
html {
|
||||
|
||||
@@ -19,18 +19,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/* eslint-disable no-var */
|
||||
/// <reference lib="webworker" />
|
||||
|
||||
import { ExpirationPlugin } from "workbox-expiration";
|
||||
import {
|
||||
precacheAndRoute,
|
||||
createHandlerBoundToURL,
|
||||
cleanupOutdatedCaches
|
||||
} from "workbox-precaching";
|
||||
import { setCacheNameDetails } from "workbox-core";
|
||||
import { registerRoute } from "workbox-routing";
|
||||
import { StaleWhileRevalidate } from "workbox-strategies";
|
||||
import "./service-worker.dev.js";
|
||||
|
||||
declare var self: ServiceWorkerGlobalScope & typeof globalThis;
|
||||
|
||||
setCacheNameDetails({
|
||||
prefix: IS_BETA ? "notesnook-beta" : "notesnook",
|
||||
suffix: `${self.registration.scope}-${APP_VERSION}-${GIT_HASH}`,
|
||||
precache: "precache",
|
||||
runtime: "runtime"
|
||||
});
|
||||
|
||||
cleanupOutdatedCaches();
|
||||
|
||||
precacheAndRoute(self.__WB_MANIFEST);
|
||||
@@ -59,19 +65,3 @@ registerRoute(
|
||||
},
|
||||
createHandlerBoundToURL(PUBLIC_URL + "/index.html")
|
||||
);
|
||||
|
||||
// An example runtime caching route for requests that aren't handled by the
|
||||
// precache, in this case same-origin .png requests like those from in public/
|
||||
registerRoute(
|
||||
// Add in any other file extensions or routing criteria as needed.
|
||||
({ url }) =>
|
||||
url.origin === self.location.origin && url.pathname.endsWith(".png"), // Customize this strategy as needed, e.g., by changing to CacheFirst.
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: "images",
|
||||
plugins: [
|
||||
// Ensure that once this runtime cache reaches a maximum size the
|
||||
// least-recently used images are removed.
|
||||
new ExpirationPlugin({ maxEntries: 50 })
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
@@ -73,13 +73,19 @@ export async function installUpdate() {
|
||||
else {
|
||||
const registrations =
|
||||
(await navigator.serviceWorker?.getRegistrations()) || [];
|
||||
let reload = false;
|
||||
for (const registration of registrations) {
|
||||
if (registration.waiting) {
|
||||
registration.waiting.addEventListener("statechange", () => {
|
||||
const worker =
|
||||
registration.active ||
|
||||
registration.waiting ||
|
||||
registration.installing;
|
||||
if (worker?.state === "activated") {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
registration.waiting.postMessage({ type: "SKIP_WAITING" });
|
||||
reload = true;
|
||||
}
|
||||
}
|
||||
if (reload) window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
3
fastlane/metadata/android/en-US/changelogs/15254.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/15254.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
- Bug fixes
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -134,7 +134,7 @@ test("changing content shouldn't reset the note title ", () =>
|
||||
expect(note?.title).toBe("I am a note");
|
||||
}));
|
||||
|
||||
test("note title with headline format should keep generating headline until title is edited", () =>
|
||||
test("note title with headline format should keep generating headline title until title is edited", () =>
|
||||
noteTest().then(async ({ db }) => {
|
||||
await db.settings.setTitleFormat("$headline$");
|
||||
const id = await db.notes.add({
|
||||
@@ -191,19 +191,44 @@ test("note title with headline format should keep generating headline until titl
|
||||
"headlineLorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam rutrum ex ac eros egestas, ut rhoncus felis faucibus. Mauris tempor orci nisl,"
|
||||
]
|
||||
].forEach(([testCase, content, expectedHeadline]) => {
|
||||
test(`note should generate headline up to HEADLINE_CHARACTER_LIMIT characters - ${testCase}`, () =>
|
||||
noteTest({
|
||||
...TEST_NOTE,
|
||||
content: {
|
||||
type: TEST_NOTE.content.type,
|
||||
data: content
|
||||
}
|
||||
}).then(async ({ db, id }) => {
|
||||
test(`note title with headline format should generate headline title up to 150 characters - ${testCase}`, () =>
|
||||
noteTest().then(async ({ db }) => {
|
||||
await db.settings.setTitleFormat("$headline$");
|
||||
const id = await db.notes.add({
|
||||
content: {
|
||||
type: TEST_NOTE.content.type,
|
||||
data: content
|
||||
}
|
||||
});
|
||||
const note = await db.notes.note(id);
|
||||
expect(note?.headline).toBe(expectedHeadline);
|
||||
expect(note?.title).toBe(expectedHeadline);
|
||||
}));
|
||||
});
|
||||
|
||||
test("note should get headline from first paragraph in content", () =>
|
||||
noteTest({
|
||||
...TEST_NOTE,
|
||||
content: {
|
||||
type: TEST_NOTE.content.type,
|
||||
data: "<p>This is a very colorful existence.</p>"
|
||||
}
|
||||
}).then(async ({ db, id }) => {
|
||||
const note = await db.notes.note(id);
|
||||
expect(note?.headline).toBe("This is a very colorful existence.");
|
||||
}));
|
||||
|
||||
test("note should not get headline if there is no p tag", () =>
|
||||
noteTest({
|
||||
...TEST_NOTE,
|
||||
content: {
|
||||
type: TEST_NOTE.content.type,
|
||||
data: `<ol style="list-style-type: decimal;" data-mce-style="list-style-type: decimal;"><li>Hello I won't be a headline :(</li><li>Me too.</li><li>Gold.</li></ol>`
|
||||
}
|
||||
}).then(async ({ db, id }) => {
|
||||
const note = await db.notes.note(id);
|
||||
expect(note?.headline).toBe("");
|
||||
}));
|
||||
|
||||
test("note title should allow trailing space", () =>
|
||||
noteTest({ title: "Hello ", content: TEST_NOTE.content }).then(
|
||||
async ({ db, id }) => {
|
||||
|
||||
@@ -87,6 +87,7 @@ export class Notes implements ICollection {
|
||||
let contentId = item.contentId;
|
||||
let dateEdited = item.dateEdited;
|
||||
let headline = item.headline;
|
||||
let headlineTitle = "";
|
||||
|
||||
if (item.content && item.content.data && item.content.type) {
|
||||
logger.debug("saving content", { id });
|
||||
@@ -95,7 +96,8 @@ export class Notes implements ICollection {
|
||||
const content = await getContentFromData(type, data);
|
||||
if (!content) throw new Error("Invalid content type.");
|
||||
|
||||
headline = getNoteHeadline(content);
|
||||
headline = content.toHeadline();
|
||||
headlineTitle = content.toTitle();
|
||||
dateEdited = Date.now();
|
||||
contentId = await this.db.content.add({
|
||||
noteId: id,
|
||||
@@ -125,7 +127,7 @@ export class Notes implements ICollection {
|
||||
this.db.settings.getTitleFormat(),
|
||||
this.db.settings.getDateFormat(),
|
||||
this.db.settings.getTimeFormat(),
|
||||
headline ? headlineToTitle(headline) : "",
|
||||
headlineTitle,
|
||||
this.totalNotes
|
||||
);
|
||||
item.isGeneratedTitle = true;
|
||||
@@ -134,8 +136,7 @@ export class Notes implements ICollection {
|
||||
const currentNoteTitleFields = await this.db
|
||||
.sql()
|
||||
.selectFrom("notes")
|
||||
.select("isGeneratedTitle")
|
||||
.select("title")
|
||||
.select(["isGeneratedTitle", "title"])
|
||||
.where("id", "=", id)
|
||||
.executeTakeFirst();
|
||||
if (isUpdating) {
|
||||
@@ -147,14 +148,12 @@ export class Notes implements ICollection {
|
||||
if (
|
||||
item.isGeneratedTitle &&
|
||||
HEADLINE_REGEX.test(titleFormat) &&
|
||||
headline &&
|
||||
currentNoteTitleFields?.title !== headlineToTitle(headline)
|
||||
headlineTitle &&
|
||||
currentNoteTitleFields?.title !== headlineTitle
|
||||
) {
|
||||
item.title = titleFormat.replace(
|
||||
HEADLINE_REGEX,
|
||||
headlineToTitle(headline)
|
||||
);
|
||||
}
|
||||
item.title = titleFormat.replace(HEADLINE_REGEX, headlineTitle);
|
||||
} else if (item.title === "")
|
||||
item.title = currentNoteTitleFields?.title || "Untitled";
|
||||
}
|
||||
|
||||
if (isUpdating) {
|
||||
@@ -488,11 +487,3 @@ export class Notes implements ICollection {
|
||||
).internalLinks;
|
||||
}
|
||||
}
|
||||
|
||||
function getNoteHeadline(content: Tiptap) {
|
||||
return content.toHeadline();
|
||||
}
|
||||
|
||||
function headlineToTitle(headline: string) {
|
||||
return headline.split(" ").splice(0, 10).join(" ");
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import dataurl from "../utils/dataurl.js";
|
||||
import {
|
||||
HTMLParser,
|
||||
extractHeadline,
|
||||
extractTitle,
|
||||
getDummyDocument
|
||||
} from "../utils/html-parser.js";
|
||||
import { HTMLRewriter } from "../utils/html-rewriter.js";
|
||||
@@ -89,7 +90,11 @@ export class Tiptap {
|
||||
}
|
||||
|
||||
toHeadline() {
|
||||
return extractHeadline(this.data, 150);
|
||||
return extractHeadline(this.data);
|
||||
}
|
||||
|
||||
toTitle() {
|
||||
return extractTitle(this.data, 150);
|
||||
}
|
||||
|
||||
// isEmpty() {
|
||||
|
||||
@@ -44,14 +44,42 @@ function wrapIntoHTMLDocument(input: string) {
|
||||
return `<!doctype html><html lang="en"><head><title>Document Fragment</title></head><body>${input}</body></html>`;
|
||||
}
|
||||
|
||||
export function extractHeadline(html: string, headlineCharacterLimit: number) {
|
||||
export function extractHeadline(html: string) {
|
||||
let text = "";
|
||||
let start = false;
|
||||
const parser = new Parser(
|
||||
{
|
||||
onopentag: (name) => {
|
||||
if (name === "p") start = true;
|
||||
},
|
||||
onclosetag: (name) => {
|
||||
if (name === "p") {
|
||||
start = false;
|
||||
parser.pause();
|
||||
parser.end();
|
||||
}
|
||||
},
|
||||
ontext: (data) => {
|
||||
if (start) text += data;
|
||||
}
|
||||
},
|
||||
{
|
||||
lowerCaseTags: false,
|
||||
decodeEntities: true
|
||||
}
|
||||
);
|
||||
parser.end(html);
|
||||
return text;
|
||||
}
|
||||
|
||||
export function extractTitle(html: string, characterLimit: number) {
|
||||
let text = "";
|
||||
const parser = new Parser(
|
||||
{
|
||||
ontext: (data) => {
|
||||
text += data;
|
||||
if (text.length > headlineCharacterLimit) {
|
||||
text = text.slice(0, headlineCharacterLimit);
|
||||
if (text.length > characterLimit) {
|
||||
text = text.slice(0, characterLimit);
|
||||
parser.pause();
|
||||
parser.end();
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
font-family: "Inter";
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: "Inter";
|
||||
/* p { */
|
||||
/* font-family: "Inter"; */
|
||||
/* color: var(--nn_primary_paragraph) ## TODO: use fixed color */
|
||||
}
|
||||
/* } */
|
||||
|
||||
::selection {
|
||||
color: white;
|
||||
@@ -37,6 +37,10 @@
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#statusbar p {
|
||||
font-family: "Inter";
|
||||
}
|
||||
</style>
|
||||
<meta name="description" content="Notesnook editor for mobile" />
|
||||
<!--
|
||||
|
||||
@@ -43,7 +43,11 @@ export default function TiptapEditorWrapper(props: {
|
||||
sx={{
|
||||
display: props.settings.noToolbar ? "none" : "flex",
|
||||
overflowY: "hidden",
|
||||
minHeight: "50px"
|
||||
minHeight: "50px",
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0
|
||||
}}
|
||||
editor={editor}
|
||||
location="bottom"
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Editor, scrollIntoViewById } from "@notesnook/editor";
|
||||
import { keepLastLineInView } from "@notesnook/editor/extensions/keep-in-view/keep-in-view.js";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import {
|
||||
ThemeDefinition,
|
||||
@@ -66,38 +67,12 @@ type Timers = {
|
||||
scroll: NodeJS.Timeout | null;
|
||||
};
|
||||
|
||||
function isInViewport(element: any) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <=
|
||||
(window.innerHeight || document.documentElement.clientHeight) &&
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
}
|
||||
|
||||
function scrollIntoView(editor: Editor) {
|
||||
if (__PLATFORM__ == "android") return;
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const node = editor?.state.selection.$from;
|
||||
const dom = node ? editor?.view?.domAtPos?.(node.pos) : null;
|
||||
let domNode = dom?.node;
|
||||
|
||||
if (domNode) {
|
||||
if (domNode.nodeType === Node.TEXT_NODE && domNode.parentNode) {
|
||||
domNode = domNode.parentNode;
|
||||
}
|
||||
if (isInViewport(domNode)) return;
|
||||
(domNode as HTMLElement).scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "end"
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
}
|
||||
}, 100);
|
||||
if (!editor.isFocused) return;
|
||||
keepLastLineInView(editor);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
export type EditorController = {
|
||||
|
||||
@@ -71,7 +71,7 @@ export const KeepInView = Extension.create<
|
||||
|
||||
export function keepLastLineInView(
|
||||
editor: Editor,
|
||||
THRESHOLD = 80,
|
||||
THRESHOLD = 90,
|
||||
SCROLL_THRESHOLD = 100
|
||||
) {
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user