mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 11:39:21 +02:00
Compare commits
23 Commits
mobile-rel
...
feat-home-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22497199c5 | ||
|
|
873b180dab | ||
|
|
ca8e505cd0 | ||
|
|
465e1af838 | ||
|
|
b59a3fb5de | ||
|
|
0a7f8631b6 | ||
|
|
50d0827972 | ||
|
|
a0d4be2379 | ||
|
|
e4eb5d1f0f | ||
|
|
9ae4d3df55 | ||
|
|
95b6b45cc4 | ||
|
|
9280d0ef7e | ||
|
|
5171b2e5e2 | ||
|
|
8455ff876a | ||
|
|
0503af6819 | ||
|
|
fbaba8ebe8 | ||
|
|
64c70f51c9 | ||
|
|
946da6e274 | ||
|
|
9450849b9d | ||
|
|
461c4acfb7 | ||
|
|
ccb0840771 | ||
|
|
b5a706a6a9 | ||
|
|
4fbb39efd0 |
2
.github/workflows/desktop.tests.yml
vendored
2
.github/workflows/desktop.tests.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
test-macos-x64:
|
||||
name: Test macOS x64
|
||||
needs: build
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
|
||||
633
apps/desktop/package-lock.json
generated
633
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.6-beta.3",
|
||||
"version": "3.3.6",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/cjs/index.js",
|
||||
@@ -37,7 +37,7 @@
|
||||
"electron-updater": "^6.6.2",
|
||||
"icojs": "^0.19.5",
|
||||
"sqlite-better-trigram": "0.0.5",
|
||||
"sqlite3-fts5-html": "^0.0.4",
|
||||
"sqlite3-fts5-html": "^0.0.6",
|
||||
"typed-emitter": "^2.1.0",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "3.24.3"
|
||||
|
||||
@@ -125,7 +125,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3086
|
||||
versionCode 3087
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -6,7 +6,14 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
@@ -29,6 +36,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class RCTNNativeModule extends ReactContextBaseJavaModule {
|
||||
@@ -237,6 +245,221 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void addShortcut(final String id, final String type, final String title, final String description, final String color, Promise promise) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
promise.reject("UNSUPPORTED", "Pinned launcher shortcuts require Android 8.0 or higher");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ShortcutManager shortcutManager = mContext.getSystemService(ShortcutManager.class);
|
||||
if (shortcutManager == null) {
|
||||
promise.reject("ERROR", "ShortcutManager not available");
|
||||
return;
|
||||
}
|
||||
|
||||
String uri = "https://app.notesnook.com/open_" + type + "?id=" + id;
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, android.net.Uri.parse(uri));
|
||||
intent.setPackage(mContext.getPackageName());
|
||||
|
||||
Icon icon = createLetterIcon(type, title, color);
|
||||
ShortcutInfo shortcut = new ShortcutInfo.Builder(mContext, id)
|
||||
.setShortLabel(title)
|
||||
.setLongLabel(description != null && !description.isEmpty() ? description : title)
|
||||
.setIcon(icon)
|
||||
.setCategories(Set.of(type))
|
||||
.setIntent(intent)
|
||||
.build();
|
||||
|
||||
shortcutManager.requestPinShortcut(shortcut, null);
|
||||
promise.resolve(true);
|
||||
} catch (Exception e) {
|
||||
promise.reject("ERROR", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeShortcut(final String id, Promise promise) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
promise.reject("UNSUPPORTED", "Pinned launcher shortcuts require Android 8.0 or higher");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ShortcutManager shortcutManager = mContext.getSystemService(ShortcutManager.class);
|
||||
if (shortcutManager == null) {
|
||||
promise.reject("ERROR", "ShortcutManager not available");
|
||||
return;
|
||||
}
|
||||
|
||||
shortcutManager.disableShortcuts(java.util.Collections.singletonList(id));
|
||||
promise.resolve(true);
|
||||
} catch (Exception e) {
|
||||
promise.reject("ERROR", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void updateShortcut(final String id, final String type, final String title, final String description,final String color, Promise promise) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
promise.reject("UNSUPPORTED", "Pinned launcher shortcuts require Android 8.0 or higher");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ShortcutManager shortcutManager = mContext.getSystemService(ShortcutManager.class);
|
||||
if (shortcutManager == null) {
|
||||
promise.reject("ERROR", "ShortcutManager not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get existing shortcut to preserve icon and intent
|
||||
List<ShortcutInfo> shortcuts = shortcutManager.getPinnedShortcuts();
|
||||
ShortcutInfo existingShortcut = null;
|
||||
for (ShortcutInfo s : shortcuts) {
|
||||
if (s.getId().equals(id)) {
|
||||
existingShortcut = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingShortcut == null) {
|
||||
return;
|
||||
}
|
||||
Icon icon = createLetterIcon(type, title, color);
|
||||
ShortcutInfo updatedShortcut = new ShortcutInfo.Builder(mContext, id)
|
||||
.setShortLabel(title)
|
||||
.setIcon(icon)
|
||||
.setLongLabel(description != null && !description.isEmpty() ? description : title)
|
||||
.setIntent(existingShortcut.getIntent())
|
||||
.build();
|
||||
|
||||
shortcutManager.updateShortcuts(java.util.Collections.singletonList(updatedShortcut));
|
||||
promise.resolve(true);
|
||||
} catch (Exception e) {
|
||||
promise.reject("ERROR", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeAllShortcuts(Promise promise) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
promise.reject("UNSUPPORTED", "Pinned launcher shortcuts require Android 8.0 or higher");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ShortcutManager shortcutManager = mContext.getSystemService(ShortcutManager.class);
|
||||
if (shortcutManager == null) {
|
||||
promise.reject("ERROR", "ShortcutManager not available");
|
||||
return;
|
||||
}
|
||||
|
||||
List<ShortcutInfo> pinnedShortcuts = shortcutManager.getPinnedShortcuts();
|
||||
if (!pinnedShortcuts.isEmpty()) {
|
||||
List<String> ids = new ArrayList<>();
|
||||
for (ShortcutInfo shortcut : pinnedShortcuts) {
|
||||
ids.add(shortcut.getId());
|
||||
}
|
||||
shortcutManager.disableShortcuts(ids);
|
||||
}
|
||||
promise.resolve(true);
|
||||
} catch (Exception e) {
|
||||
promise.reject("ERROR", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getAllShortcuts(Promise promise) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
}
|
||||
ShortcutManager shortcutManager = mContext.getSystemService(ShortcutManager.class);
|
||||
WritableArray shortcuts = Arguments.createArray();
|
||||
List<ShortcutInfo> infos = shortcutManager.getPinnedShortcuts();
|
||||
|
||||
for (ShortcutInfo info: infos) {
|
||||
WritableMap data = Arguments.createMap();
|
||||
data.putString("id", info.getId());
|
||||
if (info.getShortLabel() != null) {
|
||||
data.putString("title", info.getShortLabel().toString());
|
||||
}
|
||||
|
||||
if (info.getLongLabel() != null) {
|
||||
data.putString("description", info.getLongLabel().toString());
|
||||
}
|
||||
|
||||
|
||||
if (!Objects.requireNonNull(info.getCategories()).isEmpty()) {
|
||||
if (info.getCategories().contains("note")) {
|
||||
data.putString("type", "note");
|
||||
} else if (info.getCategories().contains("notebook")) {
|
||||
data.putString("type", "notebook");
|
||||
} else if (info.getCategories().contains("tag")) {
|
||||
data.putString("type", "tag");
|
||||
} else if (info.getCategories().contains("color")) {
|
||||
data.putString("type", "color");
|
||||
}
|
||||
}
|
||||
shortcuts.pushMap(data);
|
||||
}
|
||||
promise.resolve(shortcuts);
|
||||
}
|
||||
|
||||
private Icon createLetterIcon(String type, String title, String colorCode) {
|
||||
|
||||
String letter = type.contains("tag") ? "#" : title != null && !title.isEmpty()
|
||||
? title.substring(0, 1).toUpperCase()
|
||||
: "?";
|
||||
int color = type.equals("color") ? Color.parseColor(colorCode) : getColorForLetter(letter);
|
||||
|
||||
if (type.equals("notebook")) return Icon.createWithResource(mContext, R.drawable.ic_notebook);
|
||||
// Use a larger canvas and fill it completely to avoid white borders from launcher masking.
|
||||
int iconSize = 256;
|
||||
Bitmap bitmap = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
|
||||
if (type.equals("color")) {
|
||||
Paint backgroundPaint = new Paint();
|
||||
backgroundPaint.setColor(color);
|
||||
backgroundPaint.setAntiAlias(true);
|
||||
canvas.drawCircle(iconSize / 2, iconSize / 2, iconSize/2, backgroundPaint);
|
||||
} else {
|
||||
Paint textPaint = new Paint();
|
||||
textPaint.setColor(color);
|
||||
textPaint.setTextSize(130);
|
||||
textPaint.setAntiAlias(true);
|
||||
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||
textPaint.setTypeface(android.graphics.Typeface.create(android.graphics.Typeface.DEFAULT, android.graphics.Typeface.BOLD));
|
||||
|
||||
float x = iconSize / 2f;
|
||||
float y = (iconSize / 2f) - ((textPaint.descent() + textPaint.ascent()) / 2f);
|
||||
|
||||
canvas.drawText(letter, x, y, textPaint);
|
||||
}
|
||||
|
||||
return Icon.createWithBitmap(bitmap);
|
||||
}
|
||||
|
||||
private int getColorForLetter(String letter) {
|
||||
int[] colors = {
|
||||
0xFF1976D2, // Blue
|
||||
0xFFD32F2F, // Red
|
||||
0xFF388E3C, // Green
|
||||
0xFFF57C00, // Orange
|
||||
0xFF7B1FA2, // Purple
|
||||
0xFF0097A7, // Cyan
|
||||
0xFFC2185B, // Pink
|
||||
0xFF455A64, // Blue Grey
|
||||
0xFF6A1B9A, // Deep Purple
|
||||
0xFF00796B, // Teal
|
||||
0xFF512DA8, // Indigo
|
||||
0xFF1565C0 // Dark Blue
|
||||
};
|
||||
|
||||
int hash = Math.abs(letter.hashCode());
|
||||
return colors[hash % colors.length];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="#008837"
|
||||
android:alpha="1">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L720,80Q753,80 776.5,103.5Q800,127 800,160L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM240,800L720,800Q720,800 720,800Q720,800 720,800L720,160Q720,160 720,160Q720,160 720,160L640,160L640,440L540,380L440,440L440,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800ZM240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800ZM440,440L540,380L640,440L640,440L540,380L440,440Z"/>
|
||||
</vector>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 284 B |
Binary file not shown.
|
After Width: | Height: | Size: 211 B |
Binary file not shown.
|
After Width: | Height: | Size: 440 B |
Binary file not shown.
|
After Width: | Height: | Size: 807 B |
@@ -24,7 +24,7 @@ import {
|
||||
useThemeEngineStore
|
||||
} from "@notesnook/theme";
|
||||
import React, { useEffect } from "react";
|
||||
import { Appearance, I18nManager, StatusBar } from "react-native";
|
||||
import { Appearance, I18nManager, Linking, StatusBar } from "react-native";
|
||||
import "react-native-gesture-handler";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
@@ -43,7 +43,7 @@ import { changeSystemBarColors, useThemeStore } from "./stores/use-theme-store";
|
||||
import { useUserStore } from "./stores/use-user-store";
|
||||
import RNBootSplash from "react-native-bootsplash";
|
||||
import AppLocked from "./components/app-lock";
|
||||
|
||||
import { useSettingStore } from "./stores/use-setting-store";
|
||||
I18nManager.allowRTL(false);
|
||||
I18nManager.forceRTL(false);
|
||||
I18nManager.swapLeftAndRightInRTL(false);
|
||||
@@ -54,13 +54,18 @@ if (appLockEnabled || appLockMode !== "none") {
|
||||
RNBootSplash.hide({
|
||||
fade: true
|
||||
});
|
||||
Linking.getInitialURL().then((url) => {
|
||||
useSettingStore.setState({
|
||||
initialUrl: url
|
||||
});
|
||||
});
|
||||
const App = (props: { configureMode: "note-preview" }) => {
|
||||
useAppEvents();
|
||||
//@ts-ignore
|
||||
globalThis["IS_MAIN_APP_RUNNING"] = true;
|
||||
useEffect(() => {
|
||||
changeSystemBarColors();
|
||||
SettingsService.onFirstLaunch();
|
||||
changeSystemBarColors();
|
||||
setTimeout(async () => {
|
||||
await Notifications.get();
|
||||
if (SettingsService.get().notifNotes) {
|
||||
|
||||
@@ -50,7 +50,7 @@ export const Header = ({
|
||||
onLeftMenuButtonPress?: () => void;
|
||||
renderedInRoute?: RouteName;
|
||||
id?: string;
|
||||
title: string;
|
||||
title?: string;
|
||||
canGoBack?: boolean;
|
||||
onPressDefaultRightButton?: () => void;
|
||||
hasSearch?: boolean;
|
||||
@@ -114,7 +114,16 @@ export const Header = ({
|
||||
onLeftButtonPress={onLeftMenuButtonPress}
|
||||
/>
|
||||
|
||||
{hasSearch ? (
|
||||
{!title ? (
|
||||
<View
|
||||
style={{
|
||||
width: 100,
|
||||
backgroundColor: colors.primary.hover,
|
||||
height: 10,
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
) : hasSearch ? (
|
||||
<Paragraph>
|
||||
{selectionMode
|
||||
? `${selectedItemsList.length} selected`
|
||||
|
||||
@@ -57,6 +57,7 @@ const BOTTOM_BAR_ITEMS: ActionId[] = [
|
||||
"export",
|
||||
"copy-link",
|
||||
"duplicate",
|
||||
"launcher-shortcut",
|
||||
"trash"
|
||||
];
|
||||
|
||||
@@ -76,6 +77,7 @@ const COLUMN_BAR_ITEMS: ActionId[] = [
|
||||
"reorder",
|
||||
"rename-color",
|
||||
"rename-tag",
|
||||
"launcher-shortcut",
|
||||
"restore",
|
||||
"trash",
|
||||
"delete"
|
||||
@@ -170,15 +172,15 @@ export const Items = ({
|
||||
DDS.isTab
|
||||
? AppFontSize.xxl
|
||||
: shouldShrink
|
||||
? AppFontSize.xxl
|
||||
: AppFontSize.lg
|
||||
? AppFontSize.xxl
|
||||
: AppFontSize.lg
|
||||
}
|
||||
color={
|
||||
item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
: item.id.match(/(delete|trash)/g)
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
}
|
||||
/>
|
||||
</Pressable>
|
||||
@@ -209,8 +211,8 @@ export const Items = ({
|
||||
text: item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
: item.id === "delete" || item.id === "trash"
|
||||
? colors.error.paragraph
|
||||
: colors.primary.paragraph
|
||||
? colors.error.paragraph
|
||||
: colors.primary.paragraph
|
||||
}}
|
||||
testID={"icon-" + item.id}
|
||||
onPress={item.onPress}
|
||||
@@ -274,8 +276,8 @@ export const Items = ({
|
||||
item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
: item.id === "delete" || item.id === "trash"
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/* eslint-disable no-inner-declarations */
|
||||
import { useAreFeaturesAvailable } from "@notesnook/common";
|
||||
import {
|
||||
Color,
|
||||
createInternalLink,
|
||||
Item,
|
||||
ItemReference,
|
||||
Note,
|
||||
Notebook,
|
||||
VAULT_ERRORS
|
||||
} from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
@@ -70,6 +72,7 @@ import { deleteItems } from "../utils/functions";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { sleep } from "../utils/time";
|
||||
import { resetStoredState } from "./use-stored-state";
|
||||
import { NotesnookModule } from "../utils/notesnook-module";
|
||||
|
||||
export type ActionId =
|
||||
| "select"
|
||||
@@ -114,7 +117,8 @@ export type ActionId =
|
||||
| "remove-from-notebook"
|
||||
| "trash"
|
||||
| "default-homepage"
|
||||
| "default-tag";
|
||||
| "default-tag"
|
||||
| "launcher-shortcut";
|
||||
|
||||
export type Action = {
|
||||
id: ActionId;
|
||||
@@ -158,7 +162,8 @@ export const useActions = ({
|
||||
"shortcuts",
|
||||
"notebooks",
|
||||
"customizableSidebar",
|
||||
"customHomepage"
|
||||
"customHomepage",
|
||||
"androidLauncherShortcuts"
|
||||
]);
|
||||
const [item, setItem] = useState(propItem);
|
||||
const { colors } = useThemeColors();
|
||||
@@ -297,7 +302,6 @@ export const useActions = ({
|
||||
id: item.id,
|
||||
title: value
|
||||
});
|
||||
|
||||
eSendEvent(Navigation.routeNames.TaggedNotes);
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
useTagStore.getState().refresh();
|
||||
@@ -706,26 +710,6 @@ export const useActions = ({
|
||||
type: item.type
|
||||
}
|
||||
);
|
||||
|
||||
resetStoredState(
|
||||
"app-home-navigtion-key",
|
||||
isHomepage
|
||||
? undefined
|
||||
: {
|
||||
name:
|
||||
item.type === "notebook"
|
||||
? "Notebook"
|
||||
: item.type === "tag"
|
||||
? "TaggedNotes"
|
||||
: item.type === "color"
|
||||
? "ColorNotes"
|
||||
: undefined,
|
||||
params: {
|
||||
item: item,
|
||||
id: item.id
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1207,5 +1191,44 @@ export const useActions = ({
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
Platform.OS === "android" &&
|
||||
(item.type === "tag" ||
|
||||
item.type === "note" ||
|
||||
item.type === "notebook" ||
|
||||
item.type === "color")
|
||||
) {
|
||||
actions.push({
|
||||
id: "launcher-shortcut",
|
||||
title: strings.addToHome(),
|
||||
icon: "cellphone-arrow-down",
|
||||
locked: !features?.androidLauncherShortcuts.isAllowed,
|
||||
onPress: async () => {
|
||||
if (features && !features?.androidLauncherShortcuts.isAllowed) {
|
||||
ToastManager.show({
|
||||
message: features?.androidLauncherShortcuts.error,
|
||||
type: "info",
|
||||
actionText: strings.upgrade(),
|
||||
context: "local",
|
||||
func: () => {
|
||||
PaywallSheet.present(features?.androidLauncherShortcuts);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await NotesnookModule.addShortcut(
|
||||
item.id,
|
||||
item.type,
|
||||
item.title,
|
||||
(item as Note).headline || (item as Notebook).description || "",
|
||||
(item as Color).colorCode
|
||||
);
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@ import Notifications from "../services/notifications";
|
||||
import PremiumService from "../services/premium";
|
||||
import SettingsService from "../services/settings";
|
||||
import Sync from "../services/sync";
|
||||
import { clearAllStores, initAfterSync, initialize } from "../stores";
|
||||
import { clearAllStores, initAfterSync } from "../stores";
|
||||
import { refreshAllStores } from "../stores/create-db-collection-store";
|
||||
import { useAttachmentStore } from "../stores/use-attachment-store";
|
||||
import { useMessageStore } from "../stores/use-message-store";
|
||||
@@ -159,7 +159,10 @@ const onUserSessionExpired = async () => {
|
||||
eSendEvent(eLoginSessionExpired);
|
||||
};
|
||||
|
||||
const onAppOpenedFromURL = async (event: { url: string }) => {
|
||||
const onAppOpenedFromURL = async (event: {
|
||||
url: string;
|
||||
isInitialUrl?: boolean;
|
||||
}) => {
|
||||
const url = event.url;
|
||||
|
||||
try {
|
||||
@@ -171,7 +174,7 @@ const onAppOpenedFromURL = async (event: { url: string }) => {
|
||||
eSendEvent(eOnLoadNote, { newNote: true });
|
||||
fluidTabsRef.current?.goToPage("editor", false);
|
||||
return;
|
||||
} else if (url.startsWith("https://app.notesnook.com/open_note")) {
|
||||
} else if (url.startsWith("https://app.notesnook.com/open_note?")) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
const note = await db.notes.note(id);
|
||||
@@ -182,6 +185,53 @@ const onAppOpenedFromURL = async (event: { url: string }) => {
|
||||
fluidTabsRef.current?.goToPage("editor", false);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
url.startsWith("https://app.notesnook.com/open_notebook?") &&
|
||||
!event.isInitialUrl
|
||||
) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
const notebook = await db.notebooks.notebook(id);
|
||||
if (notebook) {
|
||||
Navigation.navigate("Notebook", {
|
||||
id: notebook.id,
|
||||
canGoBack: true,
|
||||
item: notebook
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
url.startsWith("https://app.notesnook.com/open_tag?") &&
|
||||
!event.isInitialUrl
|
||||
) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
const tag = await db.tags.tag(id);
|
||||
if (tag) {
|
||||
Navigation.navigate("TaggedNotes", {
|
||||
type: "tag",
|
||||
id: tag.id,
|
||||
item: tag,
|
||||
canGoBack: true
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
url.startsWith("https://app.notesnook.com/open_color?") &&
|
||||
!event.isInitialUrl
|
||||
) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
const color = await db.colors.color(id);
|
||||
if (color) {
|
||||
Navigation.navigate("ColoredNotes", {
|
||||
type: "color",
|
||||
id: color.id,
|
||||
item: color,
|
||||
canGoBack: true
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (url.startsWith("https://app.notesnook.com/open_reminder")) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
@@ -464,7 +514,10 @@ const initializeDatabase = async (password?: string) => {
|
||||
};
|
||||
|
||||
export const useAppEvents = () => {
|
||||
const isAppLoading = useSettingStore((state) => state.isAppLoading);
|
||||
const [isAppLoading, initialUrl] = useSettingStore((state) => [
|
||||
state.isAppLoading,
|
||||
state.initialUrl
|
||||
]);
|
||||
const [setLastSynced, setUser, appLocked, syncing] = useUserStore((state) => [
|
||||
state.setLastSynced,
|
||||
state.setUser,
|
||||
@@ -509,6 +562,15 @@ export const useAppEvents = () => {
|
||||
};
|
||||
}, [isAppLoading, onSyncComplete]);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialUrl) {
|
||||
onAppOpenedFromURL({
|
||||
url: initialUrl!,
|
||||
isInitialUrl: true
|
||||
});
|
||||
}
|
||||
}, [initialUrl]);
|
||||
|
||||
const subscribeToPurchaseListeners = useCallback(async () => {
|
||||
if (Platform.OS === "android") {
|
||||
try {
|
||||
@@ -774,23 +836,10 @@ export const useAppEvents = () => {
|
||||
}
|
||||
};
|
||||
|
||||
if (!refValues.current.initialUrl) {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
if (url) {
|
||||
refValues.current.initialUrl = url;
|
||||
}
|
||||
});
|
||||
}
|
||||
let sub: NativeEventSubscription;
|
||||
if (!isAppLoading && !appLocked) {
|
||||
setTimeout(() => {
|
||||
sub = AppState.addEventListener("change", onAppStateChanged);
|
||||
if (refValues.current.initialUrl) {
|
||||
onAppOpenedFromURL({
|
||||
url: refValues.current.initialUrl!
|
||||
});
|
||||
refValues.current.initialUrl = undefined;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
refValues.current.removeInternetStateListener = NetInfo.addEventListener(
|
||||
|
||||
@@ -31,8 +31,6 @@ import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { rootNavigatorRef } from "../utils/global-refs";
|
||||
import Navigation from "../services/navigation";
|
||||
import { isFeatureAvailable } from "@notesnook/common";
|
||||
import { useStoredValue } from "../hooks/use-stored-state";
|
||||
import { db } from "../common/database";
|
||||
|
||||
const RootStack = createNativeStackNavigator();
|
||||
const AppStack = createNativeStackNavigator();
|
||||
@@ -58,105 +56,114 @@ const AppNavigation = React.memo(
|
||||
() => {
|
||||
const { colors } = useThemeColors();
|
||||
const homepageV2 = useSettingStore((state) => state.settings.homepageV2);
|
||||
const home = useStoredValue(
|
||||
"app-home-navigtion-key",
|
||||
!homepageV2 || homepageV2.id === DEFAULT_HOME.name
|
||||
? DEFAULT_HOME
|
||||
: undefined
|
||||
);
|
||||
const loading = useSettingStore((state) => state.isAppLoading);
|
||||
const [home, setHome] = React.useState<
|
||||
{ name: string; params: any } | undefined
|
||||
>(undefined);
|
||||
|
||||
if (!home.value && !homepageV2) {
|
||||
home.value = DEFAULT_HOME;
|
||||
}
|
||||
React.useEffect(() => {
|
||||
if (useSettingStore.getState().initialUrl) {
|
||||
const url = useSettingStore.getState().initialUrl;
|
||||
if (url?.startsWith("https://app.notesnook.com/open_notebook?")) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
setHome({
|
||||
name: "Notebook",
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (url?.startsWith("https://app.notesnook.com/open_tag?")) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
setHome({
|
||||
name: "TaggedNotes",
|
||||
params: {
|
||||
type: "tag",
|
||||
id: id
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (url?.startsWith("https://app.notesnook.com/open_color?")) {
|
||||
const id = new URL(url).searchParams.get("id");
|
||||
if (id) {
|
||||
setHome({
|
||||
name: "ColoredNotes",
|
||||
params: {
|
||||
type: "color",
|
||||
id: id
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (homepageV2) {
|
||||
switch (homepageV2.type) {
|
||||
case "notebook": {
|
||||
setHome({
|
||||
name: "Notebook",
|
||||
params: {
|
||||
id: homepageV2.id
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
case "color": {
|
||||
setHome({
|
||||
name: "ColoredNotes",
|
||||
params: {
|
||||
type: "color",
|
||||
id: homepageV2.id
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
case "tag": {
|
||||
setHome({
|
||||
name: "TaggedNotes",
|
||||
params: {
|
||||
type: "tag",
|
||||
id: homepageV2.id
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
case "default":
|
||||
setHome(DEFAULT_HOME);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
setHome(DEFAULT_HOME);
|
||||
}
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!homepageV2 || loading) return;
|
||||
(async () => {
|
||||
isFeatureAvailable("customHomepage").then((value) => {
|
||||
if (!value.isAllowed) {
|
||||
home.value = DEFAULT_HOME;
|
||||
SettingsService.setProperty("homepageV2", undefined);
|
||||
}
|
||||
});
|
||||
|
||||
if (!home.value) {
|
||||
switch (homepageV2.type) {
|
||||
case "notebook":
|
||||
{
|
||||
const notebook = await db.notebooks.notebook(homepageV2.id);
|
||||
if (notebook) {
|
||||
home.value = {
|
||||
name: "Notebook",
|
||||
params: {
|
||||
item: notebook,
|
||||
id: notebook.id,
|
||||
title: notebook.title
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "color": {
|
||||
const color = await db.colors.color(homepageV2.id);
|
||||
if (color) {
|
||||
home.value = {
|
||||
name: "ColoredNotes",
|
||||
params: {
|
||||
item: color,
|
||||
id: color.id,
|
||||
title: color.title
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "tag": {
|
||||
const tag = await db.tags.tag(homepageV2.id);
|
||||
if (tag) {
|
||||
home.value = {
|
||||
name: "TaggedNotes",
|
||||
params: {
|
||||
item: tag,
|
||||
id: tag.id,
|
||||
title: tag.title
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "default":
|
||||
{
|
||||
home.value = DEFAULT_HOME;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
home.value = undefined;
|
||||
setTimeout(() => {
|
||||
home.value = DEFAULT_HOME;
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [homepageV2, loading, home.value]);
|
||||
}, [homepageV2, loading]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!home) return;
|
||||
useNavigationStore.getState().update(home?.name as keyof RouteParams);
|
||||
useNavigationStore
|
||||
.getState()
|
||||
.update(home.value?.name as keyof RouteParams);
|
||||
useNavigationStore
|
||||
.getState()
|
||||
.setFocusedRouteId(home.value?.params?.id || home.value?.name);
|
||||
.setFocusedRouteId(home?.params?.id || home?.name);
|
||||
}, [home]);
|
||||
|
||||
return !home.value ? null : (
|
||||
return !home ? null : (
|
||||
<AppStack.Navigator
|
||||
initialRouteName={home.value?.name}
|
||||
initialRouteName={home?.name}
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
animation: "none",
|
||||
@@ -197,7 +204,7 @@ const AppNavigation = React.memo(
|
||||
return TaggedNotes;
|
||||
}}
|
||||
initialParams={
|
||||
home.value?.name === "TaggedNotes" ? home.value?.params : undefined
|
||||
home?.name === "TaggedNotes" ? home?.params : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -209,7 +216,7 @@ const AppNavigation = React.memo(
|
||||
return ColoredNotes;
|
||||
}}
|
||||
initialParams={
|
||||
home.value?.name === "ColoredNotes" ? home.value?.params : undefined
|
||||
home?.name === "ColoredNotes" ? home?.params : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -244,9 +251,7 @@ const AppNavigation = React.memo(
|
||||
Notebook = Notebook || require("../screens/notebook").default;
|
||||
return Notebook;
|
||||
}}
|
||||
initialParams={
|
||||
home.value?.name === "Notebook" ? home.value?.params : undefined
|
||||
}
|
||||
initialParams={home?.name === "Notebook" ? home?.params : undefined}
|
||||
/>
|
||||
|
||||
<AppStack.Screen
|
||||
|
||||
@@ -1088,7 +1088,10 @@ export const useEditor = (
|
||||
|
||||
if (!state.current?.initialLoadCalled) {
|
||||
const url = await Linking.getInitialURL();
|
||||
let noteId = url && new URL(url).searchParams.get("id");
|
||||
let noteId =
|
||||
url &&
|
||||
url.startsWith("https://app.notesnook.com/open_note?") &&
|
||||
new URL(url).searchParams.get("id");
|
||||
if (noteId) {
|
||||
const note = await db.notes?.note(noteId);
|
||||
fluidTabsRef.current?.goToPage("editor");
|
||||
|
||||
@@ -38,7 +38,6 @@ import { eUpdateNotebookRoute } from "../../utils/events";
|
||||
import { findRootNotebookId } from "../../utils/notebooks";
|
||||
import { openEditor, setOnFirstSave } from "../notes/common";
|
||||
import { View } from "react-native";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Notebooks } from "../../components/sheets/notebooks";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { rootNavigatorRef } from "../../utils/global-refs";
|
||||
@@ -47,6 +46,9 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const params = useRef<NotebookScreenParams>(route?.params);
|
||||
const isAppLoading = useSettingStore((state) => state.isAppLoading);
|
||||
const [notebook, setNotebook] = useState<Notebook | undefined>(
|
||||
params.current.item
|
||||
);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const updateOnFocus = useRef(false);
|
||||
const [breadcrumbs, setBreadcrumbs] = useState<
|
||||
@@ -75,10 +77,10 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
});
|
||||
|
||||
const syncWithNavigation = React.useCallback(() => {
|
||||
useNavigationStore.getState().setFocusedRouteId(params?.current?.item?.id);
|
||||
useNavigationStore.getState().setFocusedRouteId(params?.current?.id);
|
||||
setOnFirstSave({
|
||||
type: "notebook",
|
||||
id: params.current.item.id
|
||||
id: params.current.id
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -86,23 +88,20 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
async (data?: NotebookScreenParams) => {
|
||||
if (useSettingStore.getState().isAppLoading) return;
|
||||
if (
|
||||
useNavigationStore.getState().focusedRouteId !==
|
||||
params.current.item.id &&
|
||||
useNavigationStore.getState().focusedRouteId !== params.current.id &&
|
||||
!data
|
||||
) {
|
||||
updateOnFocus.current = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (data?.item?.id && params.current.item?.id !== data?.item?.id) {
|
||||
const nextRootNotebookId = await findRootNotebookId(data?.item?.id);
|
||||
const currentNotebookRoot = await findRootNotebookId(
|
||||
params.current.item.id
|
||||
);
|
||||
if (data?.id && params.current?.id !== data?.id) {
|
||||
const nextRootNotebookId = await findRootNotebookId(data?.id);
|
||||
const currentNotebookRoot = await findRootNotebookId(params.current.id);
|
||||
|
||||
if (
|
||||
nextRootNotebookId !== currentNotebookRoot ||
|
||||
nextRootNotebookId === params.current?.item?.id
|
||||
nextRootNotebookId === params.current?.id
|
||||
) {
|
||||
// Never update notebook in route if root is different or if the root is current notebook.
|
||||
return;
|
||||
@@ -112,14 +111,13 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
if (data) params.current = data;
|
||||
|
||||
try {
|
||||
const notebook = await db.notebooks?.notebook(
|
||||
params?.current?.item?.id
|
||||
);
|
||||
|
||||
const notebook = await db.notebooks?.notebook(params?.current?.id);
|
||||
setNotebook(notebook);
|
||||
params.current.item = notebook;
|
||||
if (notebook) {
|
||||
const breadcrumbs = await db.notebooks.breadcrumbs(notebook.id);
|
||||
setBreadcrumbs(breadcrumbs.slice(0, breadcrumbs.length - 1));
|
||||
params.current.item = notebook;
|
||||
params.current.id = notebook.id;
|
||||
const notes = await db.relations
|
||||
.from(notebook, "note")
|
||||
.selector.grouped(db.settings.getGroupOptions("notes"));
|
||||
@@ -160,29 +158,27 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
<>
|
||||
<Header
|
||||
renderedInRoute={route.name}
|
||||
title={params.current.item?.title}
|
||||
title={notebook?.title}
|
||||
canGoBack={params?.current?.canGoBack}
|
||||
rightButton={{
|
||||
name: "dots-vertical",
|
||||
onPress: () => {
|
||||
Properties.present(params.current.item);
|
||||
Properties.present(notebook);
|
||||
}
|
||||
}}
|
||||
hasSearch={true}
|
||||
onSearch={() => {
|
||||
const selector = db.relations.from(
|
||||
params.current.item,
|
||||
"note"
|
||||
).selector;
|
||||
if (!notebook) return;
|
||||
const selector = db.relations.from(notebook, "note").selector;
|
||||
Navigation.push("Search", {
|
||||
placeholder: strings.searchInRoute(params.current.item?.title),
|
||||
placeholder: strings.searchInRoute(notebook?.title),
|
||||
type: "note",
|
||||
title: params.current.item?.title,
|
||||
title: notebook?.title,
|
||||
route: route.name,
|
||||
items: selector
|
||||
});
|
||||
}}
|
||||
id={params.current.item?.id}
|
||||
id={notebook?.id}
|
||||
/>
|
||||
|
||||
<DelayLayout wait={loading}>
|
||||
@@ -192,19 +188,19 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
onRefresh={() => {
|
||||
onRequestUpdate();
|
||||
}}
|
||||
id={params.current.item?.id}
|
||||
id={params.current?.id}
|
||||
renderedInRoute="Notebook"
|
||||
headerTitle={params.current.item.title}
|
||||
headerTitle={notebook?.title}
|
||||
loading={loading}
|
||||
CustomLisHeader={
|
||||
<NotebookHeader
|
||||
breadcrumbs={breadcrumbs}
|
||||
notebook={params.current.item}
|
||||
notebook={notebook!}
|
||||
totalNotes={notes?.placeholders.length || 0}
|
||||
/>
|
||||
}
|
||||
placeholder={{
|
||||
title: params.current.item?.title,
|
||||
title: notebook?.title!,
|
||||
paragraph: strings.notesEmpty(),
|
||||
button: strings.addFirstNote(),
|
||||
action: openEditor,
|
||||
@@ -225,7 +221,8 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
testID="notebookTreeSheet"
|
||||
size="small"
|
||||
onPress={() => {
|
||||
Notebooks.present(params.current.item);
|
||||
if (!notebook) return;
|
||||
Notebooks.present(notebook);
|
||||
}}
|
||||
style={{
|
||||
position: "relative",
|
||||
@@ -246,7 +243,7 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
|
||||
/>
|
||||
</View>
|
||||
<SelectionHeader
|
||||
id={route.params?.item?.id}
|
||||
id={route.params?.id}
|
||||
items={notes}
|
||||
type="note"
|
||||
renderedInRoute="Notebook"
|
||||
@@ -260,6 +257,7 @@ NotebookScreen.navigate = async (item: Notebook, canGoBack?: boolean) => {
|
||||
const { currentRoute, focusedRouteId } = useNavigationStore.getState();
|
||||
if (currentRoute === "Notebooks") {
|
||||
Navigation.push("Notebook", {
|
||||
id: item.id,
|
||||
item: item,
|
||||
canGoBack
|
||||
});
|
||||
@@ -276,22 +274,24 @@ NotebookScreen.navigate = async (item: Notebook, canGoBack?: boolean) => {
|
||||
// Update the route in place instead
|
||||
|
||||
eSendEvent(eUpdateNotebookRoute, {
|
||||
item: item,
|
||||
title: item.title,
|
||||
canGoBack: canGoBack
|
||||
id: item.id,
|
||||
canGoBack: canGoBack,
|
||||
item: item
|
||||
});
|
||||
} else {
|
||||
// Push a new route
|
||||
Navigation.push("Notebook", {
|
||||
item: item,
|
||||
canGoBack
|
||||
id: item.id,
|
||||
canGoBack,
|
||||
item: item
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Push a new route anyways
|
||||
Navigation.push("Notebook", {
|
||||
item: item,
|
||||
canGoBack
|
||||
id: item.id,
|
||||
canGoBack,
|
||||
item: item
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import useNavigationStore, {
|
||||
NotesScreenParams
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { PLACEHOLDER_DATA, openEditor, toCamelCase } from "./common";
|
||||
import { PLACEHOLDER_DATA, openEditor } from "./common";
|
||||
export const ColoredNotes = ({
|
||||
navigation,
|
||||
route
|
||||
@@ -45,11 +45,13 @@ export const ColoredNotes = ({
|
||||
|
||||
ColoredNotes.get = async (params: NotesScreenParams, grouped = true) => {
|
||||
if (!grouped) {
|
||||
return await db.relations.from(params.item, "note").resolve();
|
||||
return await db.relations
|
||||
.from({ id: params.id, type: "color" }, "note")
|
||||
.resolve();
|
||||
}
|
||||
|
||||
return await db.relations
|
||||
.from(params.item, "note")
|
||||
.from({ id: params.id, type: "color" }, "note")
|
||||
.selector.grouped(db.settings.getGroupOptions("notes"));
|
||||
};
|
||||
|
||||
@@ -63,8 +65,10 @@ ColoredNotes.navigate = (item: Color, canGoBack: boolean) => {
|
||||
}
|
||||
|
||||
Navigation.push<"ColoredNotes">("ColoredNotes", {
|
||||
item: item,
|
||||
canGoBack
|
||||
type: "color",
|
||||
id: item.id,
|
||||
canGoBack,
|
||||
item: item
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { resolveItems } from "@notesnook/common";
|
||||
import { VirtualizedGrouping } from "@notesnook/core";
|
||||
import { Tag, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { Color, Note } from "@notesnook/core";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { db } from "../../common/database";
|
||||
@@ -76,14 +76,12 @@ const NotesPage = ({
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const [loadingNotes, setLoadingNotes] = useState(true);
|
||||
const isMonograph = route.name === "Monographs";
|
||||
const title =
|
||||
params.current?.item.type === "tag"
|
||||
? "#" + params.current?.item.title
|
||||
: params.current?.item.title;
|
||||
const [item, setItem] = useState<Tag | Color | undefined>(
|
||||
params.current.item
|
||||
);
|
||||
const title = item?.type === "tag" ? "#" + item?.title : item?.title;
|
||||
const accentColor =
|
||||
route.name === "ColoredNotes"
|
||||
? (params.current?.item as Color)?.colorCode
|
||||
: undefined;
|
||||
route.name === "ColoredNotes" ? (item as Color)?.colorCode : undefined;
|
||||
const updateOnFocus = useRef(false);
|
||||
const isAppLoading = useSettingStore((state) => state.isAppLoading);
|
||||
const isFocused = useNavigationFocus(navigation, {
|
||||
@@ -107,31 +105,30 @@ const NotesPage = ({
|
||||
});
|
||||
|
||||
const syncWithNavigation = React.useCallback(() => {
|
||||
const { item } = params.current;
|
||||
useNavigationStore
|
||||
.getState()
|
||||
.setFocusedRouteId(params?.current?.item?.id || route.name);
|
||||
const { id } = params.current;
|
||||
useNavigationStore.getState().setFocusedRouteId(id || route.name);
|
||||
|
||||
!isMonograph &&
|
||||
setOnFirstSave({
|
||||
type: getItemType(route.name),
|
||||
id: item.id
|
||||
id: id
|
||||
});
|
||||
}, [isMonograph, route.name]);
|
||||
|
||||
const onRequestUpdate = React.useCallback(
|
||||
async (data?: NotesScreenParams) => {
|
||||
if (useSettingStore.getState().isAppLoading) return;
|
||||
|
||||
if (
|
||||
params.current.item.id &&
|
||||
useNavigationStore.getState().focusedRouteId !==
|
||||
params.current.item.id &&
|
||||
params.current.id &&
|
||||
useNavigationStore.getState().focusedRouteId !== params.current.id &&
|
||||
!data
|
||||
) {
|
||||
updateOnFocus.current = false;
|
||||
return;
|
||||
}
|
||||
const isNew = data && data?.item?.id !== params.current?.item?.id;
|
||||
|
||||
const isNew = data && data?.id !== params.current?.id;
|
||||
if (data) params.current = data;
|
||||
|
||||
try {
|
||||
@@ -142,9 +139,9 @@ const NotesPage = ({
|
||||
)) as VirtualizedGrouping<Note>;
|
||||
|
||||
if (route.name === "TaggedNotes" || route.name === "ColoredNotes") {
|
||||
const item = await (db as any)[params.current.item.type + "s"][
|
||||
params.current.item.type
|
||||
](params.current.item.id);
|
||||
const item = await (db as any)[params.current.type + "s"][
|
||||
params.current.type
|
||||
](params.current.id);
|
||||
|
||||
if (!item) {
|
||||
if (rootNavigatorRef.canGoBack()) {
|
||||
@@ -154,7 +151,7 @@ const NotesPage = ({
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setItem(item);
|
||||
params.current.item = item;
|
||||
}
|
||||
|
||||
@@ -173,15 +170,7 @@ const NotesPage = ({
|
||||
useEffect(() => {
|
||||
if (isAppLoading) return;
|
||||
if (loadingNotes) {
|
||||
get(params.current, true)
|
||||
.then(async (items) => {
|
||||
setNotes(items as VirtualizedGrouping<Note>);
|
||||
await (items as VirtualizedGrouping<Note>).item(0, resolveItems);
|
||||
setLoadingNotes(false);
|
||||
})
|
||||
.catch((e) => {
|
||||
setLoadingNotes(false);
|
||||
});
|
||||
onRequestUpdate(params.current);
|
||||
}
|
||||
}, [loadingNotes, get, isAppLoading]);
|
||||
|
||||
@@ -202,19 +191,18 @@ const NotesPage = ({
|
||||
}
|
||||
canGoBack={params?.current?.canGoBack}
|
||||
hasSearch={true}
|
||||
id={
|
||||
route.name === "Monographs" ? "Monographs" : params?.current.item?.id
|
||||
}
|
||||
id={route.name === "Monographs" ? "Monographs" : params?.current?.id}
|
||||
onSearch={() => {
|
||||
if (!item) return;
|
||||
const selector =
|
||||
route.name === "Monographs"
|
||||
? db.monographs.all
|
||||
: db.relations.from(params.current.item, "note").selector;
|
||||
: db.relations.from(item, "note").selector;
|
||||
|
||||
Navigation.push("Search", {
|
||||
placeholder: strings.searchInRoute(title || route.name),
|
||||
type: "note",
|
||||
title: title,
|
||||
title: title!,
|
||||
route: route.name,
|
||||
items: selector
|
||||
});
|
||||
@@ -229,7 +217,7 @@ const NotesPage = ({
|
||||
onRefresh={onRequestUpdate}
|
||||
loading={false}
|
||||
renderedInRoute={route.name}
|
||||
id={params.current.item?.id}
|
||||
id={params.current?.id}
|
||||
headerTitle={title || "Monographs"}
|
||||
customAccentColor={accentColor}
|
||||
placeholder={placeholder}
|
||||
@@ -246,7 +234,7 @@ const NotesPage = ({
|
||||
) : null}
|
||||
</DelayLayout>
|
||||
<SelectionHeader
|
||||
id={route.params?.item?.id || route.name}
|
||||
id={route.params?.id || route.name}
|
||||
items={notes}
|
||||
type="note"
|
||||
renderedInRoute={route.name}
|
||||
|
||||
@@ -62,7 +62,8 @@ Monographs.get = async (params?: NotesScreenParams, grouped = true) => {
|
||||
|
||||
Monographs.navigate = (canGoBack?: boolean) => {
|
||||
Navigation.navigate<"Monographs">("Monographs", {
|
||||
item: { type: "monograph" } as any,
|
||||
type: "monograph",
|
||||
id: "monograph",
|
||||
canGoBack: canGoBack as boolean
|
||||
});
|
||||
};
|
||||
|
||||
@@ -46,11 +46,13 @@ export const TaggedNotes = ({
|
||||
|
||||
TaggedNotes.get = async (params: NotesScreenParams, grouped = true) => {
|
||||
if (!grouped) {
|
||||
return await db.relations.from(params.item, "note").resolve();
|
||||
return await db.relations
|
||||
.from({ id: params.id, type: "tag" }, "note")
|
||||
.resolve();
|
||||
}
|
||||
|
||||
return await db.relations
|
||||
.from(params.item, "note")
|
||||
.from({ id: params.id, type: "tag" }, "note")
|
||||
.selector.grouped(db.settings.getGroupOptions("notes"));
|
||||
};
|
||||
|
||||
@@ -64,8 +66,10 @@ TaggedNotes.navigate = (item: Tag, canGoBack?: boolean) => {
|
||||
}
|
||||
|
||||
Navigation.push<"TaggedNotes">("TaggedNotes", {
|
||||
item: item,
|
||||
canGoBack
|
||||
id: item.id,
|
||||
type: "tag",
|
||||
canGoBack,
|
||||
item: item
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -24,9 +24,7 @@ import dayjs from "dayjs";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Dimensions,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
useWindowDimensions,
|
||||
View
|
||||
} from "react-native";
|
||||
@@ -44,8 +42,6 @@ import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useStoredRef } from "../../hooks/use-stored-ref";
|
||||
|
||||
function formatNumber(num: number) {
|
||||
if (num >= 1000000) {
|
||||
@@ -635,7 +631,8 @@ function SummarySlide({
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
padding: DefaultAppStyles.GAP
|
||||
padding: DefaultAppStyles.GAP,
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
|
||||
@@ -17,12 +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 { db } from "../common/database";
|
||||
import { DatabaseLogger, db } from "../common/database";
|
||||
import { eSendEvent } from "../services/event-manager";
|
||||
import Navigation from "../services/navigation";
|
||||
import { NotePreviewWidget } from "../services/note-preview-widget";
|
||||
import Notifications from "../services/notifications";
|
||||
import { eAfterSync } from "../utils/events";
|
||||
import { NotesnookModule, ShortcutInfo } from "../utils/notesnook-module";
|
||||
import { useFavoriteStore } from "./use-favorite-store";
|
||||
import { useMenuStore } from "./use-menu-store";
|
||||
import { useMonographStore } from "./use-monograph-store";
|
||||
@@ -34,6 +35,81 @@ import { useTagStore } from "./use-tag-store";
|
||||
import { useTrashStore } from "./use-trash-store";
|
||||
import { useUserStore } from "./use-user-store";
|
||||
|
||||
async function syncShortcuts(result: ShortcutInfo[]) {
|
||||
try {
|
||||
for (let shortcut of result) {
|
||||
switch (shortcut.type) {
|
||||
case "note":
|
||||
{
|
||||
const note = await db.notes.note(shortcut.id);
|
||||
if (!note) {
|
||||
NotesnookModule.removeShortcut(shortcut.id);
|
||||
} else if (note.title !== shortcut.title) {
|
||||
NotesnookModule.updateShortcut(
|
||||
shortcut.id,
|
||||
"note",
|
||||
note.title,
|
||||
note.headline
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "notebook":
|
||||
{
|
||||
const notebook = await db.notebooks.notebook(shortcut.id);
|
||||
if (!notebook) {
|
||||
NotesnookModule.removeShortcut(shortcut.id);
|
||||
} else if (notebook.title !== shortcut.title) {
|
||||
NotesnookModule.updateShortcut(
|
||||
shortcut.id,
|
||||
"notebook",
|
||||
notebook.title,
|
||||
notebook.description
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "tag":
|
||||
{
|
||||
const tag = await db.tags.tag(shortcut.id);
|
||||
if (!tag) {
|
||||
NotesnookModule.removeShortcut(shortcut.id);
|
||||
} else if (tag.title !== shortcut.title) {
|
||||
NotesnookModule.updateShortcut(
|
||||
shortcut.id,
|
||||
"tag",
|
||||
tag.title,
|
||||
tag.title
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "color":
|
||||
{
|
||||
const color = await db.colors.color(shortcut.id);
|
||||
if (!color) {
|
||||
NotesnookModule.removeShortcut(shortcut.id);
|
||||
} else if (color.title !== shortcut.title) {
|
||||
NotesnookModule.updateShortcut(
|
||||
shortcut.id,
|
||||
"color",
|
||||
color.title,
|
||||
color.title,
|
||||
color.colorCode
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(
|
||||
e as Error,
|
||||
"Error while syncing homescreen shortcuts"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function initAfterSync(type: "full" | "send" = "send") {
|
||||
if (type === "full") {
|
||||
Navigation.queueRoutesForUpdate();
|
||||
@@ -46,9 +122,16 @@ export function initAfterSync(type: "full" | "send" = "send") {
|
||||
profile: db.settings.getProfile()
|
||||
});
|
||||
}
|
||||
|
||||
Notifications.setupReminders(true);
|
||||
NotePreviewWidget.updateNotes();
|
||||
eSendEvent(eAfterSync);
|
||||
|
||||
NotesnookModule.getAllShortcuts()
|
||||
.then(syncShortcuts)
|
||||
.catch((e) => {
|
||||
DatabaseLogger.log(e);
|
||||
});
|
||||
}
|
||||
|
||||
export async function initialize() {}
|
||||
|
||||
@@ -25,8 +25,7 @@ import {
|
||||
Note,
|
||||
Notebook,
|
||||
Reminder,
|
||||
Tag,
|
||||
TrashItem
|
||||
Tag
|
||||
} from "@notesnook/core";
|
||||
import { ParamListBase } from "@react-navigation/core";
|
||||
import { create } from "zustand";
|
||||
@@ -36,12 +35,15 @@ export type GenericRouteParam = {
|
||||
};
|
||||
|
||||
export type NotebookScreenParams = {
|
||||
item: Notebook;
|
||||
id: string;
|
||||
item?: Notebook;
|
||||
canGoBack?: boolean;
|
||||
};
|
||||
|
||||
export type NotesScreenParams = {
|
||||
item: Note | Notebook | Tag | Color | TrashItem | Reminder;
|
||||
type: "tag" | "color" | "monograph";
|
||||
id: string;
|
||||
item?: Tag | Color;
|
||||
canGoBack?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ export interface SettingStore {
|
||||
dateFormat: string;
|
||||
dbPassword?: string;
|
||||
isOldAppLock: () => boolean;
|
||||
initialUrl: string | null;
|
||||
}
|
||||
|
||||
const { width, height } = Dimensions.get("window");
|
||||
@@ -236,5 +237,6 @@ export const useSettingStore = create<SettingStore>((set, get) => ({
|
||||
},
|
||||
insets: initialWindowMetrics?.insets
|
||||
? initialWindowMetrics.insets
|
||||
: { top: 0, right: 0, left: 0, bottom: 0 }
|
||||
: { top: 0, right: 0, left: 0, bottom: 0 },
|
||||
initialUrl: null
|
||||
}));
|
||||
|
||||
@@ -19,6 +19,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { NativeModules, Platform } from "react-native";
|
||||
|
||||
export type ShortcutInfo = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
type: "note" | "notebook" | "tag" | "color";
|
||||
};
|
||||
|
||||
interface NotesnookModuleInterface {
|
||||
getActivityName: () => Promise<string>;
|
||||
setBackgroundColor: (color: string) => void;
|
||||
@@ -41,6 +48,23 @@ interface NotesnookModuleInterface {
|
||||
updateWidgetNote: (noteId: string, data: string) => void;
|
||||
updateReminderWidget: () => void;
|
||||
isGestureNavigationEnabled: () => boolean;
|
||||
addShortcut: (
|
||||
id: string,
|
||||
type: "note" | "notebook" | "tag" | "color",
|
||||
title: string,
|
||||
description?: string,
|
||||
color?: string
|
||||
) => Promise<boolean>;
|
||||
removeShortcut: (id: string) => Promise<boolean>;
|
||||
updateShortcut: (
|
||||
id: string,
|
||||
type: "note" | "notebook" | "tag" | "color",
|
||||
title: string,
|
||||
description?: string,
|
||||
color?: string
|
||||
) => Promise<boolean>;
|
||||
removeAllShortcuts: () => Promise<boolean>;
|
||||
getAllShortcuts: () => Promise<ShortcutInfo[]>;
|
||||
}
|
||||
|
||||
export const NotesnookModule: NotesnookModuleInterface = Platform.select({
|
||||
@@ -61,7 +85,12 @@ export const NotesnookModule: NotesnookModuleInterface = Platform.select({
|
||||
hasWidgetNote: () => {},
|
||||
updateWidgetNote: () => {},
|
||||
updateReminderWidget: () => {},
|
||||
isGestureNavigationEnabled: () => true
|
||||
isGestureNavigationEnabled: () => true,
|
||||
addShortcut: () => Promise.resolve(false),
|
||||
removeShortcut: () => Promise.resolve(false),
|
||||
updateShortcut: () => Promise.resolve(false),
|
||||
removeAllShortcuts: () => Promise.resolve(false),
|
||||
getAllShortcuts: () => Promise.resolve([])
|
||||
},
|
||||
android: NativeModules.NNativeModule
|
||||
});
|
||||
|
||||
@@ -1029,7 +1029,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1104,7 +1104,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1135,7 +1135,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1210,7 +1210,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1367,7 +1367,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1379,7 +1379,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1410,7 +1410,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1423,7 +1423,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1453,7 +1453,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1534,7 +1534,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1565,7 +1565,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2166;
|
||||
CURRENT_PROJECT_VERSION = 2167;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1647,7 +1647,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
|
||||
MARKETING_VERSION = 3.3.10;
|
||||
MARKETING_VERSION = 3.3.11;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.3.10",
|
||||
"version": "3.3.11",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
@@ -208,4 +208,4 @@
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,8 @@ const EXTRA_ICON_NAMES = [
|
||||
"update",
|
||||
"notebook-minus",
|
||||
"calendar-blank",
|
||||
"email-newsletter"
|
||||
"email-newsletter",
|
||||
"cellphone-arrow-down"
|
||||
];
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
|
||||
1552
apps/web/package-lock.json
generated
1552
apps/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@notesnook/web",
|
||||
"description": "Your private note taking space",
|
||||
"version": "3.3.6-beta.3",
|
||||
"version": "3.3.6",
|
||||
"private": true,
|
||||
"main": "./src/app.js",
|
||||
"homepage": "https://notesnook.com/",
|
||||
@@ -57,7 +57,6 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"hash-wasm": "4.12.0",
|
||||
"hotkeys-js": "^3.8.3",
|
||||
"htmlparser2": "^10.0.0",
|
||||
"katex": "0.16.11",
|
||||
"mac-scrollbar": "0.13.6",
|
||||
"mutative": "^1.1.0",
|
||||
@@ -87,7 +86,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.22.5",
|
||||
"@cloudflare/workers-types": "^4.20250224.0",
|
||||
"@playwright/test": "1.48.2",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@swc/plugin-react-remove-properties": "^6.0.2",
|
||||
"@trpc/server": "10.45.2",
|
||||
"@types/babel__core": "^7.20.1",
|
||||
|
||||
@@ -91,7 +91,27 @@ const features: Record<FeatureKeys, Feature> = {
|
||||
)
|
||||
}
|
||||
]
|
||||
: [],
|
||||
: [
|
||||
{
|
||||
title: "Wrapped 2025",
|
||||
subtitle:
|
||||
"Notesnook Wrapped 2025 is your personal recap of the year you spent with Notesnook. Available throughout December and accessible from the sidebar, it highlights your activity, stats and how you used Notesnook this year."
|
||||
},
|
||||
{
|
||||
title: "Monograph analytics",
|
||||
subtitle:
|
||||
"Pro users can now see view count analytics for monographs."
|
||||
},
|
||||
{
|
||||
title: "Twitter/X embed support",
|
||||
subtitle: "Simply paste the post URL in the Insert Embed dialog."
|
||||
},
|
||||
{
|
||||
title: "New keyboard shortcuts",
|
||||
subtitle:
|
||||
"Use Ctrl/Cmd + Alt/Option + F to open search/replace in the editor. We have also added Ctrl/Cmd + Alt/Option + C for toggling text color."
|
||||
}
|
||||
],
|
||||
cta: {
|
||||
title: strings.gotIt(),
|
||||
icon: Checkmark,
|
||||
|
||||
@@ -22,7 +22,7 @@ The following keyboard shortcuts will help you navigate Notesnook faster.
|
||||
| --- | --- | --- | --- |
|
||||
| Next tab | Ctrl Alt → / Ctrl Alt ⇧ → | Ctrl tab | ⌘ tab |
|
||||
| Previous tab | Ctrl Alt ← / Ctrl Alt ⇧ ← | Ctrl ⇧ tab | ⌘ ⇧ tab |
|
||||
| Command palette | Ctrl ⇧ P | Ctrl ⇧ P | ⌘ ⇧ P |
|
||||
| Command palette | Ctrl ⇧ P / Ctrl ⇧ : | Ctrl ⇧ P | ⌘ ⇧ P |
|
||||
| Quick open | Ctrl P | Ctrl P | ⌘ P |
|
||||
| New tab | - | Ctrl T | ⌘ T |
|
||||
| Close active tab | - | Ctrl W | ⌘ W |
|
||||
|
||||
5
fastlane/metadata/android/en-US/changelogs/15439.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/15439.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
- Get an overview of your year with Notesnook with Wrapped 2025
|
||||
- Cell selection and column resizing in tables
|
||||
- Bug fixes and minor improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -469,6 +469,17 @@ const features = {
|
||||
believer: createLimit(true),
|
||||
legacyPro: createLimit(true)
|
||||
}
|
||||
}),
|
||||
androidLauncherShortcuts: createFeature({
|
||||
id: "androidLauncherShortcuts",
|
||||
title: "Android Launcher Shortcuts",
|
||||
availability: {
|
||||
free: createLimit(false),
|
||||
essential: createLimit(false),
|
||||
pro: createLimit(true),
|
||||
believer: createLimit(true),
|
||||
legacyPro: createLimit(true)
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -96,7 +96,10 @@ export const hotkeys = {
|
||||
type: "hotkeys"
|
||||
},
|
||||
openCommandPalette: {
|
||||
keys: normalizeKeys(["ctrl+shift+p"]),
|
||||
keys: normalizeKeys({
|
||||
desktop: ["ctrl+shift+p"],
|
||||
web: ["ctrl+shift+p", "ctrl+shift+:"]
|
||||
}),
|
||||
description: "Command palette",
|
||||
category: "Navigation",
|
||||
type: "hotkeys"
|
||||
|
||||
@@ -119,7 +119,15 @@ export function TableComponent(
|
||||
</SimpleBar>
|
||||
</DesktopOnly>
|
||||
<MobileOnly>
|
||||
<div dir={textDirection}>
|
||||
<div
|
||||
dir={textDirection}
|
||||
style={{
|
||||
overflowY: "hidden",
|
||||
overflowX: "auto",
|
||||
WebkitOverflowScrolling: "touch",
|
||||
maxWidth: "100%"
|
||||
}}
|
||||
>
|
||||
<table
|
||||
ref={(ref) => {
|
||||
forwardRef?.(ref);
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 { Attrs, Node as ProsemirrorNode } from "prosemirror-model";
|
||||
import { EditorState, Plugin, PluginKey, Transaction } from "prosemirror-state";
|
||||
import {
|
||||
@@ -9,7 +28,7 @@ import {
|
||||
import { tableNodeTypes } from "./schema.js";
|
||||
import { TableMap } from "./tablemap.js";
|
||||
import { TableView, updateColumnsOnResize } from "./tableview.js";
|
||||
import { cellAround, CellAttrs, getClientX } from "./util.js";
|
||||
import { cellAround, CellAttrs, getClientX, isTouchEvent } from "./util.js";
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -192,8 +211,7 @@ function handleMouseDown(
|
||||
|
||||
const pluginState = columnResizingPluginKey.getState(view.state);
|
||||
if (pluginState?.dragging) {
|
||||
if (event instanceof TouchEvent)
|
||||
(view as any).domObserver.connectSelection();
|
||||
if (isTouchEvent(event)) (view as any).domObserver.connectSelection();
|
||||
updateColumnWidth(
|
||||
view,
|
||||
activeHandle,
|
||||
@@ -224,8 +242,7 @@ function handleMouseDown(
|
||||
win.addEventListener("touchcancel", finish);
|
||||
win.addEventListener("touchmove", move);
|
||||
event.preventDefault();
|
||||
if (event instanceof TouchEvent)
|
||||
(view as any).domObserver.disconnectSelection();
|
||||
if (isTouchEvent(event)) (view as any).domObserver.disconnectSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
// This file defines a number of helpers for wiring up user input to
|
||||
// table-related functionality.
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 { keydownHandler } from "prosemirror-keymap";
|
||||
import { Fragment, ResolvedPos, Slice } from "prosemirror-model";
|
||||
@@ -23,6 +39,7 @@ import {
|
||||
getClientY,
|
||||
inSameTable,
|
||||
isInTable,
|
||||
isTouchEvent,
|
||||
nextCell,
|
||||
selectionCell,
|
||||
tableEditingKey
|
||||
@@ -313,7 +330,3 @@ function cellUnderMouse(
|
||||
if (!mousePos) return null;
|
||||
return mousePos ? cellAround(view.state.doc.resolve(mousePos.pos)) : null;
|
||||
}
|
||||
|
||||
function isTouchEvent(event: Event): event is TouchEvent {
|
||||
return "TouchEvent" in window && event instanceof TouchEvent;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
// Various helper function for working with tables
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 { EditorState, NodeSelection, PluginKey } from "prosemirror-state";
|
||||
|
||||
@@ -216,3 +233,7 @@ export function getClientY(event: MouseEvent | TouchEvent): number | null {
|
||||
? event.changedTouches[0].clientY
|
||||
: null;
|
||||
}
|
||||
|
||||
export function isTouchEvent(event: Event): event is TouchEvent {
|
||||
return "TouchEvent" in window && event instanceof TouchEvent;
|
||||
}
|
||||
|
||||
@@ -735,6 +735,10 @@ msgstr "Add tags to multiple notes at once"
|
||||
msgid "Add to dictionary"
|
||||
msgstr "Add to dictionary"
|
||||
|
||||
#: src/strings.ts:2613
|
||||
msgid "Add to home"
|
||||
msgstr "Add to home"
|
||||
|
||||
#: src/strings.ts:2476
|
||||
msgid "Add to notebook"
|
||||
msgstr "Add to notebook"
|
||||
|
||||
@@ -735,6 +735,10 @@ msgstr ""
|
||||
msgid "Add to dictionary"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2613
|
||||
msgid "Add to home"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2476
|
||||
msgid "Add to notebook"
|
||||
msgstr ""
|
||||
|
||||
@@ -2609,5 +2609,6 @@ Use this if changes from other devices are not appearing on this device. This wi
|
||||
views: () => t`Views`,
|
||||
clickToUpdate: () => t`Click to update`,
|
||||
noPassword: () => t`No password`,
|
||||
publishToTheWeb: () => t`Publish to the web`
|
||||
publishToTheWeb: () => t`Publish to the web`,
|
||||
addToHome: () => t`Add to home`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user