mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-15 19:27:51 +01:00
Merge branch 'master' of https://github.com/streetwriters/notesnook-private
This commit is contained in:
@@ -12,4 +12,7 @@ apps/mobile/__tests__/
|
||||
apps/web/public/an.js
|
||||
|
||||
# editor
|
||||
packages/editor/styles/
|
||||
packages/editor/styles/
|
||||
|
||||
# editor mobile
|
||||
packages/editor-mobile/build.bundle
|
||||
@@ -1,8 +1,5 @@
|
||||
import Database from "@streetwriters/notesnook-core/api/index";
|
||||
import {
|
||||
initalize,
|
||||
logger as dbLogger
|
||||
} from "@streetwriters/notesnook-core/logger";
|
||||
import Database from "@notesnook/core/api/index";
|
||||
import { initalize, logger as dbLogger } from "@notesnook/core/logger";
|
||||
import { Platform } from "react-native";
|
||||
import { MMKVLoader } from "react-native-mmkv-storage";
|
||||
import filesystem from "../filesystem";
|
||||
@@ -16,7 +13,7 @@ initalize(new KV(LoggerStorage));
|
||||
export const DatabaseLogger = dbLogger;
|
||||
|
||||
/**
|
||||
* @type {import("@streetwriters/notesnook-core/api/index").default}
|
||||
* @type {import("@notesnook/core/api/index").default}
|
||||
*/
|
||||
export var db = new Database(
|
||||
Storage,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import hosts from "@streetwriters/notesnook-core/utils/constants";
|
||||
import hosts from "@notesnook/core/utils/constants";
|
||||
import React from "react";
|
||||
import { Platform } from "react-native";
|
||||
import * as ScopedStorage from "react-native-scoped-storage";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { EV, EVENTS } from "@streetwriters/notesnook-core/common";
|
||||
import { EV, EVENTS } from "@notesnook/core/common";
|
||||
import { useEffect, useRef } from "react";
|
||||
import {
|
||||
Appearance,
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"dependencies": {
|
||||
"@flyerhq/react-native-link-preview": "^1.6.0",
|
||||
"@mdi/js": "^6.7.96",
|
||||
"@streetwriters/editor": "*",
|
||||
"@streetwriters/notesnook-core": "^7.3.5",
|
||||
"@notesnook/editor": "*",
|
||||
"@notesnook/core": "*",
|
||||
"absolutify": "^0.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.10.4",
|
||||
@@ -22,7 +22,6 @@
|
||||
"react-native-reanimated-material-menu": "github:ammarahm-ed/react-native-reanimated-material-menu",
|
||||
"react-native-reanimated-progress-bar": "1.0.1",
|
||||
"showdown": "github:thecodrr/showdown",
|
||||
"showndown": "github:thecodrr/showdown",
|
||||
"toggle-switch-react-native": "3.2.0",
|
||||
"url": "^0.11.0",
|
||||
"validator": "^13.5.2",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import { EV, EVENTS } from "@streetwriters/notesnook-core/common";
|
||||
import { EV, EVENTS } from "@notesnook/core/common";
|
||||
import React, {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type {
|
||||
Attachment,
|
||||
AttachmentProgress
|
||||
} from "@streetwriters/editor/dist/extensions/attachment/index";
|
||||
import type { ImageAttributes } from "@streetwriters/editor/dist/extensions/image/index";
|
||||
} from "@notesnook/editor/dist/extensions/attachment/index";
|
||||
import type { ImageAttributes } from "@notesnook/editor/dist/extensions/image/index";
|
||||
import { createRef, RefObject } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { EdgeInsets } from "react-native-safe-area-context";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEditor } from "./use-editor";
|
||||
import type { ToolbarGroupDefinition } from "@streetwriters/editor/dist/toolbar/types";
|
||||
import type { ToolbarGroupDefinition } from "@notesnook/editor/dist/toolbar/types";
|
||||
import { ThemeStore } from "../../../stores/use-theme-store";
|
||||
import { NoteType } from "../../../utils/types";
|
||||
export type useEditorType = ReturnType<typeof useEditor>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { groupArray } from "@streetwriters/notesnook-core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { qclone } from "qclone";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { groupArray } from "@streetwriters/notesnook-core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { groupArray } from "@streetwriters/notesnook-core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { groupArray } from "@streetwriters/notesnook-core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { groupArray } from "@streetwriters/notesnook-core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import { LogMessage } from "@streetwriters/logger";
|
||||
import {
|
||||
format,
|
||||
LogLevel,
|
||||
logManager
|
||||
} from "@streetwriters/notesnook-core/logger";
|
||||
import { format, LogLevel, logManager } from "@notesnook/core/logger";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { FlatList, Platform, TouchableOpacity, View } from "react-native";
|
||||
import * as ScopedStorage from "react-native-scoped-storage";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ToolbarGroupDefinition } from "@streetwriters/editor/dist/toolbar/types";
|
||||
import type { ToolbarGroupDefinition } from "@notesnook/editor/dist/toolbar/types";
|
||||
import create, { State } from "zustand";
|
||||
import { persist, StateStorage } from "zustand/middleware";
|
||||
import { useNoteStore } from "../../../stores/use-notes-store";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ToolId } from "@streetwriters/editor/dist/toolbar";
|
||||
import { ToolId } from "@notesnook/editor/dist/toolbar";
|
||||
import React, { RefObject } from "react";
|
||||
import { View } from "react-native";
|
||||
import ActionSheet from "react-native-actions-sheet";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Icons } from "@streetwriters/editor/dist/toolbar/icons";
|
||||
import { Icons } from "@notesnook/editor/dist/toolbar/icons";
|
||||
import {
|
||||
getDefaultPresets,
|
||||
getAllTools
|
||||
} from "@streetwriters/editor/dist/toolbar/tool-definitions";
|
||||
import { ToolId } from "@streetwriters/editor/dist/toolbar/tools";
|
||||
import { ToolbarGroupDefinition } from "@streetwriters/editor/dist/toolbar/index";
|
||||
} from "@notesnook/editor/dist/toolbar/tool-definitions";
|
||||
import { ToolId } from "@notesnook/editor/dist/toolbar/tools";
|
||||
import { ToolbarGroupDefinition } from "@notesnook/editor/dist/toolbar/index";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
|
||||
export const tools = getAllTools();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import EventManager from "@streetwriters/notesnook-core/utils/eventmanager";
|
||||
import EventManager from "@notesnook/core/utils/eventmanager";
|
||||
import { RefObject } from "react";
|
||||
import ActionSheet from "react-native-actions-sheet";
|
||||
import { Config } from "react-native-config";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CHECK_IDS } from "@streetwriters/notesnook-core/common";
|
||||
import { CHECK_IDS } from "@notesnook/core/common";
|
||||
import React from "react";
|
||||
import { Platform } from "react-native";
|
||||
import * as RNIap from "react-native-iap";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { EVENTS } from "@streetwriters/notesnook-core/common";
|
||||
import { EVENTS } from "@notesnook/core/common";
|
||||
import { initAfterSync } from "../stores/index";
|
||||
import { useUserStore } from "../stores/use-user-store";
|
||||
import { doInBackground } from "../utils";
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
"test": "jest --forceExit",
|
||||
"lint": "eslint .",
|
||||
"build-detox-android": "REACT_NATIVE_DEPENDENCIES=$HOME/Repos/notesnook-mobile/rn-build-deps/ ENVFILE=.env.test detox build -c android.emu.debug",
|
||||
"update": "yarn add @streetwriters/notesnook-core",
|
||||
"test-android": "adb reverse tcp:8081 tcp:8081 && yarn detox test --configuration android.emu.debug --detectOpenHandles",
|
||||
"test-android-release": "detox test --configuration android.emu.release",
|
||||
"bump": "npx react-native bump-version --skip-semver-for android",
|
||||
|
||||
@@ -83,7 +83,7 @@ module.exports = (env) => {
|
||||
//'react-native': reactNativePath,
|
||||
"react": path.join(__dirname, "../node_modules/react"),
|
||||
"react-dom": path.join(__dirname, "../node_modules/react-dom"),
|
||||
"@streetwriters/editor": path.join(__dirname, "../../../packages/editor"),
|
||||
"@notesnook/editor": path.join(__dirname, "../../../packages/editor"),
|
||||
},
|
||||
},
|
||||
/**
|
||||
|
||||
14
apps/mobile/package-lock.json
generated
14
apps/mobile/package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"app/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@streetwriters/editor": "*"
|
||||
"@notesnook/editor": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"patch-package": "^6.4.7",
|
||||
@@ -26,7 +26,7 @@
|
||||
"dependencies": {
|
||||
"@flyerhq/react-native-link-preview": "^1.6.0",
|
||||
"@mdi/js": "^6.7.96",
|
||||
"@streetwriters/editor": "*",
|
||||
"@notesnook/editor": "*",
|
||||
"@streetwriters/notesnook-core": "^7.3.5",
|
||||
"absolutify": "^0.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
@@ -5823,9 +5823,9 @@
|
||||
"resolved": "https://registry.npmjs.org/@social-embed/lib/-/lib-0.0.1-next.12.tgz",
|
||||
"integrity": "sha512-CUqgq2PTmdmWhNPzth9JN1cFoaFsIWhgiuDVD23hjD+9HJVD+xs3GlbZF+YWYMKVhRa3hib4A7Ev8rOjz2QBwQ=="
|
||||
},
|
||||
"node_modules/@streetwriters/editor": {
|
||||
"node_modules/@notesnook/editor": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://npm.pkg.github.com/download/@streetwriters/editor/1.3.1/68b8d739d0b11e392b689b5bf7375318172de61a",
|
||||
"resolved": "https://npm.pkg.github.com/download/@notesnook/editor/1.3.1/68b8d739d0b11e392b689b5bf7375318172de61a",
|
||||
"integrity": "sha512-Pub5JXPabY4YdraMLi43HW+Y94ZiZRZA+PXJUmpSPa+KBr/36hTZG55EJUEqlY+bGDdec0ctSWFJ5Hu1cNPBqg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0",
|
||||
@@ -26147,7 +26147,7 @@
|
||||
"requires": {
|
||||
"@flyerhq/react-native-link-preview": "^1.6.0",
|
||||
"@mdi/js": "^6.7.96",
|
||||
"@streetwriters/editor": "*",
|
||||
"@notesnook/editor": "*",
|
||||
"@streetwriters/notesnook-core": "^7.3.5",
|
||||
"absolutify": "^0.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
@@ -27440,9 +27440,9 @@
|
||||
"resolved": "https://registry.npmjs.org/@social-embed/lib/-/lib-0.0.1-next.12.tgz",
|
||||
"integrity": "sha512-CUqgq2PTmdmWhNPzth9JN1cFoaFsIWhgiuDVD23hjD+9HJVD+xs3GlbZF+YWYMKVhRa3hib4A7Ev8rOjz2QBwQ=="
|
||||
},
|
||||
"@streetwriters/editor": {
|
||||
"@notesnook/editor": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://npm.pkg.github.com/download/@streetwriters/editor/1.3.1/68b8d739d0b11e392b689b5bf7375318172de61a",
|
||||
"resolved": "https://npm.pkg.github.com/download/@notesnook/editor/1.3.1/68b8d739d0b11e392b689b5bf7375318172de61a",
|
||||
"integrity": "sha512-Pub5JXPabY4YdraMLi43HW+Y94ZiZRZA+PXJUmpSPa+KBr/36hTZG55EJUEqlY+bGDdec0ctSWFJ5Hu1cNPBqg==",
|
||||
"requires": {
|
||||
"@_ueberdosis/prosemirror-tables": "^1.1.3",
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
"typescript": "^4.8.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@streetwriters/editor": "*"
|
||||
"@notesnook/editor": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getPreviewData } from "@flyerhq/react-native-link-preview";
|
||||
import { parseHTML } from "@streetwriters/notesnook-core/utils/htmlparser";
|
||||
import { parseHTML } from "@notesnook/core/utils/htmlparser";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
|
||||
@@ -104,9 +104,6 @@
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
|
||||
1
packages/editor-mobile/.env
Normal file
1
packages/editor-mobile/.env
Normal file
@@ -0,0 +1 @@
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
24
packages/editor-mobile/.gitignore
vendored
Normal file
24
packages/editor-mobile/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
/build.bundle
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
0
packages/editor-mobile/README.md
Normal file
0
packages/editor-mobile/README.md
Normal file
40744
packages/editor-mobile/package-lock.json
generated
Normal file
40744
packages/editor-mobile/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
56
packages/editor-mobile/package.json
Normal file
56
packages/editor-mobile/package.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "@notesnook/editor-mobile",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@notesnook/editor": "*",
|
||||
"@notesnook/theme": "*",
|
||||
"framer-motion": "^4.1.17",
|
||||
"mdi-react": "^8.4.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"zustand": "^3.6.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.0",
|
||||
"@testing-library/react": "^11.2.7",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@types/node": "^16.11.11",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"patch-package": "^6.4.7",
|
||||
"react-error-overlay": "6.0.9",
|
||||
"react-scripts": "^4.0.3",
|
||||
"typescript": "^4.7.2",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "BROWSER=none react-scripts start",
|
||||
"build": "GENERATE_SOURCEMAP=false react-scripts build && mv build build.bundle",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"homepage": ".",
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"nx": {
|
||||
"targets": {
|
||||
"build": {
|
||||
"outputs": [
|
||||
"{projectRoot}/build.bundle"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
packages/editor-mobile/public/favicon.ico
Normal file
BIN
packages/editor-mobile/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
71
packages/editor-mobile/public/fonts.css
Normal file
71
packages/editor-mobile/public/fonts.css
Normal file
@@ -0,0 +1,71 @@
|
||||
/* open-sans-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-Regular.ttf) format('truetype');
|
||||
}
|
||||
|
||||
/* open-sans-italic - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-Italic.ttf) format('truetype');
|
||||
}
|
||||
|
||||
/* open-sans-600 - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-Medium.ttf) format('truetype');
|
||||
}
|
||||
|
||||
/* open-sans-600italic - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-MediumItalic.ttf) format('truetype');
|
||||
}
|
||||
|
||||
/* open-sans-600 - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-SemiBold.ttf) format('truetype');
|
||||
}
|
||||
|
||||
/* open-sans-600italic - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-SemiBoldItalic.ttf) format('truetype');
|
||||
}
|
||||
|
||||
/* open-sans-700 - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-Bold.ttf) format('truetype');
|
||||
}
|
||||
|
||||
/* open-sans-700italic - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans'), url(./fonts/OpenSans-BoldItalic.ttf) format('truetype');
|
||||
}
|
||||
BIN
packages/editor-mobile/public/fonts/OpenSans-Bold.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-Bold.ttf
Normal file
Binary file not shown.
BIN
packages/editor-mobile/public/fonts/OpenSans-BoldItalic.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
packages/editor-mobile/public/fonts/OpenSans-Italic.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-Italic.ttf
Normal file
Binary file not shown.
BIN
packages/editor-mobile/public/fonts/OpenSans-Medium.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-Medium.ttf
Normal file
Binary file not shown.
BIN
packages/editor-mobile/public/fonts/OpenSans-MediumItalic.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
packages/editor-mobile/public/fonts/OpenSans-Regular.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-Regular.ttf
Normal file
Binary file not shown.
BIN
packages/editor-mobile/public/fonts/OpenSans-SemiBold.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-SemiBold.ttf
Normal file
Binary file not shown.
BIN
packages/editor-mobile/public/fonts/OpenSans-SemiBoldItalic.ttf
Normal file
BIN
packages/editor-mobile/public/fonts/OpenSans-SemiBoldItalic.ttf
Normal file
Binary file not shown.
83
packages/editor-mobile/public/index.html
Normal file
83
packages/editor-mobile/public/index.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="height: 100%" lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link href="fonts.css" rel="stylesheet" />
|
||||
<style>
|
||||
body {
|
||||
height: 100%;
|
||||
font-family: "Open Sans";
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: "Open Sans";
|
||||
/* color: var(--nn_pri) ## TODO: use fixed color */
|
||||
}
|
||||
|
||||
::selection {
|
||||
color: white;
|
||||
background-color: var(--nn_accent);
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: var(--nn_placeholder);
|
||||
}
|
||||
|
||||
p.is-editor-empty:first-child::before {
|
||||
color: var(--nn_placeholder) !important;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: "Open Sans";
|
||||
color: var(--nn_heading);
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
<meta name="description" content="Notesnook editor for mobile" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Notesnook editor</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
15
packages/editor-mobile/public/manifest.json
Normal file
15
packages/editor-mobile/public/manifest.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "Notesnook editor",
|
||||
"name": "Notesnook editor for mobile",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
1
packages/editor-mobile/public/placeholder.svg
Normal file
1
packages/editor-mobile/public/placeholder.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 135.47 135.47"><g fill="gray"><path d="M65.63 65.86a4.48 4.48 0 1 0-.01-8.96 4.48 4.48 0 0 0 0 8.96zm0-6.33a1.85 1.85 0 1 1 0 3.7 1.85 1.85 0 0 1 0-3.7zm0 0"/><path d="M88.49 48.53H46.98c-.9 0-1.64.73-1.64 1.64V85.3c0 .9.74 1.64 1.64 1.64h41.5c.91 0 1.64-.74 1.64-1.64V50.17c0-.9-.73-1.64-1.63-1.64Zm-.99 2.62v20.77l-8.25-8.25a1.38 1.38 0 0 0-1.95 0L65.63 75.34l-7.46-7.46a1.37 1.37 0 0 0-1.95 0l-8.25 8.25V51.15ZM47.97 84.31v-4.47l9.22-9.22 7.46 7.45a1.37 1.37 0 0 0 1.95 0L78.27 66.4l9.23 9.23v8.68zm0 0"/></g></svg>
|
||||
|
After Width: | Height: | Size: 571 B |
3
packages/editor-mobile/public/robots.txt
Normal file
3
packages/editor-mobile/public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
15
packages/editor-mobile/src/App.css
Normal file
15
packages/editor-mobile/src/App.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.App {
|
||||
flex:1;
|
||||
}
|
||||
|
||||
.ProseMirror:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ProseMirror p.is-editor-empty:first-child::before {
|
||||
color: #a9a9a9;
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
8
packages/editor-mobile/src/App.test.js
Normal file
8
packages/editor-mobile/src/App.test.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import App from "./App";
|
||||
|
||||
test("renders learn react link", () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
8
packages/editor-mobile/src/App.tsx
Normal file
8
packages/editor-mobile/src/App.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import "./App.css";
|
||||
import Tiptap from "./components/editor";
|
||||
|
||||
function App() {
|
||||
return <Tiptap />;
|
||||
}
|
||||
|
||||
export default App;
|
||||
48
packages/editor-mobile/src/components/button.tsx
Normal file
48
packages/editor-mobile/src/components/button.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Editor } from "@notesnook/editor";
|
||||
|
||||
type ButtonType = {
|
||||
editor: Editor | null;
|
||||
title: string;
|
||||
onPress: () => void;
|
||||
marginRight?: number;
|
||||
activeKey: string;
|
||||
};
|
||||
|
||||
export default function Button({
|
||||
editor,
|
||||
title,
|
||||
onPress,
|
||||
marginRight = 10,
|
||||
activeKey
|
||||
}: ButtonType) {
|
||||
const active = editor?.isActive(activeKey);
|
||||
|
||||
return (
|
||||
<button
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderWidth: 0,
|
||||
background: active ? "var(--nn_transGray)" : "var(--nn_nav)",
|
||||
borderRadius: 5,
|
||||
fontWeight: "bold",
|
||||
userSelect: "none",
|
||||
color: active ? "var(--nn_accent)" : "var(--nn_pri)",
|
||||
marginRight: marginRight,
|
||||
fontSize: 18
|
||||
}}
|
||||
onMouseUp={(e) => {
|
||||
e.preventDefault();
|
||||
onPress();
|
||||
}}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onTouchEnd={(e) => {
|
||||
e.preventDefault();
|
||||
onPress();
|
||||
}}
|
||||
className={active ? "is-active" : ""}
|
||||
>
|
||||
{title}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
259
packages/editor-mobile/src/components/editor.tsx
Normal file
259
packages/editor-mobile/src/components/editor.tsx
Normal file
@@ -0,0 +1,259 @@
|
||||
import {
|
||||
Editor,
|
||||
PortalProvider,
|
||||
Toolbar,
|
||||
usePermissionHandler,
|
||||
useTiptap
|
||||
} from "@notesnook/editor";
|
||||
import { Theme, useTheme } from "@notesnook/theme";
|
||||
import {
|
||||
forwardRef,
|
||||
memo,
|
||||
useCallback,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { useEditorController } from "../hooks/useEditorController";
|
||||
import { useSettings } from "../hooks/useSettings";
|
||||
import { useEditorThemeStore } from "../state/theme";
|
||||
import { EventTypes, Settings } from "../utils";
|
||||
import Header from "./header";
|
||||
import StatusBar from "./statusbar";
|
||||
import Tags from "./tags";
|
||||
import Title from "./title";
|
||||
|
||||
const Tiptap = ({
|
||||
editorTheme,
|
||||
toolbarTheme,
|
||||
settings
|
||||
}: {
|
||||
editorTheme: Theme;
|
||||
toolbarTheme: Theme;
|
||||
settings: Settings;
|
||||
}) => {
|
||||
const [tick, setTick] = useState(0);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [layout, setLayout] = useState(false);
|
||||
usePermissionHandler({
|
||||
claims: {
|
||||
premium: settings.premium
|
||||
},
|
||||
onPermissionDenied: () => {
|
||||
post(EventTypes.pro);
|
||||
}
|
||||
});
|
||||
const _editor = useTiptap(
|
||||
{
|
||||
onUpdate: ({ editor }) => {
|
||||
global.editorController.contentChange(editor as Editor);
|
||||
},
|
||||
onSelectionUpdate: (props) => {
|
||||
props.transaction.scrollIntoView();
|
||||
},
|
||||
onOpenAttachmentPicker: (editor, type) => {
|
||||
global.editorController.openFilePicker(type);
|
||||
return true;
|
||||
},
|
||||
onDownloadAttachment: (editor, attachment) => {
|
||||
global.editorController.downloadAttachment(attachment);
|
||||
return true;
|
||||
},
|
||||
theme: editorTheme,
|
||||
element: !layout ? undefined : contentRef.current || undefined,
|
||||
editable: !settings.readonly,
|
||||
editorProps: {
|
||||
editable: () => !settings.readonly
|
||||
},
|
||||
content: global.editorController?.content?.current,
|
||||
isMobile: true,
|
||||
isKeyboardOpen: settings.keyboardShown,
|
||||
doubleSpacedLines: settings.doubleSpacedLines
|
||||
},
|
||||
[layout, settings.readonly, tick]
|
||||
);
|
||||
|
||||
const update = useCallback(() => {
|
||||
setTick((tick) => tick + 1);
|
||||
|
||||
globalThis.editorController.setTitlePlaceholder("Note title");
|
||||
}, []);
|
||||
|
||||
const controller = useEditorController(update);
|
||||
const controllerRef = useRef(controller);
|
||||
globalThis.editorController = controller;
|
||||
globalThis.editor = _editor;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setLayout(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
flexDirection: "column",
|
||||
maxWidth: "100vw",
|
||||
marginBottom: "5px"
|
||||
}}
|
||||
>
|
||||
<Header
|
||||
hasRedo={_editor?.can().redo() || false}
|
||||
hasUndo={_editor?.can().undo() || false}
|
||||
settings={settings}
|
||||
noHeader={settings.noHeader || false}
|
||||
/>
|
||||
<div
|
||||
onScroll={controller.scroll}
|
||||
ref={containerRef}
|
||||
style={{
|
||||
overflowY: "scroll",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
{settings.noHeader ? null : (
|
||||
<>
|
||||
<Tags />
|
||||
<Title
|
||||
titlePlaceholder={controller.titlePlaceholder}
|
||||
readonly={settings.readonly}
|
||||
controller={controllerRef}
|
||||
title={controller.title}
|
||||
/>
|
||||
<StatusBar container={containerRef} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<ContentDiv
|
||||
padding={settings.doubleSpacedLines ? 0 : 6}
|
||||
ref={contentRef}
|
||||
/>
|
||||
|
||||
<div
|
||||
onDoubleClick={() => {
|
||||
const lastPosition = globalThis.editor?.state.doc.content.size;
|
||||
if (!lastPosition) return;
|
||||
globalThis.editor
|
||||
?.chain()
|
||||
.insertContentAt(lastPosition - 1, "<p></p>", {
|
||||
updateSelection: true
|
||||
})
|
||||
.run();
|
||||
setTimeout(() => {
|
||||
globalThis.editor?.commands.focus();
|
||||
}, 1);
|
||||
}}
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
height: 150,
|
||||
width: "100%"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{settings.noToolbar || !layout ? null : (
|
||||
<Toolbar
|
||||
sx={{ pl: "10px", pt: "5px", minHeight: 45 }}
|
||||
theme={toolbarTheme}
|
||||
editor={_editor}
|
||||
location="bottom"
|
||||
tools={[...settings.tools]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ContentDiv = memo(
|
||||
forwardRef<HTMLDivElement, { padding: number }>((props, ref) => {
|
||||
const theme = useEditorThemeStore((state) => state.colors);
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
padding: 12,
|
||||
paddingTop: props.padding,
|
||||
flex: 1,
|
||||
color: theme.pri,
|
||||
marginTop: -12,
|
||||
caretColor: theme.accent
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
() => true
|
||||
);
|
||||
|
||||
const modifyToolbarTheme = (toolbarTheme: Theme) => {
|
||||
toolbarTheme.space = [0, 10, 12, 18];
|
||||
toolbarTheme.space.small = "10px";
|
||||
|
||||
toolbarTheme.buttons.menuitem = {
|
||||
...toolbarTheme.buttons.menuitem,
|
||||
height: "50px",
|
||||
paddingX: "20px",
|
||||
borderBottomWidth: 0
|
||||
};
|
||||
|
||||
toolbarTheme.iconSizes = {
|
||||
big: 20,
|
||||
medium: 18,
|
||||
small: 18
|
||||
};
|
||||
toolbarTheme.fontSizes = {
|
||||
...toolbarTheme.fontSizes,
|
||||
subBody: "0.8rem",
|
||||
body: "0.9rem"
|
||||
};
|
||||
|
||||
toolbarTheme.radii = {
|
||||
...toolbarTheme.radii,
|
||||
small: 5
|
||||
};
|
||||
|
||||
toolbarTheme.buttons.menuitem = {
|
||||
...toolbarTheme.buttons.menuitem,
|
||||
px: 5,
|
||||
height: "45px"
|
||||
};
|
||||
};
|
||||
|
||||
const TiptapProvider = () => {
|
||||
const settings = useSettings();
|
||||
const theme = useEditorThemeStore((state) => state.colors);
|
||||
const toolbarTheme = useTheme({
|
||||
//todo
|
||||
accent: theme?.accent,
|
||||
scale: 1,
|
||||
theme: theme?.night ? "dark" : "light"
|
||||
});
|
||||
modifyToolbarTheme(toolbarTheme);
|
||||
const editorTheme = useTheme({
|
||||
//todo
|
||||
accent: theme?.accent,
|
||||
scale: 1,
|
||||
theme: theme?.night ? "dark" : "light"
|
||||
});
|
||||
editorTheme.colors.background = theme?.bg || "#f0f0f0";
|
||||
editorTheme.space = [0, 10, 12, 20];
|
||||
|
||||
return (
|
||||
<PortalProvider>
|
||||
<Tiptap
|
||||
editorTheme={editorTheme}
|
||||
toolbarTheme={toolbarTheme}
|
||||
settings={settings}
|
||||
/>
|
||||
</PortalProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default TiptapProvider;
|
||||
305
packages/editor-mobile/src/components/header.tsx
Normal file
305
packages/editor-mobile/src/components/header.tsx
Normal file
@@ -0,0 +1,305 @@
|
||||
import ArrowBackIcon from "mdi-react/ArrowBackIcon";
|
||||
import CloudUploadOutlineIcon from "mdi-react/CloudUploadOutlineIcon";
|
||||
import CrownIcon from "mdi-react/CrownIcon";
|
||||
import DotsHorizontalIcon from "mdi-react/DotsHorizontalIcon";
|
||||
import ArrowULeftTopIcon from "mdi-react/ArrowULeftTopIcon";
|
||||
import ArrowURightTopIcon from "mdi-react/ArrowURightTopIcon";
|
||||
import FullscreenIcon from "mdi-react/FullscreenIcon";
|
||||
import MagnifyIcon from "mdi-react/MagnifyIcon";
|
||||
import React from "react";
|
||||
import { useSafeArea } from "../hooks/useSafeArea";
|
||||
import { EventTypes, Settings } from "../utils";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
const Button = ({
|
||||
onPress,
|
||||
children,
|
||||
style,
|
||||
preventDefault = true
|
||||
}: {
|
||||
onPress: () => void;
|
||||
children: React.ReactNode;
|
||||
style: React.CSSProperties;
|
||||
preventDefault?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
className={styles.btn_header}
|
||||
style={style}
|
||||
onMouseDown={(e) => {
|
||||
if (preventDefault) e.preventDefault();
|
||||
onPress();
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Header({
|
||||
noHeader,
|
||||
settings,
|
||||
hasUndo,
|
||||
hasRedo
|
||||
}: {
|
||||
noHeader: boolean;
|
||||
settings: Settings;
|
||||
hasUndo: boolean;
|
||||
hasRedo: boolean;
|
||||
}) {
|
||||
const insets = useSafeArea();
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: noHeader ? `${insets.top}px` : `${50 + insets.top}px`,
|
||||
backgroundColor: "var(--nn_bg)",
|
||||
position: "sticky",
|
||||
width: "100vw"
|
||||
}}
|
||||
>
|
||||
{noHeader ? null : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
paddingTop: insets.top,
|
||||
height: 50,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
|
||||
<div />
|
||||
) : (
|
||||
<Button
|
||||
onPress={() => {
|
||||
post(EventTypes.back);
|
||||
}}
|
||||
preventDefault={false}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginLeft: 6,
|
||||
width: 40,
|
||||
height: 40,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon
|
||||
size={27}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_pri)"
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.undo();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<ArrowULeftTopIcon
|
||||
color={!hasUndo ? "var(--nn_nav)" : "var(--nn_pri)"}
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.redo();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<ArrowURightTopIcon
|
||||
color={!hasRedo ? "var(--nn_nav)" : "var(--nn_pri)"}
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
{!settings.premium && (
|
||||
<Button
|
||||
onPress={() => {
|
||||
post(EventTypes.pro);
|
||||
}}
|
||||
preventDefault={false}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<CrownIcon
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="orange"
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.startSearch();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<MagnifyIcon
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_pri)"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
onPress={() => {
|
||||
post(EventTypes.monograph);
|
||||
}}
|
||||
preventDefault={false}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<CloudUploadOutlineIcon
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_pri)"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
|
||||
<Button
|
||||
onPress={() => {
|
||||
post(EventTypes.fullscreen);
|
||||
}}
|
||||
preventDefault={false}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<FullscreenIcon
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_pri)"
|
||||
/>
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
post(EventTypes.properties);
|
||||
}}
|
||||
preventDefault={false}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 12,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<DotsHorizontalIcon
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_pri)"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
99
packages/editor-mobile/src/components/statusbar.tsx
Normal file
99
packages/editor-mobile/src/components/statusbar.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React, { RefObject, useEffect, useRef, useState } from "react";
|
||||
|
||||
function StatusBar({ container }: { container: RefObject<HTMLDivElement> }) {
|
||||
const [status, setStatus] = useState({
|
||||
date: "",
|
||||
saved: ""
|
||||
});
|
||||
const [sticky, setSticky] = useState(false);
|
||||
const stickyRef = useRef(false);
|
||||
const prevScroll = useRef(0);
|
||||
const lastStickyChangeTime = useRef(0);
|
||||
const [words, setWords] = useState("0 words");
|
||||
const currentWords = useRef(words);
|
||||
const interval = useRef(0);
|
||||
const statusBar = useRef({
|
||||
set: setStatus
|
||||
});
|
||||
globalThis.statusBar = statusBar;
|
||||
|
||||
const onScroll = React.useCallback((event) => {
|
||||
const currentOffset = event.target.scrollTop;
|
||||
if (currentOffset < 200) {
|
||||
if (stickyRef.current) {
|
||||
stickyRef.current = false;
|
||||
setSticky(false);
|
||||
lastStickyChangeTime.current = Date.now();
|
||||
prevScroll.current = currentOffset;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (Date.now() - lastStickyChangeTime.current < 300) return;
|
||||
if (currentOffset > prevScroll.current) {
|
||||
setSticky(false);
|
||||
stickyRef.current = false;
|
||||
} else {
|
||||
setSticky(true);
|
||||
stickyRef.current = true;
|
||||
}
|
||||
lastStickyChangeTime.current = Date.now();
|
||||
prevScroll.current = currentOffset;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
currentWords.current = words;
|
||||
}, [words]);
|
||||
|
||||
useEffect(() => {
|
||||
clearInterval(interval.current);
|
||||
interval.current = setInterval(() => {
|
||||
const words = editor?.storage?.characterCount?.words() + " words";
|
||||
if (currentWords.current === words) return;
|
||||
setWords(words);
|
||||
}, 3000) as unknown as number;
|
||||
return () => {
|
||||
clearInterval(interval.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const node = container.current;
|
||||
node?.addEventListener("scroll", onScroll);
|
||||
return () => {
|
||||
node?.removeEventListener("scroll", onScroll);
|
||||
};
|
||||
}, [onScroll, container]);
|
||||
|
||||
const paragraphStyle = {
|
||||
marginTop: 0,
|
||||
marginBottom: 0,
|
||||
fontSize: "12px",
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 8,
|
||||
paddingBottom: 0
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
display: "flex",
|
||||
paddingRight: 12,
|
||||
paddingLeft: 12,
|
||||
position: sticky ? "sticky" : "relative",
|
||||
top: -3,
|
||||
backgroundColor: "var(--nn_bg)",
|
||||
zIndex: 1,
|
||||
justifyContent: sticky ? "center" : "flex-start",
|
||||
paddingTop: 2,
|
||||
paddingBottom: 2
|
||||
}}
|
||||
>
|
||||
<p style={paragraphStyle}>{words}</p>
|
||||
<p style={paragraphStyle}>{status.date}</p>
|
||||
<p style={paragraphStyle}>{status.saved}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(StatusBar, () => true);
|
||||
21
packages/editor-mobile/src/components/styles.module.css
Normal file
21
packages/editor-mobile/src/components/styles.module.css
Normal file
@@ -0,0 +1,21 @@
|
||||
.btn {
|
||||
background-color: var(--nn_nav);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
background-color: var(--nn_transGray);
|
||||
}
|
||||
|
||||
.btn_header {
|
||||
width:40;
|
||||
height:40;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.btn_header:active {
|
||||
background-color: var(--nn_transGray);
|
||||
}
|
||||
|
||||
.titleBar::placeholder {
|
||||
color: var(--nn_placeholder)
|
||||
}
|
||||
109
packages/editor-mobile/src/components/tags.tsx
Normal file
109
packages/editor-mobile/src/components/tags.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { EventTypes } from "../utils";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
export default function Tags() {
|
||||
const [tags, setTags] = useState<{ title: string; alias: string }[]>([]);
|
||||
const editorTags = useRef({
|
||||
setTags: setTags
|
||||
});
|
||||
|
||||
global.editorTags = editorTags;
|
||||
|
||||
const openManageTagsSheet = () => {
|
||||
if (editor?.isFocused) {
|
||||
editor.commands.blur();
|
||||
editorTitle.current?.blur();
|
||||
}
|
||||
post(EventTypes.newtag);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
padding: "0px 12px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<button
|
||||
className={styles.btn}
|
||||
onMouseUp={(e) => {
|
||||
e.preventDefault();
|
||||
openManageTagsSheet();
|
||||
}}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onTouchEnd={(e) => {
|
||||
e.preventDefault();
|
||||
openManageTagsSheet();
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
backgroundColor: "var(--nn_nav)",
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
padding: "0px 10px",
|
||||
fontFamily: "Open Sans",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: "30px"
|
||||
}}
|
||||
>
|
||||
{tags.length === 0 ? (
|
||||
<p
|
||||
style={{
|
||||
marginRight: 4,
|
||||
fontSize: 13,
|
||||
color: "var(--nn_icon)",
|
||||
userSelect: "none"
|
||||
}}
|
||||
>
|
||||
Add a tag
|
||||
</p>
|
||||
) : null}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="var(--nn_accent)"
|
||||
d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{tags.map((tag) => (
|
||||
<button
|
||||
key={tag.title}
|
||||
className={styles.btn}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
backgroundColor: "var(--nn_nav)",
|
||||
marginRight: 5,
|
||||
borderRadius: 100,
|
||||
padding: "0px 10px",
|
||||
height: "30px",
|
||||
fontFamily: "Open Sans",
|
||||
fontSize: 13,
|
||||
color: "var(--nn_icon)"
|
||||
}}
|
||||
onMouseUp={(e) => {
|
||||
e.preventDefault();
|
||||
post(EventTypes.tag, tag.title);
|
||||
}}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onTouchEnd={(e) => {
|
||||
e.preventDefault();
|
||||
post(EventTypes.tag, tag.title);
|
||||
}}
|
||||
>
|
||||
#{tag.alias}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
63
packages/editor-mobile/src/components/title.tsx
Normal file
63
packages/editor-mobile/src/components/title.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import React from "react";
|
||||
import { RefObject, useEffect, useRef } from "react";
|
||||
import { EditorController } from "../hooks/useEditorController";
|
||||
import styles from "./styles.module.css";
|
||||
function Title({
|
||||
controller,
|
||||
title,
|
||||
titlePlaceholder,
|
||||
readonly,
|
||||
}: {
|
||||
controller: RefObject<EditorController>;
|
||||
title: string;
|
||||
titlePlaceholder: string;
|
||||
readonly: boolean;
|
||||
}) {
|
||||
const titleRef = useRef<HTMLInputElement>(null);
|
||||
const emitUpdate = useRef(true);
|
||||
global.editorTitle = titleRef;
|
||||
|
||||
useEffect(() => {
|
||||
if (titleRef.current) {
|
||||
emitUpdate.current = false;
|
||||
titleRef.current.value = title;
|
||||
emitUpdate.current = true;
|
||||
}
|
||||
}, [title]);
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={titleRef}
|
||||
className={styles.titleBar}
|
||||
contentEditable={!readonly}
|
||||
disabled={readonly}
|
||||
style={{
|
||||
height: 50,
|
||||
fontSize: 25,
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
borderWidth: 0,
|
||||
paddingRight: 12,
|
||||
paddingLeft: 12,
|
||||
fontWeight: 600,
|
||||
fontFamily: "Open Sans",
|
||||
backgroundColor: "transparent",
|
||||
color: "var(--nn_heading)",
|
||||
caretColor: "var(--nn_accent)",
|
||||
}}
|
||||
maxLength={150}
|
||||
onChange={(event) => {
|
||||
if (!emitUpdate.current) return;
|
||||
controller.current?.titleChange(event.target.value);
|
||||
}}
|
||||
placeholder={titlePlaceholder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Title, (prev, next) => {
|
||||
if (prev.title !== next.title) return false;
|
||||
if (prev.titlePlaceholder !== next.titlePlaceholder) return false;
|
||||
if (prev.readonly !== next.readonly) return false;
|
||||
return true;
|
||||
});
|
||||
152
packages/editor-mobile/src/hooks/useEditorController.ts
Normal file
152
packages/editor-mobile/src/hooks/useEditorController.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { Editor } from "@notesnook/editor";
|
||||
import {
|
||||
MutableRefObject,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { useEditorThemeStore } from "../state/theme";
|
||||
import { EventTypes, isReactNative, post } from "../utils";
|
||||
|
||||
type Attachment = {
|
||||
hash: string;
|
||||
filename: string;
|
||||
type: string;
|
||||
size: number;
|
||||
};
|
||||
|
||||
export type Selection = {
|
||||
[name: string]: {
|
||||
text?: string;
|
||||
length?: number;
|
||||
attributes?: Record<string, unknown>;
|
||||
type?: "mark" | "node";
|
||||
};
|
||||
};
|
||||
|
||||
type Timers = {
|
||||
selectionChange: NodeJS.Timeout | null;
|
||||
change: NodeJS.Timeout | null;
|
||||
};
|
||||
|
||||
export type EditorController = {
|
||||
selectionChange: (editor: Editor) => void;
|
||||
titleChange: (title: string) => void;
|
||||
contentChange: (editor: Editor) => void;
|
||||
scroll: (event: React.UIEvent<HTMLDivElement, UIEvent>) => void;
|
||||
title: string;
|
||||
setTitle: React.Dispatch<React.SetStateAction<string>>;
|
||||
openFilePicker: (type: "image" | "file" | "camera") => void;
|
||||
downloadAttachment: (attachment: Attachment) => void;
|
||||
content: MutableRefObject<string | null>;
|
||||
onUpdate: () => void;
|
||||
titlePlaceholder: string;
|
||||
setTitlePlaceholder: React.Dispatch<React.SetStateAction<string>>;
|
||||
};
|
||||
|
||||
export function useEditorController(update: () => void): EditorController {
|
||||
const [title, setTitle] = useState("");
|
||||
const [titlePlaceholder, setTitlePlaceholder] = useState("Note title");
|
||||
const htmlContentRef = useRef<string | null>(null);
|
||||
const timers = useRef<Timers>({
|
||||
selectionChange: null,
|
||||
change: null
|
||||
});
|
||||
|
||||
const selectionChange = useCallback((_editor: Editor) => {}, []);
|
||||
|
||||
const titleChange = useCallback((title: string) => {
|
||||
post(EventTypes.title, title);
|
||||
}, []);
|
||||
|
||||
const contentChange = useCallback((editor: Editor) => {
|
||||
if (!editor) return;
|
||||
if (typeof timers.current.change === "number") {
|
||||
clearTimeout(timers.current?.change);
|
||||
}
|
||||
timers.current.change = setTimeout(() => {
|
||||
htmlContentRef.current = editor.getHTML();
|
||||
post(EventTypes.content, htmlContentRef.current);
|
||||
}, 300);
|
||||
}, []);
|
||||
|
||||
const scroll = useCallback(
|
||||
(_event: React.UIEvent<HTMLDivElement, UIEvent>) => {},
|
||||
[]
|
||||
);
|
||||
|
||||
const onUpdate = useCallback(() => {
|
||||
update();
|
||||
}, [update]);
|
||||
|
||||
const onMessage = useCallback(
|
||||
(event: Event & { data?: string }) => {
|
||||
if (event?.data?.[0] !== "{") return;
|
||||
|
||||
const message = JSON.parse(event.data);
|
||||
const type = message.type;
|
||||
const value = message.value;
|
||||
global.sessionId = message.sessionId;
|
||||
switch (type) {
|
||||
case "native:html":
|
||||
htmlContentRef.current = value;
|
||||
update();
|
||||
break;
|
||||
case "native:theme":
|
||||
useEditorThemeStore.getState().setColors(message.value);
|
||||
break;
|
||||
case "native:title":
|
||||
setTitle(value);
|
||||
break;
|
||||
case "native:titleplaceholder":
|
||||
setTitlePlaceholder(value);
|
||||
break;
|
||||
case "native:status":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
post(type); // Notify that message was delivered successfully.
|
||||
},
|
||||
[update]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReactNative()) return; // Subscribe only in react native webview.
|
||||
const isSafari = navigator.vendor.match(/apple/i);
|
||||
let root: Document | Window = document;
|
||||
if (isSafari) {
|
||||
root = window;
|
||||
}
|
||||
console.log("recreating messaging");
|
||||
root.addEventListener("message", onMessage);
|
||||
|
||||
return () => {
|
||||
root.removeEventListener("message", onMessage);
|
||||
};
|
||||
}, [onMessage]);
|
||||
|
||||
const openFilePicker = useCallback((type) => {
|
||||
post(EventTypes.filepicker, type);
|
||||
}, []);
|
||||
|
||||
const downloadAttachment = useCallback((attachment: Attachment) => {
|
||||
post(EventTypes.download, attachment);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
contentChange,
|
||||
selectionChange,
|
||||
titleChange,
|
||||
scroll,
|
||||
title,
|
||||
setTitle,
|
||||
titlePlaceholder,
|
||||
setTitlePlaceholder,
|
||||
openFilePicker,
|
||||
downloadAttachment,
|
||||
content: htmlContentRef,
|
||||
onUpdate: onUpdate
|
||||
};
|
||||
}
|
||||
31
packages/editor-mobile/src/hooks/useSafeArea.ts
Normal file
31
packages/editor-mobile/src/hooks/useSafeArea.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useState } from "react";
|
||||
|
||||
const insetsStorage = localStorage.getItem("safeAreaInsets");
|
||||
const initialState =
|
||||
insetsStorage && !globalThis.noHeader
|
||||
? JSON.parse(insetsStorage)
|
||||
: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
};
|
||||
|
||||
global.safeAreaController = {
|
||||
update: (safeArea) => {
|
||||
if (safeAreaController.set) safeAreaController.set(safeArea);
|
||||
safeAreaController.previous = safeArea;
|
||||
localStorage.setItem("safeAreaInsets", JSON.stringify(safeArea));
|
||||
},
|
||||
reset: () => {
|
||||
if (safeAreaController.set) safeAreaController.set(initialState);
|
||||
},
|
||||
previous: initialState,
|
||||
};
|
||||
|
||||
export const useSafeArea = () => {
|
||||
const [safeArea, setSafeArea] = useState(global.safeAreaController.previous);
|
||||
global.safeAreaController.set = setSafeArea;
|
||||
|
||||
return safeArea;
|
||||
};
|
||||
52
packages/editor-mobile/src/hooks/useSettings.ts
Normal file
52
packages/editor-mobile/src/hooks/useSettings.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { getDefaultPresets } from "@notesnook/editor";
|
||||
import { useState } from "react";
|
||||
|
||||
const settingsJson = localStorage.getItem("editorSettings");
|
||||
const initialState = {
|
||||
fullscreen: false,
|
||||
deviceMode: "mobile",
|
||||
premium: false,
|
||||
tools: JSON.parse(JSON.stringify(getDefaultPresets().default)),
|
||||
noToolbar: globalThis.noToolbar,
|
||||
noHeader: globalThis.noHeader,
|
||||
readonly: globalThis.readonly,
|
||||
doubleSpacedLines: true
|
||||
};
|
||||
|
||||
global.settingsController = {
|
||||
update: (settings) => {
|
||||
const nextSettings = {
|
||||
...settings,
|
||||
noToolbar: globalThis.noToolbar || settings.noToolbar,
|
||||
noHeader: globalThis.noHeader || settings.noHeader,
|
||||
readonly: globalThis.readonly || settings.readonly
|
||||
};
|
||||
if (
|
||||
JSON.stringify(nextSettings) ===
|
||||
JSON.stringify(global.settingsController.previous)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (global.settingsController.set)
|
||||
global.settingsController.set(nextSettings);
|
||||
if (settings) {
|
||||
localStorage.setItem("editorSettings", JSON.stringify(nextSettings));
|
||||
} else {
|
||||
localStorage.removeItem("editorSettings");
|
||||
}
|
||||
settingsController.previous = { ...nextSettings };
|
||||
},
|
||||
previous: settingsJson ? JSON.parse(settingsJson) : { ...initialState }
|
||||
};
|
||||
global.settingsController.previous.noHeader = globalThis.noHeader;
|
||||
global.settingsController.previous.noToolbar = globalThis.noToolbar;
|
||||
global.settingsController.previous.readonly = globalThis.readonly;
|
||||
|
||||
export const useSettings = () => {
|
||||
const [settings, setSettings] = useState({
|
||||
...global.settingsController.previous
|
||||
});
|
||||
global.settingsController.set = setSettings;
|
||||
|
||||
return settings;
|
||||
};
|
||||
22
packages/editor-mobile/src/index.css
Normal file
22
packages/editor-mobile/src/index.css
Normal file
@@ -0,0 +1,22 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
input:active,
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
16
packages/editor-mobile/src/index.tsx
Normal file
16
packages/editor-mobile/src/index.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import "@notesnook/editor/styles/katex.min.css";
|
||||
import "@notesnook/editor/styles/prism-theme.css";
|
||||
import "@notesnook/editor/styles/fonts.mobile.css";
|
||||
import "@notesnook/editor/styles/katex-fonts.mobile.css";
|
||||
import "@notesnook/editor/styles/styles.css";
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
1
packages/editor-mobile/src/logo.svg
Normal file
1
packages/editor-mobile/src/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
1
packages/editor-mobile/src/react-app-env.d.ts
vendored
Normal file
1
packages/editor-mobile/src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
5
packages/editor-mobile/src/setupTests.js
Normal file
5
packages/editor-mobile/src/setupTests.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import "@testing-library/jest-dom";
|
||||
81
packages/editor-mobile/src/state/theme.tsx
Normal file
81
packages/editor-mobile/src/state/theme.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import create, { State } from "zustand";
|
||||
import { injectCss, transform } from "../utils/css";
|
||||
export type Colors = {
|
||||
night: boolean;
|
||||
bg: string;
|
||||
navbg: string;
|
||||
input: string;
|
||||
nav: string;
|
||||
heading: string;
|
||||
pri: string;
|
||||
sec: string;
|
||||
light: string;
|
||||
transGray: string;
|
||||
accent: string;
|
||||
shade: string;
|
||||
fg: string;
|
||||
normal: string;
|
||||
errorText: string;
|
||||
successBg: string;
|
||||
successText: string;
|
||||
warningBg: string;
|
||||
warningText: string;
|
||||
red: string;
|
||||
orange: string;
|
||||
yellow: string;
|
||||
green: string;
|
||||
blue: string;
|
||||
purple: string;
|
||||
gray: string;
|
||||
discord: string;
|
||||
border: string;
|
||||
placeholder: string;
|
||||
};
|
||||
|
||||
const DefaultColors = {
|
||||
accent: "#008837",
|
||||
shade: "#00883712",
|
||||
fg: "#008837",
|
||||
normal: "black",
|
||||
icon: "gray",
|
||||
transGray: "#00000010",
|
||||
errorBg: "#FFB6C1",
|
||||
errorText: "#ff6961",
|
||||
successBg: "#DFF2BF",
|
||||
successText: "#4F8A10",
|
||||
warningBg: "#FF990020",
|
||||
warningText: "#FF9900",
|
||||
red: "#f44336",
|
||||
orange: "#FF9800",
|
||||
yellow: "#FFD600",
|
||||
green: "#4CAF50",
|
||||
blue: "#2196F3",
|
||||
purple: "#673AB7",
|
||||
gray: "#9E9E9E",
|
||||
discord: "#5865F2",
|
||||
night: false,
|
||||
bg: "#ffffff",
|
||||
navbg: "#f7f7f7",
|
||||
nav: "#f7f7f7",
|
||||
pri: "#424242",
|
||||
sec: "white",
|
||||
light: "#ffffff",
|
||||
input: "transparent",
|
||||
heading: "#212121",
|
||||
border: "#E8E8E8",
|
||||
placeholder: "#a9a9a9"
|
||||
};
|
||||
|
||||
injectCss(transform(DefaultColors));
|
||||
interface ThemeState extends State {
|
||||
colors: Colors;
|
||||
setColors: (colors: Colors) => void;
|
||||
}
|
||||
|
||||
export const useEditorThemeStore = create<ThemeState>((set) => ({
|
||||
colors: DefaultColors,
|
||||
setColors: (colors) => {
|
||||
injectCss(transform(colors));
|
||||
set({ colors: colors });
|
||||
}
|
||||
}));
|
||||
54
packages/editor-mobile/src/utils/css.js
Normal file
54
packages/editor-mobile/src/utils/css.js
Normal file
@@ -0,0 +1,54 @@
|
||||
export function removeCss(id) {
|
||||
var link = document.getElementById(id);
|
||||
link.remove();
|
||||
}
|
||||
|
||||
export function injectCssSrc(id, src) {
|
||||
var head = document.head;
|
||||
var link = document.createElement("link");
|
||||
|
||||
link.id = id;
|
||||
link.type = "text/css";
|
||||
link.rel = "stylesheet";
|
||||
link.href = src;
|
||||
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
export function injectCss(rule) {
|
||||
let variableCss = document.getElementById("variables-nn");
|
||||
let head = document.getElementsByTagName("head")[0];
|
||||
if (variableCss) {
|
||||
head.removeChild(variableCss);
|
||||
}
|
||||
let css = document.createElement("style");
|
||||
css.type = "text/css";
|
||||
css.id = "variables-nn";
|
||||
// Support for IE
|
||||
if (css.styleSheet) css.styleSheet.cssText = rule;
|
||||
// Support for the rest
|
||||
else css.appendChild(document.createTextNode(rule));
|
||||
head.insertBefore(css, getRootStylesheet());
|
||||
}
|
||||
|
||||
function getRootStylesheet() {
|
||||
for (let sty of document.getElementsByTagName("style")) {
|
||||
if (sty.innerHTML.includes("#root")) {
|
||||
return sty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function changeSvgTheme(newAccent) {
|
||||
var nodes = document.querySelectorAll('*[fill="#0560ff"]');
|
||||
for (var n = 0; n < nodes.length; ++n)
|
||||
nodes[n].setAttribute("fill", newAccent);
|
||||
}
|
||||
|
||||
export function transform(colors) {
|
||||
let root = ":root {";
|
||||
for (let color in colors) {
|
||||
root += `--nn_${color}: ${colors[color]};`;
|
||||
}
|
||||
return root + "}";
|
||||
}
|
||||
160
packages/editor-mobile/src/utils/index.ts
Normal file
160
packages/editor-mobile/src/utils/index.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { ToolbarGroupDefinition } from "@notesnook/editor";
|
||||
import { Editor } from "@notesnook/editor";
|
||||
import { Dispatch, MutableRefObject, RefObject, SetStateAction } from "react";
|
||||
import { useEditorController } from "../hooks/useEditorController";
|
||||
|
||||
export type SafeAreaType = {
|
||||
top: number;
|
||||
left: number;
|
||||
bottom: number;
|
||||
right: number;
|
||||
};
|
||||
|
||||
export type Settings = {
|
||||
readonly: boolean;
|
||||
fullscreen: boolean;
|
||||
deviceMode: "mobile" | "smallTablet" | "tablet";
|
||||
premium: boolean;
|
||||
tools: ToolbarGroupDefinition[];
|
||||
noToolbar?: boolean;
|
||||
noHeader?: boolean;
|
||||
keyboardShown?: boolean;
|
||||
doubleSpacedLines?: boolean;
|
||||
};
|
||||
|
||||
/* eslint-disable no-var */
|
||||
declare global {
|
||||
var statusBar: React.MutableRefObject<{
|
||||
set: React.Dispatch<
|
||||
React.SetStateAction<{
|
||||
date: string;
|
||||
saved: string;
|
||||
}>
|
||||
>;
|
||||
}>;
|
||||
var readonly: boolean;
|
||||
var noToolbar: boolean;
|
||||
var noHeader: boolean;
|
||||
/**
|
||||
* Id of current session
|
||||
*/
|
||||
var sessionId: string;
|
||||
/**
|
||||
* Current tiptap instance
|
||||
*/
|
||||
var editor: Editor | null;
|
||||
/**
|
||||
* Current editor controller
|
||||
*/
|
||||
var editorController: ReturnType<typeof useEditorController>;
|
||||
|
||||
var settingsController: {
|
||||
update: (settings: Settings) => void;
|
||||
previous: Settings;
|
||||
set?: Dispatch<SetStateAction<Settings>>;
|
||||
};
|
||||
|
||||
var premiumController: {
|
||||
update: (premium: boolean) => void;
|
||||
previous: boolean;
|
||||
set?: Dispatch<SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
var safeAreaController: {
|
||||
update: (insets: SafeAreaType) => void;
|
||||
reset: () => void;
|
||||
previous: SafeAreaType;
|
||||
set?: Dispatch<
|
||||
SetStateAction<{
|
||||
top: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
right: number;
|
||||
}>
|
||||
>;
|
||||
};
|
||||
|
||||
var editorTitle: RefObject<HTMLInputElement>;
|
||||
|
||||
/**
|
||||
* Global ref to manage tags in editor.
|
||||
*/
|
||||
var editorTags: MutableRefObject<{
|
||||
setTags: React.Dispatch<
|
||||
React.SetStateAction<{ title: string; alias: string }[]>
|
||||
>;
|
||||
}>;
|
||||
|
||||
function logger(type: "info" | "warn" | "error", ...logs: never[]): void;
|
||||
/**
|
||||
* Function to post message to react native
|
||||
* @param type
|
||||
* @param value
|
||||
*/
|
||||
|
||||
function post<T extends keyof typeof EventTypes>(
|
||||
type: typeof EventTypes[T],
|
||||
value?: unknown
|
||||
): void;
|
||||
interface Window {
|
||||
/**
|
||||
* React Native WebView
|
||||
*/
|
||||
ReactNativeWebView: {
|
||||
postMessage: (data: string) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-var */
|
||||
|
||||
export const EventTypes = {
|
||||
selection: "editor-event:selection",
|
||||
content: "editor-event:content",
|
||||
title: "editor-event:title",
|
||||
scroll: "editor-event:scroll",
|
||||
history: "editor-event:history",
|
||||
newtag: "editor-event:newtag",
|
||||
tag: "editor-event:tag",
|
||||
filepicker: "editor-event:picker",
|
||||
download: "editor-event:download-attachment",
|
||||
logger: "native:logger",
|
||||
back: "editor-event:back",
|
||||
pro: "editor-event:pro",
|
||||
monograph: "editor-event:monograph",
|
||||
properties: "editor-event:properties",
|
||||
fullscreen: "editor-event:fullscreen"
|
||||
} as const;
|
||||
|
||||
export function isReactNative(): boolean {
|
||||
return !!window.ReactNativeWebView;
|
||||
}
|
||||
|
||||
export function logger(type: "info" | "warn" | "error", ...logs: never[]) {
|
||||
const logString = logs
|
||||
.map((log) => {
|
||||
return typeof log !== "string" ? JSON.stringify(log) : log;
|
||||
})
|
||||
.join(" ");
|
||||
|
||||
post(EventTypes.logger, `[${type}]: ` + logString);
|
||||
}
|
||||
|
||||
export function post<T extends keyof typeof EventTypes>(
|
||||
type: typeof EventTypes[T],
|
||||
value?: unknown
|
||||
) {
|
||||
if (isReactNative()) {
|
||||
window.ReactNativeWebView.postMessage(
|
||||
JSON.stringify({
|
||||
type,
|
||||
value: value,
|
||||
sessionId: globalThis.sessionId
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.log(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.logger = logger;
|
||||
globalThis.post = post;
|
||||
19
packages/editor-mobile/tsconfig.json
Normal file
19
packages/editor-mobile/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"jsx": "react-jsx",
|
||||
"noEmit": true,
|
||||
"downlevelIteration": true,
|
||||
"maxNodeModuleJsDepth": 5,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { ThemeConfig } from "./types";
|
||||
|
||||
export type Theme = {
|
||||
breakpoints: string[];
|
||||
space: number[] & { small?: number };
|
||||
space: number[] & { small?: number | string };
|
||||
sizes: { full: "100%"; half: "50%" };
|
||||
radii: {
|
||||
none: number;
|
||||
@@ -41,13 +41,13 @@ class ThemeFactory {
|
||||
shadows:
|
||||
config.theme === "dark"
|
||||
? {
|
||||
menu: "0px 0px 10px 0px #00000078",
|
||||
menu: "0px 0px 10px 0px #00000078"
|
||||
}
|
||||
: {
|
||||
menu: "0px 0px 10px 0px #00000022",
|
||||
menu: "0px 0px 10px 0px #00000022"
|
||||
},
|
||||
...getFontConfig(config.scale),
|
||||
...variants,
|
||||
...variants
|
||||
};
|
||||
theme.space.small = 3;
|
||||
return theme;
|
||||
|
||||
Reference in New Issue
Block a user