mobile: refactor

This commit is contained in:
ammarahm-ed
2023-06-10 12:17:23 +05:00
committed by Abdullah Atta
parent c881e74aae
commit fb616a246e
92 changed files with 1110 additions and 1078 deletions

View File

@@ -16,8 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { database } from "@notesnook/common";
import Database from "@notesnook/core/api/index";
import { initalize, logger as dbLogger } from "@notesnook/core/logger"; import { initalize, logger as dbLogger } from "@notesnook/core/logger";
import { Platform } from "react-native"; import { Platform } from "react-native";
import { MMKVLoader } from "react-native-mmkv-storage"; import { MMKVLoader } from "react-native-mmkv-storage";
@@ -30,13 +29,8 @@ import * as Gzip from "react-native-gzip";
const LoggerStorage = new MMKVLoader() const LoggerStorage = new MMKVLoader()
.withInstanceID("notesnook_logs") .withInstanceID("notesnook_logs")
.initialize(); .initialize();
initalize(new KV(LoggerStorage), true);
export const DatabaseLogger = dbLogger;
/** database.setup(
* @type {import("@notesnook/core/api/index").default}
*/
export var db = new Database(
Storage, Storage,
Platform.OS === "ios" ? EventSource : AndroidEventSource, Platform.OS === "ios" ? EventSource : AndroidEventSource,
filesystem, filesystem,
@@ -46,7 +40,7 @@ export var db = new Database(
} }
); );
db.host( database.host(
__DEV__ __DEV__
? { ? {
API_HOST: "https://api.notesnook.com", API_HOST: "https://api.notesnook.com",
@@ -68,3 +62,8 @@ db.host(
ISSUES_HOST: "https://issues.streetwriters.co" ISSUES_HOST: "https://issues.streetwriters.co"
} }
); );
initalize(new KV(LoggerStorage), true);
export const db = database;
export const DatabaseLogger = dbLogger;

View File

@@ -34,7 +34,6 @@ import {
import PremiumService from "../../services/premium"; import PremiumService from "../../services/premium";
import { useAttachmentStore } from "../../stores/use-attachment-store"; import { useAttachmentStore } from "../../stores/use-attachment-store";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { formatBytes } from "../../utils";
import { eCloseAttachmentDialog, eCloseSheet } from "../../utils/events"; import { eCloseAttachmentDialog, eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time"; import { sleep } from "../../utils/time";
@@ -47,6 +46,7 @@ import { Notice } from "../ui/notice";
import { PressableButton } from "../ui/pressable"; import { PressableButton } from "../ui/pressable";
import Heading from "../ui/typography/heading"; import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph"; import Paragraph from "../ui/typography/paragraph";
import { formatBytes } from "@notesnook/common";
const Actions = ({ attachment, setAttachments, fwdRef }) => { const Actions = ({ attachment, setAttachments, fwdRef }) => {
const colors = useThemeStore((state) => state.colors); const colors = useThemeStore((state) => state.colors);

View File

@@ -23,7 +23,7 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database"; import { db } from "../../common/database";
import { useAttachmentProgress } from "../../hooks/use-attachment-progress"; import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { formatBytes } from "../../utils"; import { formatBytes } from "@notesnook/common";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import SheetProvider from "../sheet-provider"; import SheetProvider from "../sheet-provider";
import { IconButton } from "../ui/icon-button"; import { IconButton } from "../ui/icon-button";

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { useEffect } from "react"; import React, { useCallback, useEffect } from "react";
import { Keyboard, Platform, View } from "react-native"; import { Keyboard, Platform, View } from "react-native";
import Animated, { import Animated, {
Easing, Easing,
@@ -30,10 +30,10 @@ import { notesnook } from "../../../e2e/test.ids";
import { editorState } from "../../screens/editor/tiptap/utils"; import { editorState } from "../../screens/editor/tiptap/utils";
import { useSelectionStore } from "../../stores/use-selection-store"; import { useSelectionStore } from "../../stores/use-selection-store";
import { useSettingStore } from "../../stores/use-setting-store"; import { useSettingStore } from "../../stores/use-setting-store";
import { getElevation, showTooltip, TOOLTIP_POSITIONS } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { normalize, SIZE } from "../../utils/size"; import { SIZE, normalize } from "../../utils/size";
import NativeTooltip from "../../utils/tooltip";
import { PressableButton } from "../ui/pressable"; import { PressableButton } from "../ui/pressable";
import { useCallback } from "react";
export const FloatingButton = ({ export const FloatingButton = ({
title, title,
@@ -116,11 +116,11 @@ export const FloatingButton = ({
accentColor={color || "accent"} accentColor={color || "accent"}
accentText="light" accentText="light"
customStyle={{ customStyle={{
...getElevation(5), ...getElevationStyle(5),
borderRadius: 100 borderRadius: 100
}} }}
onLongPress={(event) => { onLongPress={(event) => {
showTooltip(event, title, TOOLTIP_POSITIONS.LEFT); NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
}} }}
onPress={onPress} onPress={onPress}
> >

View File

@@ -21,7 +21,7 @@ import React from "react";
import { View } from "react-native"; import { View } from "react-native";
import { DDS } from "../../services/device-detection"; import { DDS } from "../../services/device-detection";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
const DialogContainer = ({ width, height, ...restProps }) => { const DialogContainer = ({ width, height, ...restProps }) => {
const colors = useThemeStore((state) => state.colors); const colors = useThemeStore((state) => state.colors);
@@ -30,7 +30,7 @@ const DialogContainer = ({ width, height, ...restProps }) => {
<View <View
{...restProps} {...restProps}
style={{ style={{
...getElevation(5), ...getElevationStyle(5),
width: width || DDS.isTab ? 500 : "85%", width: width || DDS.isTab ? 500 : "85%",
maxHeight: height || 450, maxHeight: height || 450,
borderRadius: 10, borderRadius: 10,

View File

@@ -25,7 +25,7 @@ import {
eUnSubscribeEvent eUnSubscribeEvent
} from "../../services/event-manager"; } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { eCloseSimpleDialog, eOpenSimpleDialog } from "../../utils/events"; import { eCloseSimpleDialog, eOpenSimpleDialog } from "../../utils/events";
import { sleep } from "../../utils/time"; import { sleep } from "../../utils/time";
import { Toast } from "../toast"; import { Toast } from "../toast";
@@ -111,7 +111,7 @@ export const Dialog = ({ context = "global" }) => {
}; };
const style = { const style = {
...getElevation(5), ...getElevationStyle(5),
width: DDS.isTab ? 400 : "85%", width: DDS.isTab ? 400 : "85%",
maxHeight: 450, maxHeight: 450,
borderRadius: 5, borderRadius: 5,

View File

@@ -26,7 +26,7 @@ import {
} from "../../../services/event-manager"; } from "../../../services/event-manager";
import { useMessageStore } from "../../../stores/use-message-store"; import { useMessageStore } from "../../../stores/use-message-store";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { getElevation } from "../../../utils"; import { getElevationStyle } from "../../../utils/elevation";
import { import {
eCloseJumpToDialog, eCloseJumpToDialog,
eOpenJumpToDialog, eOpenJumpToDialog,
@@ -124,7 +124,7 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => {
> >
<View <View
style={{ style={{
...getElevation(5), ...getElevationStyle(5),
width: DDS.isTab ? 500 : "85%", width: DDS.isTab ? 500 : "85%",
backgroundColor: colors.bg, backgroundColor: colors.bg,
zIndex: 100, zIndex: 100,

View File

@@ -25,7 +25,7 @@ import {
eUnSubscribeEvent eUnSubscribeEvent
} from "../../../services/event-manager"; } from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { getElevation } from "../../../utils"; import { getElevationStyle } from "../../../utils/elevation";
import { eCloseResultDialog, eOpenResultDialog } from "../../../utils/events"; import { eCloseResultDialog, eOpenResultDialog } from "../../../utils/events";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import BaseDialog from "../../dialog/base-dialog"; import BaseDialog from "../../dialog/base-dialog";
@@ -68,7 +68,7 @@ const ResultDialog = () => {
<BaseDialog visible={true} onRequestClose={close}> <BaseDialog visible={true} onRequestClose={close}>
<View <View
style={{ style={{
...getElevation(5), ...getElevationStyle(5),
width: DDS.isTab ? 350 : "85%", width: DDS.isTab ? 350 : "85%",
maxHeight: 500, maxHeight: 500,
borderRadius: 10, borderRadius: 10,

View File

@@ -34,7 +34,8 @@ import {
} from "../../../services/event-manager"; } from "../../../services/event-manager";
import Navigation from "../../../services/navigation"; import Navigation from "../../../services/navigation";
import SearchService from "../../../services/search"; import SearchService from "../../../services/search";
import { getElevation, toTXT } from "../../../utils"; import { convertNoteToText } from "../../../utils/note-to-text";
import { getElevationStyle } from "../../../utils/elevation";
import { import {
eClearEditor, eClearEditor,
eCloseActionSheet, eCloseActionSheet,
@@ -564,7 +565,7 @@ export class VaultDialog extends Component {
} }
async _copyNote(note) { async _copyNote(note) {
Clipboard.setString(await toTXT(note)); Clipboard.setString(await convertNoteToText(note));
ToastEvent.show({ ToastEvent.show({
heading: "Note copied", heading: "Note copied",
type: "success", type: "success",
@@ -580,7 +581,7 @@ export class VaultDialog extends Component {
await Share.open({ await Share.open({
heading: "Share note", heading: "Share note",
failOnCancel: false, failOnCancel: false,
message: await toTXT(note) message: await convertNoteToText(note)
}); });
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@@ -678,7 +679,7 @@ export class VaultDialog extends Component {
> >
<View <View
style={{ style={{
...getElevation(5), ...getElevationStyle(5),
width: DDS.isTab ? 350 : "85%", width: DDS.isTab ? 350 : "85%",
borderRadius: 10, borderRadius: 10,
backgroundColor: colors.bg, backgroundColor: colors.bg,

View File

@@ -92,7 +92,7 @@ export const RightMenus = () => {
onRequestClose={() => { onRequestClose={() => {
menuRef.current?.hide(); menuRef.current?.hide();
}} }}
anchor={ button={
<IconButton <IconButton
onPress={() => { onPress={() => {
menuRef.current?.show(); menuRef.current?.show();

View File

@@ -29,7 +29,7 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import SettingsService from "../../services/settings"; import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store"; import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import Heading from "../ui/typography/heading"; import Heading from "../ui/typography/heading";
@@ -198,7 +198,7 @@ const Intro = ({ navigation }) => {
style={{ style={{
paddingHorizontal: 24, paddingHorizontal: 24,
alignSelf: "center", alignSelf: "center",
...getElevation(5), ...getElevationStyle(5),
borderRadius: 100 borderRadius: 100
}} }}
fontSize={SIZE.md} fontSize={SIZE.md}

View File

@@ -24,7 +24,7 @@ import { eSendEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation"; import Navigation from "../../services/navigation";
import SettingsService from "../../services/settings"; import SettingsService from "../../services/settings";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { import {
eCloseLoading, eCloseLoading,
eOpenLoading, eOpenLoading,
@@ -91,7 +91,7 @@ export const WelcomeNotice = () => {
paddingHorizontal: 24, paddingHorizontal: 24,
alignSelf: "center", alignSelf: "center",
borderRadius: 100, borderRadius: 100,
...getElevation(5), ...getElevationStyle(5),
marginTop: 30 marginTop: 30
}} }}
type="accent" type="accent"

View File

@@ -37,9 +37,9 @@ import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-store"; import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store"; import { useUserStore } from "../../stores/use-user-store";
import { AndroidModule } from "../../utils";
import { eOpenAnnouncementDialog } from "../../utils/events"; import { eOpenAnnouncementDialog } from "../../utils/events";
import { getGithubVersion } from "../../utils/github-version"; import { getGithubVersion } from "../../utils/github-version";
import { NotesnookModule } from "../../utils/notesnook-module";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time"; import { sleep } from "../../utils/time";
import Migrate from "../sheets/migrate"; import Migrate from "../sheets/migrate";
@@ -175,7 +175,7 @@ const Launcher = React.memo(
const onUnlockBiometrics = useCallback(async () => { const onUnlockBiometrics = useCallback(async () => {
if (!(await BiometricService.isBiometryAvailable())) return; if (!(await BiometricService.isBiometryAvailable())) return;
if (Platform.OS === "android") { if (Platform.OS === "android") {
const activityName = await AndroidModule.getActivityName(); const activityName = await NotesnookModule.getActivityName();
if (activityName !== "MainActivity") return; if (activityName !== "MainActivity") return;
} }

View File

@@ -23,7 +23,7 @@ import { View } from "react-native";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { useMenuStore } from "../../../stores/use-menu-store"; import { useMenuStore } from "../../../stores/use-menu-store";
import { ToastEvent } from "../../../services/event-manager"; import { ToastEvent } from "../../../services/event-manager";
import { getTotalNotes } from "../../../utils"; import { getTotalNotes } from "@notesnook/common";
import { db } from "../../../common/database"; import { db } from "../../../common/database";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { IconButton } from "../../ui/icon-button"; import { IconButton } from "../../ui/icon-button";

View File

@@ -30,7 +30,6 @@ import {
} from "../../../services/event-manager"; } from "../../../services/event-manager";
import { useEditorStore } from "../../../stores/use-editor-store"; import { useEditorStore } from "../../../stores/use-editor-store";
import { useSelectionStore } from "../../../stores/use-selection-store"; import { useSelectionStore } from "../../../stores/use-selection-store";
import { history } from "../../../utils";
import { eOnLoadNote, eShowMergeDialog } from "../../../utils/events"; import { eOnLoadNote, eShowMergeDialog } from "../../../utils/events";
import { tabBarRef } from "../../../utils/global-refs"; import { tabBarRef } from "../../../utils/global-refs";
import { presentDialog } from "../../dialog/functions"; import { presentDialog } from "../../dialog/functions";
@@ -59,12 +58,14 @@ export const openNote = async (item, isTrash, setSelectedItem, isSheet) => {
return; return;
} }
} }
const { selectedItemsList, selectionMode, clearSelection } =
useSelectionStore.getState();
if (history.selectedItemsList.length > 0 && history.selectionMode) { if (selectedItemsList.length > 0 && selectionMode) {
setSelectedItem && setSelectedItem(_note); setSelectedItem && setSelectedItem(_note);
return; return;
} else { } else {
history.selectedItemsList = []; clearSelection();
} }
if (_note.conflicted) { if (_note.conflicted) {

View File

@@ -22,23 +22,23 @@ import { View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids"; import { notesnook } from "../../../../e2e/test.ids";
import { TopicNotes } from "../../../screens/notes/topic-notes"; import { TopicNotes } from "../../../screens/notes/topic-notes";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { useSettingStore } from "../../../stores/use-setting-store"; import { useSettingStore } from "../../../stores/use-setting-store";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { history } from "../../../utils";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { Properties } from "../../properties"; import { Properties } from "../../properties";
import { Button } from "../../ui/button"; import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button"; import { IconButton } from "../../ui/icon-button";
import Heading from "../../ui/typography/heading"; import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph"; import Paragraph from "../../ui/typography/paragraph";
import { getFormattedDate } from "../../../utils/time"; import { getFormattedDate } from "@notesnook/common";
const showActionSheet = (item) => { const showActionSheet = (item) => {
Properties.present(item); Properties.present(item);
}; };
const navigateToTopic = (topic) => { const navigateToTopic = (topic) => {
if (history.selectedItemsList.length > 0) return; if (useSelectionStore.getState().selectedItemsList.length > 0) return;
TopicNotes.navigate(topic, true); TopicNotes.navigate(topic, true);
}; };

View File

@@ -24,7 +24,6 @@ import { ToastEvent } from "../../../services/event-manager";
import Navigation from "../../../services/navigation"; import Navigation from "../../../services/navigation";
import { useSelectionStore } from "../../../stores/use-selection-store"; import { useSelectionStore } from "../../../stores/use-selection-store";
import { useTrashStore } from "../../../stores/use-trash-store"; import { useTrashStore } from "../../../stores/use-trash-store";
import { history } from "../../../utils";
import { db } from "../../../common/database"; import { db } from "../../../common/database";
import { presentDialog } from "../../dialog/functions"; import { presentDialog } from "../../dialog/functions";
import SelectionWrapper from "../selection-wrapper"; import SelectionWrapper from "../selection-wrapper";
@@ -49,11 +48,13 @@ const navigateToNotebook = (item, canGoBack) => {
export const openNotebookTopic = (item) => { export const openNotebookTopic = (item) => {
const isTrash = item.type === "trash"; const isTrash = item.type === "trash";
if (history.selectedItemsList.length > 0 && history.selectionMode) { const { selectedItemsList, setSelectedItem, selectionMode, clearSelection } =
useSelectionStore.getState().setSelectedItem(item); useSelectionStore.getState();
if (selectedItemsList.length > 0 && selectionMode) {
setSelectedItem(item);
return; return;
} else { } else {
history.selectedItemsList = []; clearSelection();
} }
if (isTrash) { if (isTrash) {

View File

@@ -36,7 +36,7 @@ import { NoteWrapper } from "../list-items/note/wrapper";
import { NotebookWrapper } from "../list-items/notebook/wrapper"; import { NotebookWrapper } from "../list-items/notebook/wrapper";
import TagItem from "../list-items/tag"; import TagItem from "../list-items/tag";
import { Empty } from "./empty"; import { Empty } from "./empty";
import { getTotalNotes } from "../../utils"; import { getTotalNotes } from "@notesnook/common";
import { useSettingStore } from "../../stores/use-setting-store"; import { useSettingStore } from "../../stores/use-setting-store";
import ReminderItem from "../list-items/reminder"; import ReminderItem from "../list-items/reminder";

View File

@@ -34,10 +34,9 @@ import {
import Navigation from "../../services/navigation"; import Navigation from "../../services/navigation";
import Sync from "../../services/sync"; import Sync from "../../services/sync";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { dHeight } from "../../utils";
import { eOnLoadNote, eShowMergeDialog } from "../../utils/events"; import { eOnLoadNote, eShowMergeDialog } from "../../utils/events";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { getFormattedDate, sleep } from "../../utils/time"; import { sleep } from "../../utils/time";
import BaseDialog from "../dialog/base-dialog"; import BaseDialog from "../dialog/base-dialog";
import DialogButtons from "../dialog/dialog-buttons"; import DialogButtons from "../dialog/dialog-buttons";
import DialogContainer from "../dialog/dialog-container"; import DialogContainer from "../dialog/dialog-container";
@@ -46,6 +45,8 @@ import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button"; import { IconButton } from "../ui/icon-button";
import Seperator from "../ui/seperator"; import Seperator from "../ui/seperator";
import Paragraph from "../ui/typography/paragraph"; import Paragraph from "../ui/typography/paragraph";
import { useSettingStore } from "../../stores/use-setting-store";
import { getFormattedDate } from "@notesnook/common";
const MergeConflicts = () => { const MergeConflicts = () => {
const colors = useThemeStore((state) => state.colors); const colors = useThemeStore((state) => state.colors);
@@ -57,6 +58,7 @@ const MergeConflicts = () => {
const content = useRef(null); const content = useRef(null);
const isKeepingConflicted = !keep?.conflicted; const isKeepingConflicted = !keep?.conflicted;
const isKeeping = !!keep; const isKeeping = !!keep;
const { height } = useSettingStore((state) => state.dimensions);
const applyChanges = async () => { const applyChanges = async () => {
let _content = keep; let _content = keep;
@@ -296,7 +298,7 @@ const MergeConflicts = () => {
<Animated.View <Animated.View
style={{ style={{
height: dHeight / 2 - (50 + insets.top / 2), height: height / 2 - (50 + insets.top / 2),
backgroundColor: colors.bg, backgroundColor: colors.bg,
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: colors.nav borderBottomColor: colors.nav
@@ -332,7 +334,7 @@ const MergeConflicts = () => {
<Animated.View <Animated.View
style={{ style={{
height: dHeight / 2 - (50 + insets.top / 2), height: height / 2 - (50 + insets.top / 2),
backgroundColor: colors.bg, backgroundColor: colors.bg,
borderRadius: 10 borderRadius: 10
}} }}

View File

@@ -26,13 +26,13 @@ import { presentSheet } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { openLinkInBrowser } from "../../utils/functions"; import { openLinkInBrowser } from "../../utils/functions";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { getFormattedDate, timeSince } from "../../utils/time";
import DialogHeader from "../dialog/dialog-header"; import DialogHeader from "../dialog/dialog-header";
import SheetProvider from "../sheet-provider"; import SheetProvider from "../sheet-provider";
import { PressableButton } from "../ui/pressable"; import { PressableButton } from "../ui/pressable";
import Seperator from "../ui/seperator"; import Seperator from "../ui/seperator";
import Paragraph from "../ui/typography/paragraph"; import Paragraph from "../ui/typography/paragraph";
import NotePreview from "./preview"; import NotePreview from "./preview";
import { getFormattedDate, getTimeAgo } from "@notesnook/common";
export default function NoteHistory({ note, fwdRef }) { export default function NoteHistory({ note, fwdRef }) {
const [history, setHistory] = useState([]); const [history, setHistory] = useState([]);
@@ -90,7 +90,7 @@ export default function NoteHistory({ note, fwdRef }) {
> >
<Paragraph>{getDate(item.dateCreated, item.dateModified)}</Paragraph> <Paragraph>{getDate(item.dateCreated, item.dateModified)}</Paragraph>
<Paragraph color={colors.icon} size={SIZE.xs}> <Paragraph color={colors.icon} size={SIZE.xs}>
{timeSince(item.dateModified)} {getTimeAgo(item.dateModified)}
</Paragraph> </Paragraph>
</PressableButton> </PressableButton>
), ),

View File

@@ -26,7 +26,7 @@ import { DDS } from "../../services/device-detection";
import { eSendEvent, presentSheet } from "../../services/event-manager"; import { eSendEvent, presentSheet } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store"; import { useUserStore } from "../../stores/use-user-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { import {
eClosePremiumDialog, eClosePremiumDialog,
eCloseSheet, eCloseSheet,
@@ -271,7 +271,7 @@ export const Component = ({ close, promo }) => {
position: "absolute", position: "absolute",
borderRadius: 100, borderRadius: 100,
bottom: 30, bottom: 30,
...getElevation(10) ...getElevationStyle(10)
}} }}
/> />
) : null} ) : null}

View File

@@ -28,7 +28,7 @@ import {
eUnSubscribeEvent eUnSubscribeEvent
} from "../../services/event-manager"; } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { import {
eCloseActionSheet, eCloseActionSheet,
eCloseSheet, eCloseSheet,
@@ -94,7 +94,7 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
position: "absolute", position: "absolute",
backgroundColor: colors.nav, backgroundColor: colors.nav,
zIndex: 999, zIndex: 999,
...getElevation(20), ...getElevationStyle(20),
padding: 12, padding: 12,
borderRadius: 10, borderRadius: 10,
flexDirection: "row", flexDirection: "row",

View File

@@ -21,8 +21,8 @@ import React from "react";
import { View } from "react-native"; import { View } from "react-native";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { getFormattedDate } from "../../utils/time";
import Paragraph from "../ui/typography/paragraph"; import Paragraph from "../ui/typography/paragraph";
import { getFormattedDate } from "@notesnook/common";
export const DateMeta = ({ item }) => { export const DateMeta = ({ item }) => {
const colors = useThemeStore((state) => state.colors); const colors = useThemeStore((state) => state.colors);

View File

@@ -41,11 +41,7 @@ enum EmailChangeSteps {
changeEmail changeEmail
} }
export const ChangeEmail = ({ export const ChangeEmail = ({ close }: ChangeEmailProps) => {
actionSheetRef,
close,
update
}: ChangeEmailProps) => {
const [step, setStep] = useState(EmailChangeSteps.verify); const [step, setStep] = useState(EmailChangeSteps.verify);
const emailChangeData = useRef<{ const emailChangeData = useRef<{
email?: string; email?: string;

View File

@@ -29,7 +29,7 @@ import Exporter from "../../../services/exporter";
import PremiumService from "../../../services/premium"; import PremiumService from "../../../services/premium";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { useUserStore } from "../../../stores/use-user-store"; import { useUserStore } from "../../../stores/use-user-store";
import { getElevation } from "../../../utils"; import { getElevationStyle } from "../../../utils/elevation";
import { ph, pv, SIZE } from "../../../utils/size"; import { ph, pv, SIZE } from "../../../utils/size";
import { sleep } from "../../../utils/time"; import { sleep } from "../../../utils/time";
import DialogHeader from "../../dialog/dialog-header"; import DialogHeader from "../../dialog/dialog-header";
@@ -340,7 +340,7 @@ ExportNotesSheet.present = (notes, allNotes) => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
...getElevation(5), ...getElevationStyle(5),
borderRadius: 5, borderRadius: 5,
paddingVertical: pv paddingVertical: pv
}, },

View File

@@ -34,7 +34,6 @@ import SettingsService from "../../../services/settings";
import { db } from "../../../common/database"; import { db } from "../../../common/database";
import Storage from "../../../common/database/storage"; import Storage from "../../../common/database/storage";
import { eOpenRecoveryKeyDialog } from "../../../utils/events"; import { eOpenRecoveryKeyDialog } from "../../../utils/events";
import { sanitizeFilename } from "../../../utils/sanitizer";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { sleep } from "../../../utils/time"; import { sleep } from "../../../utils/time";
import DialogHeader from "../../dialog/dialog-header"; import DialogHeader from "../../dialog/dialog-header";
@@ -44,6 +43,7 @@ import SheetWrapper from "../../ui/sheet";
import { QRCode } from "../../ui/svg/lazy"; import { QRCode } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph"; import Paragraph from "../../ui/typography/paragraph";
import RNFetchBlob from "react-native-blob-util"; import RNFetchBlob from "react-native-blob-util";
import { sanitizeFilename } from "@notesnook/common";
class RecoveryKeySheet extends React.Component { class RecoveryKeySheet extends React.Component {
constructor(props) { constructor(props) {

View File

@@ -36,7 +36,6 @@ import { initialize } from "../../../stores";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { eCloseRestoreDialog, eOpenRestoreDialog } from "../../../utils/events"; import { eCloseRestoreDialog, eOpenRestoreDialog } from "../../../utils/events";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { getFormattedDate } from "../../../utils/time";
import { Dialog } from "../../dialog"; import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header"; import DialogHeader from "../../dialog/dialog-header";
import { presentDialog } from "../../dialog/functions"; import { presentDialog } from "../../dialog/functions";
@@ -45,6 +44,7 @@ import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator"; import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet"; import SheetWrapper from "../../ui/sheet";
import Paragraph from "../../ui/typography/paragraph"; import Paragraph from "../../ui/typography/paragraph";
import { getFormattedDate } from "@notesnook/common";
const RestoreDataSheet = () => { const RestoreDataSheet = () => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);

View File

@@ -60,11 +60,12 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids"; import { notesnook } from "../../../../e2e/test.ids";
import { MMKV } from "../../../common/database/mmkv"; import { MMKV } from "../../../common/database/mmkv";
import { openEditor } from "../../../screens/notes/common"; import { openEditor } from "../../../screens/notes/common";
import { getTotalNotes, history } from "../../../utils"; import { getTotalNotes } from "@notesnook/common";
import { deleteItems } from "../../../utils/functions"; import { deleteItems } from "../../../utils/functions";
import { presentDialog } from "../../dialog/functions"; import { presentDialog } from "../../dialog/functions";
import { Properties } from "../../properties"; import { Properties } from "../../properties";
import Sort from "../sort"; import Sort from "../sort";
import { useSelectionStore } from "../../../stores/use-selection-store";
type ConfigItem = { id: string; type: string }; type ConfigItem = { id: string; type: string };
class TopicSheetConfig { class TopicSheetConfig {
@@ -333,7 +334,9 @@ export const TopicsSheet = () => {
}} }}
onPress={async () => { onPress={async () => {
//@ts-ignore //@ts-ignore
history.selectedItemsList = selection; useSelectionStore.setState({
selectedItemsList: selection
});
presentDialog({ presentDialog({
title: `Delete ${ title: `Delete ${
selection.length > 1 ? "topics" : "topics" selection.length > 1 ? "topics" : "topics"
@@ -345,7 +348,7 @@ export const TopicsSheet = () => {
negativeText: "Cancel", negativeText: "Cancel",
positivePress: async () => { positivePress: async () => {
await deleteItems(); await deleteItems();
history.selectedItemsList = []; useSelectionStore.getState().clearSelection();
setEnabled(false); setEnabled(false);
setSelection([]); setSelection([]);
}, },

View File

@@ -28,7 +28,7 @@ import {
eUnSubscribeEvent eUnSubscribeEvent
} from "../../services/event-manager"; } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { eHideToast, eShowToast } from "../../utils/events"; import { eHideToast, eShowToast } from "../../utils/events";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
@@ -136,7 +136,7 @@ export const Toast = ({ context = "global" }) => {
> >
<View <View
style={{ style={{
...getElevation(5), ...getElevationStyle(5),
maxWidth: "95%", maxWidth: "95%",
backgroundColor: colors.nav, backgroundColor: colors.nav,
minWidth: data?.func ? "95%" : "50%", minWidth: data?.func ? "95%" : "50%",

View File

@@ -27,9 +27,9 @@ import Animated, {
} from "react-native-reanimated"; } from "react-native-reanimated";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { ColorKey, useThemeStore } from "../../../stores/use-theme-store"; import { ColorKey, useThemeStore } from "../../../stores/use-theme-store";
import { showTooltip, TOOLTIP_POSITIONS } from "../../../utils";
import { BUTTON_TYPES } from "../../../utils/constants"; import { BUTTON_TYPES } from "../../../utils/constants";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import NativeTooltip from "../../../utils/tooltip";
import { ButtonProps } from "../button"; import { ButtonProps } from "../button";
import { PressableButton } from "../pressable"; import { PressableButton } from "../pressable";
import Heading from "../typography/heading"; import Heading from "../typography/heading";
@@ -86,7 +86,7 @@ export const AnimatedButton = ({
return; return;
} }
if (tooltipText) { if (tooltipText) {
showTooltip(event, tooltipText, TOOLTIP_POSITIONS.TOP); NativeTooltip.show(event, tooltipText, NativeTooltip.POSITIONS.TOP);
} }
}} }}
disabled={loading} disabled={loading}

View File

@@ -22,15 +22,15 @@ import {
ActivityIndicator, ActivityIndicator,
ColorValue, ColorValue,
TextStyle, TextStyle,
ViewStyle, View,
View ViewStyle
} from "react-native"; } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { ColorKey, useThemeStore } from "../../../stores/use-theme-store"; import { ColorKey, useThemeStore } from "../../../stores/use-theme-store";
import { useUserStore } from "../../../stores/use-user-store"; import { useUserStore } from "../../../stores/use-user-store";
import { showTooltip, TOOLTIP_POSITIONS } from "../../../utils";
import { BUTTON_TYPES } from "../../../utils/constants"; import { BUTTON_TYPES } from "../../../utils/constants";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import NativeTooltip from "../../../utils/tooltip";
import { ProTag } from "../../premium/pro-tag"; import { ProTag } from "../../premium/pro-tag";
import { PressableButton, PressableButtonProps } from "../pressable"; import { PressableButton, PressableButtonProps } from "../pressable";
import Heading from "../typography/heading"; import Heading from "../typography/heading";
@@ -105,7 +105,7 @@ export const Button = ({
return; return;
} }
if (tooltipText) { if (tooltipText) {
showTooltip(event, tooltipText, TOOLTIP_POSITIONS.TOP); NativeTooltip.show(event, tooltipText, NativeTooltip.POSITIONS.TOP);
} }
}} }}
disabled={loading} disabled={loading}

View File

@@ -22,10 +22,10 @@ import { ColorValue, GestureResponderEvent, ViewStyle } from "react-native";
import Animated, { Layout } from "react-native-reanimated"; import Animated, { Layout } from "react-native-reanimated";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { ColorKey, useThemeStore } from "../../../stores/use-theme-store"; import { ColorKey, useThemeStore } from "../../../stores/use-theme-store";
import { showTooltip, TOOLTIP_POSITIONS } from "../../../utils";
import { hexToRGBA, RGB_Linear_Shade } from "../../../utils/color-scheme/utils"; import { hexToRGBA, RGB_Linear_Shade } from "../../../utils/color-scheme/utils";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { PressableButton, PressableButtonProps } from "../pressable"; import { PressableButton, PressableButtonProps } from "../pressable";
import NativeTooltip from "../../../utils/tooltip";
interface IconButtonProps extends PressableButtonProps { interface IconButtonProps extends PressableButtonProps {
name: string; name: string;
color?: ColorValue; color?: ColorValue;
@@ -56,7 +56,7 @@ export const IconButton = ({
tooltipText, tooltipText,
type = "gray", type = "gray",
fwdRef, fwdRef,
tooltipPosition = TOOLTIP_POSITIONS.TOP, tooltipPosition = NativeTooltip.POSITIONS.TOP,
...restProps ...restProps
}: IconButtonProps) => { }: IconButtonProps) => {
const colors = useThemeStore((state) => state.colors); const colors = useThemeStore((state) => state.colors);
@@ -67,7 +67,7 @@ export const IconButton = ({
return; return;
} }
if (tooltipText) { if (tooltipText) {
showTooltip(event, tooltipText, tooltipPosition); NativeTooltip.show(event, tooltipText, tooltipPosition);
} }
}; };

View File

@@ -36,7 +36,7 @@ import {
validateUsername validateUsername
} from "../../../services/validation"; } from "../../../services/validation";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { getElevation } from "../../../utils"; import { getElevationStyle } from "../../../utils/elevation";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { IconButton } from "../icon-button"; import { IconButton } from "../icon-button";
import Paragraph from "../typography/paragraph"; import Paragraph from "../typography/paragraph";
@@ -344,7 +344,7 @@ const Input = ({
paddingVertical: 3, paddingVertical: 3,
paddingHorizontal: 5, paddingHorizontal: 5,
borderRadius: 2.5, borderRadius: 2.5,
...getElevation(2), ...getElevationStyle(2),
top: 0 top: 0
}} }}
> >

View File

@@ -23,8 +23,8 @@ import { ViewStyle } from "react-native";
import { Reminder } from "../../../services/notifications"; import { Reminder } from "../../../services/notifications";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { getFormattedReminderTime } from "../../../utils/time";
import { Button, ButtonProps } from "../button"; import { Button, ButtonProps } from "../button";
import { getFormattedReminderTime } from "@notesnook/common";
export const ReminderTime = ({ export const ReminderTime = ({
checkIsActive = true, checkIsActive = true,

View File

@@ -17,9 +17,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { useEffect, useRef, useState } from "react"; import { useTimeAgo } from "@notesnook/common";
import React from "react";
import { TextProps } from "react-native"; import { TextProps } from "react-native";
import { timeSince } from "../../../utils/time";
import Heading from "../typography/heading"; import Heading from "../typography/heading";
import Paragraph from "../typography/paragraph"; import Paragraph from "../typography/paragraph";
interface TimeSinceProps extends TextProps { interface TimeSinceProps extends TextProps {
@@ -34,20 +34,10 @@ export const TimeSince = ({
updateFrequency = 30000, updateFrequency = 30000,
bold bold
}: TimeSinceProps) => { }: TimeSinceProps) => {
const [timeAgo, setTimeAgo] = useState<string | null>(null); const timeAgo = useTimeAgo(time, {
const interval = useRef<NodeJS.Timer>(); interval: updateFrequency,
locale: "short"
useEffect(() => { });
let t = timeSince(time || Date.now());
setTimeAgo(t);
interval.current = setInterval(() => {
t = timeSince(time);
setTimeAgo(t);
}, updateFrequency);
return () => {
interval.current && clearInterval(interval.current);
};
}, [time, updateFrequency]);
return bold ? ( return bold ? (
<Heading style={style}>{timeAgo}</Heading> <Heading style={style}>{timeAgo}</Heading>

View File

@@ -28,7 +28,7 @@ import {
} from "../../assets/images/assets"; } from "../../assets/images/assets";
import { ThemeStore, useThemeStore } from "../../stores/use-theme-store"; import { ThemeStore, useThemeStore } from "../../stores/use-theme-store";
import { eSendEvent } from "../../services/event-manager"; import { eSendEvent } from "../../services/event-manager";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { eOpenAddNotebookDialog } from "../../utils/events"; import { eOpenAddNotebookDialog } from "../../utils/events";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import useRotator from "../../hooks/use-rotator"; import useRotator from "../../hooks/use-rotator";
@@ -89,7 +89,7 @@ const NotebookWelcome = () => {
padding: 12, padding: 12,
width: "100%", width: "100%",
backgroundColor: colors.bg, backgroundColor: colors.bg,
...getElevation(3), ...getElevationStyle(3),
borderRadius: 10, borderRadius: 10,
marginVertical: 12 marginVertical: 12
}} }}
@@ -142,7 +142,7 @@ const notebooks: { id: string; steps: TStep[] } = {
padding: 12, padding: 12,
width: "100%", width: "100%",
backgroundColor: colors.bg, backgroundColor: colors.bg,
...getElevation(3), ...getElevationStyle(3),
borderRadius: 10, borderRadius: 10,
marginVertical: 12 marginVertical: 12
}} }}

View File

@@ -50,7 +50,7 @@ import { useSelectionStore } from "../stores/use-selection-store";
import { useTagStore } from "../stores/use-tag-store"; import { useTagStore } from "../stores/use-tag-store";
import { useThemeStore } from "../stores/use-theme-store"; import { useThemeStore } from "../stores/use-theme-store";
import { useUserStore } from "../stores/use-user-store"; import { useUserStore } from "../stores/use-user-store";
import { toTXT } from "../utils"; import { convertNoteToText } from "../utils/note-to-text";
import { toggleDarkMode } from "../utils/color-scheme/utils"; import { toggleDarkMode } from "../utils/color-scheme/utils";
import { import {
eOnTopicSheetUpdate, eOnTopicSheetUpdate,
@@ -182,7 +182,7 @@ export const useActions = ({ close = () => null, item }) => {
}); });
return; return;
} }
let text = await toTXT(item, false); let text = await convertNoteToText(item, false);
let html = text.replace(/\n/g, "<br />"); let html = text.replace(/\n/g, "<br />");
Notifications.displayNotification({ Notifications.displayNotification({
title: item.title, title: item.title,
@@ -227,7 +227,7 @@ export const useActions = ({ close = () => null, item }) => {
description: "Unlock note to copy to clipboard." description: "Unlock note to copy to clipboard."
}); });
} else { } else {
Clipboard.setString(await toTXT(item)); Clipboard.setString(await convertNoteToText(item));
ToastEvent.show({ ToastEvent.show({
heading: "Note copied to clipboard", heading: "Note copied to clipboard",
type: "success", type: "success",
@@ -415,7 +415,7 @@ export const useActions = ({ close = () => null, item }) => {
Share.open({ Share.open({
title: "Share note to", title: "Share note to",
failOnCancel: false, failOnCancel: false,
message: await toTXT(item) message: await convertNoteToText(item)
}); });
} }
} }

View File

@@ -48,7 +48,6 @@ import { useNoteStore } from "../stores/use-notes-store";
import { useSelectionStore } from "../stores/use-selection-store"; import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store"; import { useSettingStore } from "../stores/use-setting-store";
import { useThemeStore } from "../stores/use-theme-store"; import { useThemeStore } from "../stores/use-theme-store";
import { history } from "../utils";
import { rootNavigatorRef } from "../utils/global-refs"; import { rootNavigatorRef } from "../utils/global-refs";
import Auth from "../components/auth"; import Auth from "../components/auth";
const NativeStack = createNativeStackNavigator(); const NativeStack = createNativeStackNavigator();
@@ -191,7 +190,7 @@ const _NavigationStack = () => {
const clearSelection = useSelectionStore((state) => state.clearSelection); const clearSelection = useSelectionStore((state) => state.clearSelection);
const onStateChange = React.useCallback(() => { const onStateChange = React.useCallback(() => {
if (history.selectionMode) { if (useSelectionStore.getState().selectionMode) {
clearSelection(true); clearSelection(true);
} }
hideAllTooltips(); hideAllTooltips();

View File

@@ -59,7 +59,6 @@ import {
import { useEditorStore } from "../stores/use-editor-store"; import { useEditorStore } from "../stores/use-editor-store";
import { useSettingStore } from "../stores/use-setting-store"; import { useSettingStore } from "../stores/use-setting-store";
import { useThemeStore } from "../stores/use-theme-store"; import { useThemeStore } from "../stores/use-theme-store";
import { setWidthHeight } from "../utils";
import { import {
eClearEditor, eClearEditor,
eCloseFullscreenEditor, eCloseFullscreenEditor,
@@ -221,7 +220,6 @@ const _TabsHolder = () => {
width: size.width, width: size.width,
height: size.height height: size.height
}); });
setWidthHeight(size);
DDS.setSize(size, orientation); DDS.setSize(size, orientation);
const nextDeviceMode = DDS.isLargeTablet() const nextDeviceMode = DDS.isLargeTablet()
? "tablet" ? "tablet"

View File

@@ -37,7 +37,7 @@ import {
eUnSubscribeEvent eUnSubscribeEvent
} from "../../services/event-manager"; } from "../../services/event-manager";
import { useEditorStore } from "../../stores/use-editor-store"; import { useEditorStore } from "../../stores/use-editor-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { openLinkInBrowser } from "../../utils/functions"; import { openLinkInBrowser } from "../../utils/functions";
import { NoteType } from "../../utils/types"; import { NoteType } from "../../utils/types";
import { EDITOR_URI } from "./source"; import { EDITOR_URI } from "./source";
@@ -256,7 +256,7 @@ const ReadonlyButton = ({ editor }: { editor: useEditorType }) => {
width: 60, width: 60,
height: 60, height: 60,
right: 12, right: 12,
...getElevation(5) ...getElevationStyle(5)
}} }}
/> />
) : null; ) : null;

View File

@@ -38,7 +38,6 @@ import { useTagStore } from "../../../stores/use-tag-store";
import { ThemeStore, useThemeStore } from "../../../stores/use-theme-store"; import { ThemeStore, useThemeStore } from "../../../stores/use-theme-store";
import { eClearEditor, eOnLoadNote } from "../../../utils/events"; import { eClearEditor, eOnLoadNote } from "../../../utils/events";
import { tabBarRef } from "../../../utils/global-refs"; import { tabBarRef } from "../../../utils/global-refs";
import { getFormattedDate } from "../../../utils/time";
import { NoteType } from "../../../utils/types"; import { NoteType } from "../../../utils/types";
import { onNoteCreated } from "../../notes/common"; import { onNoteCreated } from "../../notes/common";
import Commands from "./commands"; import Commands from "./commands";
@@ -53,6 +52,7 @@ import {
makeSessionId, makeSessionId,
post post
} from "./utils"; } from "./utils";
import { getFormattedDate } from "@notesnook/common";
export const useEditor = ( export const useEditor = (
editorId = "", editorId = "",
@@ -139,7 +139,7 @@ export const useEditor = (
const reset = useCallback( const reset = useCallback(
async (resetState = true, resetContent = true) => { async (resetState = true, resetContent = true) => {
currentNote.current?.id && db.fs.cancel(currentNote.current.id); currentNote.current?.id && db.fs?.cancel(currentNote.current.id);
currentNote.current = null; currentNote.current = null;
loadedImages.current = {}; loadedImages.current = {};
currentContent.current = null; currentContent.current = null;

View File

@@ -49,9 +49,9 @@ import {
import { ThemeStore, useThemeStore } from "../../stores/use-theme-store"; import { ThemeStore, useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store"; import { useUserStore } from "../../stores/use-user-store";
import { eCloseSheet } from "../../utils/events"; import { eCloseSheet } from "../../utils/events";
import { sanitizeFilename } from "../../utils/sanitizer";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time"; import { sleep } from "../../utils/time";
import { sanitizeFilename } from "@notesnook/common";
const mfaMethods: MFAMethod[] = [ const mfaMethods: MFAMethod[] = [
{ {
id: "app", id: "app",

View File

@@ -39,7 +39,7 @@ import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store"; import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store"; import { useUserStore } from "../../stores/use-user-store";
import { getElevation } from "../../utils"; import { getElevationStyle } from "../../utils/elevation";
import { eOpenLoginDialog } from "../../utils/events"; import { eOpenLoginDialog } from "../../utils/events";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
const AppLock = ({ route }) => { const AppLock = ({ route }) => {
@@ -274,7 +274,7 @@ const AppLock = ({ route }) => {
style={{ style={{
paddingHorizontal: 24, paddingHorizontal: 24,
alignSelf: "center", alignSelf: "center",
...getElevation(5), ...getElevationStyle(5),
marginTop: 30, marginTop: 30,
borderRadius: 100 borderRadius: 100
}} }}

View File

@@ -17,107 +17,20 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { useRef, useState } from "react"; import React from "react";
import { View } from "react-native"; import { View } from "react-native";
import Menu, { MenuItem } from "react-native-reanimated-material-menu";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { PressableButton } from "../../components/ui/pressable"; import { PressableButton } from "../../components/ui/pressable";
import Paragraph from "../../components/ui/typography/paragraph";
import { DDS } from "../../services/device-detection"; import { DDS } from "../../services/device-detection";
import { ToastEvent } from "../../services/event-manager";
import PremiumService from "../../services/premium"; import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings"; import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { import {
hexToRGBA,
RGB_Linear_Shade, RGB_Linear_Shade,
hexToRGBA,
switchAccentColor switchAccentColor
} from "../../utils/color-scheme/utils"; } from "../../utils/color-scheme/utils";
import { MenuItemsList } from "../../utils/constants";
import { SIZE } from "../../utils/size"; import { SIZE } from "../../utils/size";
export const HomagePageSelector = () => {
const colors = useThemeStore((state) => state.colors);
const settings = useSettingStore((state) => state.settings);
const menuRef = useRef();
const [width, setWidth] = useState(0);
return (
<View
onLayout={(event) => {
setWidth(event.nativeEvent.layout.width);
}}
style={{
width: "100%"
}}
>
<Menu
ref={menuRef}
animationDuration={200}
style={{
borderRadius: 5,
backgroundColor: colors.bg,
width: width,
marginTop: 60
}}
onRequestClose={() => {
menuRef.current?.hide();
}}
anchor={
<PressableButton
onPress={async () => {
await PremiumService.verify(menuRef.current?.show);
}}
type="grayBg"
customStyle={{
flexDirection: "row",
alignItems: "center",
marginTop: 10,
width: "100%",
justifyContent: "space-between",
padding: 12
}}
>
<Paragraph>{settings.homepage}</Paragraph>
<Icon color={colors.icon} name="menu-down" size={SIZE.md} />
</PressableButton>
}
>
{MenuItemsList.slice(0, MenuItemsList.length - 1).map(
(item) =>
item.name !== "Monographs" && (
<MenuItem
key={item.name}
onPress={async () => {
menuRef.current?.hide();
await SettingsService.set({ homepage: item.name });
ToastEvent.show({
heading: "Homepage set to " + item.name,
message: "Restart the app for changes to take effect.",
type: "success"
});
}}
style={{
backgroundColor:
settings.homepage === item.name
? colors.nav
: "transparent",
width: "100%",
maxWidth: width
}}
textStyle={{
fontSize: SIZE.md,
color:
settings.homepage === item.name ? colors.accent : colors.pri
}}
>
{item.name}
</MenuItem>
)
)}
</Menu>
</View>
);
};
export const AccentColorPicker = () => { export const AccentColorPicker = () => {
const colors = useThemeStore((state) => state.colors); const colors = useThemeStore((state) => state.colors);

View File

@@ -1,106 +0,0 @@
/*
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 React from "react";
import { Platform, TouchableOpacity, View } from "react-native";
import Paragraph from "../../components/ui/typography/paragraph";
import Backup from "../../services/backup";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
export const AutomaticBackupsSelector = () => {
const colors = useThemeStore((state) => state.colors);
const settings = useSettingStore((state) => state.settings);
const updateAskForBackup = async () => {
SettingsService.set({
nextBackupRequestTime: Date.now() + 86400000 * 3
});
};
return (
<View
style={{
flexDirection: "row",
borderRadius: 5,
overflow: "hidden",
flexShrink: 1,
width: "100%"
}}
>
{[
{
title: "Never",
value: "useroff"
},
{
title: "Daily",
value: "daily"
},
{
title: "Weekly",
value: "weekly"
},
{
title: "Monthly",
value: "monthly"
}
].map((item, index) => (
<TouchableOpacity
activeOpacity={0.9}
onPress={async () => {
if (item.value === "useroff") {
await SettingsService.set({ reminder: item.value });
} else {
await PremiumService.verify(async () => {
if (Platform.OS === "android") {
let granted = await Backup.checkBackupDirExists();
if (!granted) {
return;
}
}
await SettingsService.set({ reminder: item.value });
});
}
updateAskForBackup();
}}
key={item.value}
style={{
backgroundColor:
settings.reminder === item.value ? colors.accent : colors.nav,
justifyContent: "center",
alignItems: "center",
width: "25%",
height: 35,
borderRightWidth: index !== 3 ? 1 : 0,
borderRightColor: colors.border
}}
>
<Paragraph
color={settings.reminder === item.value ? "white" : colors.icon}
size={SIZE.sm - 1}
>
{item.title}
</Paragraph>
</TouchableOpacity>
))}
</View>
);
};

View File

@@ -18,29 +18,34 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { ReactElement } from "react"; import React, { ReactElement } from "react";
import { AccentColorPicker, HomagePageSelector } from "./appearance"; import { AccentColorPicker } from "./appearance";
import { AutomaticBackupsSelector } from "./backup-restore";
import DebugLogs from "./debug"; import DebugLogs from "./debug";
import { ConfigureToolbar } from "./editor/configure-toolbar"; import { ConfigureToolbar } from "./editor/configure-toolbar";
import { Licenses } from "./licenses"; import { Licenses } from "./licenses";
import SoundPicker from "./sound-picker"; import SoundPicker from "./sound-picker";
import { Subscription } from "./subscription"; import { Subscription } from "./subscription";
import { TrashIntervalSelector } from "./trash-interval-selector";
import { FontSelector } from "./font-selector";
import { TitleFormat } from "./title-format"; import { TitleFormat } from "./title-format";
import { DateFormatSelector, TimeFormatSelector } from "./date-format"; import {
HomePicker,
DateFormatPicker,
FontPicker,
TimeFormatPicker,
TrashIntervalPicker,
BackupReminderPicker
} from "./picker/pickers";
export const components: { [name: string]: ReactElement } = { export const components: { [name: string]: ReactElement } = {
colorpicker: <AccentColorPicker />, colorpicker: <AccentColorPicker />,
homeselector: <HomagePageSelector />, homeselector: <HomePicker />,
autobackups: <AutomaticBackupsSelector />, autobackups: <BackupReminderPicker />,
subscription: <Subscription />, subscription: <Subscription />,
configuretoolbar: <ConfigureToolbar />, configuretoolbar: <ConfigureToolbar />,
"debug-logs": <DebugLogs />, "debug-logs": <DebugLogs />,
"sound-picker": <SoundPicker />, "sound-picker": <SoundPicker />,
licenses: <Licenses />, licenses: <Licenses />,
"trash-interval-selector": <TrashIntervalSelector />, "trash-interval-selector": <TrashIntervalPicker />,
"font-selector": <FontSelector />, "font-selector": <FontPicker />,
"title-format": <TitleFormat />, "title-format": <TitleFormat />,
"date-format-selector": <DateFormatSelector />, "date-format-selector": <DateFormatPicker />,
"time-format-selector": <TimeFormatSelector /> "time-format-selector": <TimeFormatPicker />
}; };

View File

@@ -1,198 +0,0 @@
/*
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 dayjs from "dayjs";
import React, { useRef, useState } from "react";
import { View } from "react-native";
import Menu, { MenuItem } from "react-native-reanimated-material-menu";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import { PressableButton } from "../../components/ui/pressable";
import Paragraph from "../../components/ui/typography/paragraph";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { DATE_FORMATS, TIME_FORMATS } from "@notesnook/core/common";
import { useSettingStore } from "../../stores/use-setting-store";
export const DateFormatSelector = () => {
const colors = useThemeStore((state) => state.colors);
const menuRef = useRef();
const [width, setWidth] = useState(0);
const [dateFormat, setDateFormat] = useState(db.settings.getDateFormat());
const onChange = (item) => {
menuRef.current?.hide();
db.settings.setDateFormat(item);
setDateFormat(item);
useSettingStore.setState({
dateFormat: item
});
};
return (
<View
onLayout={(event) => {
setWidth(event.nativeEvent.layout.width);
}}
style={{
width: "100%"
}}
>
<Menu
ref={menuRef}
animationDuration={200}
style={{
borderRadius: 5,
backgroundColor: colors.bg,
width: width,
marginTop: 60
}}
onRequestClose={() => {
menuRef.current?.hide();
}}
anchor={
<PressableButton
onPress={async () => {
menuRef.current?.show();
}}
type="grayBg"
customStyle={{
flexDirection: "row",
alignItems: "center",
marginTop: 10,
width: "100%",
justifyContent: "space-between",
padding: 12
}}
>
<Paragraph>
{dateFormat} ({dayjs().format(dateFormat)})
</Paragraph>
<Icon color={colors.icon} name="menu-down" size={SIZE.md} />
</PressableButton>
}
>
{DATE_FORMATS.map((item) => (
<MenuItem
key={item.id}
onPress={async () => {
onChange(item);
}}
style={{
backgroundColor: dateFormat === item ? colors.nav : "transparent",
width: "100%",
maxWidth: width
}}
textStyle={{
fontSize: SIZE.md,
color: dateFormat === item ? colors.accent : colors.pri
}}
>
{item} ({dayjs().format(item)})
</MenuItem>
))}
</Menu>
</View>
);
};
export const TimeFormatSelector = () => {
const colors = useThemeStore((state) => state.colors);
const menuRef = useRef();
const [width, setWidth] = useState(0);
const [timeFormat, setTimeFormat] = useState(db.settings.getTimeFormat());
const onChange = (item) => {
menuRef.current?.hide();
db.settings.setTimeFormat(item);
setTimeFormat(item);
useSettingStore.setState({
timeFormat: item
});
};
const TimeFormats = {
"12-hour": "hh:mm A",
"24-hour": "HH:mm"
};
return (
<View
onLayout={(event) => {
setWidth(event.nativeEvent.layout.width);
}}
style={{
width: "100%"
}}
>
<Menu
ref={menuRef}
animationDuration={200}
style={{
borderRadius: 5,
backgroundColor: colors.bg,
width: width,
marginTop: 60
}}
onRequestClose={() => {
menuRef.current?.hide();
}}
anchor={
<PressableButton
onPress={async () => {
menuRef.current?.show();
}}
type="grayBg"
customStyle={{
flexDirection: "row",
alignItems: "center",
marginTop: 10,
width: "100%",
justifyContent: "space-between",
padding: 12
}}
>
<Paragraph>
{timeFormat} ({dayjs().format(TimeFormats[timeFormat])})
</Paragraph>
<Icon color={colors.icon} name="menu-down" size={SIZE.md} />
</PressableButton>
}
>
{TIME_FORMATS.map((item) => (
<MenuItem
key={item.id}
onPress={async () => {
onChange(item);
}}
style={{
backgroundColor: timeFormat === item ? colors.nav : "transparent",
width: "100%",
maxWidth: width
}}
textStyle={{
fontSize: SIZE.md,
color: timeFormat === item ? colors.accent : colors.pri
}}
>
{item} ({dayjs().format(TimeFormats[item])})
</MenuItem>
))}
</Menu>
</View>
);
};

View File

@@ -33,7 +33,8 @@ import useTimer from "../../hooks/use-timer";
import { ToastEvent } from "../../services/event-manager"; import { ToastEvent } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { hexToRGBA } from "../../utils/color-scheme/utils"; import { hexToRGBA } from "../../utils/color-scheme/utils";
import { sanitizeFilename } from "../../utils/sanitizer"; import { sanitizeFilename } from "@notesnook/common";
// function getLevelString(level: number) { // function getLevelString(level: number) {
// switch (level) { // switch (level) {
// case LogLevel.Debug: // case LogLevel.Debug:

View File

@@ -25,7 +25,7 @@ import { presentDialog } from "../../../components/dialog/functions";
import { IconButton } from "../../../components/ui/icon-button"; import { IconButton } from "../../../components/ui/icon-button";
import Paragraph from "../../../components/ui/typography/paragraph"; import Paragraph from "../../../components/ui/typography/paragraph";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { getElevation } from "../../../utils"; import { getElevationStyle } from "../../../utils/elevation";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { renderTool } from "./common"; import { renderTool } from "./common";
import { DraggableItem, useDragState } from "./state"; import { DraggableItem, useDragState } from "./state";
@@ -174,7 +174,7 @@ export const Group = ({
width: isDragged ? dimensions.current?.width : "100%", width: isDragged ? dimensions.current?.width : "100%",
backgroundColor: colors.bg, backgroundColor: colors.bg,
borderRadius: 10, borderRadius: 10,
...getElevation(hover ? 5 : 0), ...getElevationStyle(hover ? 5 : 0),
marginTop: isSubgroup ? 0 : 10 marginTop: isSubgroup ? 0 : 10
} }
]} ]}

View File

@@ -26,7 +26,7 @@ import { IconButton } from "../../../components/ui/icon-button";
import { SvgView } from "../../../components/ui/svg"; import { SvgView } from "../../../components/ui/svg";
import Paragraph from "../../../components/ui/typography/paragraph"; import Paragraph from "../../../components/ui/typography/paragraph";
import { useThemeStore } from "../../../stores/use-theme-store"; import { useThemeStore } from "../../../stores/use-theme-store";
import { getElevation } from "../../../utils"; import { getElevationStyle } from "../../../utils/elevation";
import { SIZE } from "../../../utils/size"; import { SIZE } from "../../../utils/size";
import { renderGroup } from "./common"; import { renderGroup } from "./common";
import { DraggableItem, useDragState } from "./state"; import { DraggableItem, useDragState } from "./state";
@@ -186,7 +186,7 @@ export const Tool = ({
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
paddingLeft: isSubgroup ? 30 : 12, paddingLeft: isSubgroup ? 30 : 12,
...getElevation(hover ? 3 : 0) ...getElevationStyle(hover ? 3 : 0)
}} }}
> >
<View <View

View File

@@ -1,110 +0,0 @@
/*
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 React, { useRef, useState } from "react";
import { View } from "react-native";
import Menu, { MenuItem } from "react-native-reanimated-material-menu";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { PressableButton } from "../../components/ui/pressable";
import Paragraph from "../../components/ui/typography/paragraph";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { SIZE } from "../../utils/size";
import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
export const FontSelector = () => {
const colors = useThemeStore((state) => state.colors);
const defaultFontFamily = useSettingStore(
(state) => state.settings.defaultFontFamily
);
const menuRef = useRef();
const [width, setWidth] = useState(0);
const onChange = (item) => {
menuRef.current?.hide();
SettingsService.set({
defaultFontFamily: item
});
};
return (
<View
onLayout={(event) => {
setWidth(event.nativeEvent.layout.width);
}}
style={{
width: "100%"
}}
>
<Menu
ref={menuRef}
animationDuration={200}
style={{
borderRadius: 5,
backgroundColor: colors.bg,
width: width,
marginTop: 60
}}
onRequestClose={() => {
menuRef.current?.hide();
}}
anchor={
<PressableButton
onPress={async () => {
menuRef.current?.show();
}}
type="grayBg"
customStyle={{
flexDirection: "row",
alignItems: "center",
marginTop: 10,
width: "100%",
justifyContent: "space-between",
padding: 12
}}
>
<Paragraph>{getFontById(defaultFontFamily).title}</Paragraph>
<Icon color={colors.icon} name="menu-down" size={SIZE.md} />
</PressableButton>
}
>
{getFonts().map((item) => (
<MenuItem
key={item.id}
onPress={async () => {
onChange(item.id);
}}
style={{
backgroundColor:
defaultFontFamily === item.id ? colors.nav : "transparent",
width: "100%",
maxWidth: width
}}
textStyle={{
fontSize: SIZE.md,
color: defaultFontFamily === item.id ? colors.accent : colors.pri
}}
>
{item.title}
</MenuItem>
))}
</Menu>
</View>
);
};

View File

@@ -21,25 +21,51 @@ import React, { useRef, useState } from "react";
import { View } from "react-native"; import { View } from "react-native";
import Menu, { MenuItem } from "react-native-reanimated-material-menu"; import Menu, { MenuItem } from "react-native-reanimated-material-menu";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database"; import { PressableButton } from "../../../components/ui/pressable";
import { PressableButton } from "../../components/ui/pressable"; import Paragraph from "../../../components/ui/typography/paragraph";
import Paragraph from "../../components/ui/typography/paragraph"; import { useThemeStore } from "../../../stores/use-theme-store";
import PremiumService from "../../services/premium"; import { SIZE } from "../../../utils/size";
import { useThemeStore } from "../../stores/use-theme-store"; import PremiumService from "../../../services/premium";
import { SIZE } from "../../utils/size";
export const TrashIntervalSelector = () => { interface PickerOptions<T> {
getValue: () => T;
updateValue: (item: T) => Promise<void>;
formatValue: (item: T) => any;
compareValue: (current: T, item: T) => boolean;
getItemKey: (item: T) => string;
options: T[];
premium?: boolean;
onCheckOptionIsPremium?: (item: T) => boolean;
}
export function SettingsPicker<T>({
getValue,
updateValue,
formatValue,
compareValue,
options,
getItemKey,
premium,
onCheckOptionIsPremium = () => true
}: PickerOptions<T>) {
const colors = useThemeStore((state) => state.colors); const colors = useThemeStore((state) => state.colors);
const [trashInterval, setTrashInterval] = useState( const menuRef = useRef<any>();
db.settings.getTrashCleanupInterval()
);
const menuRef = useRef();
const [width, setWidth] = useState(0); const [width, setWidth] = useState(0);
const [currentValue, setCurrentValue] = useState(getValue());
const onChange = async (item: T) => {
if (premium && onCheckOptionIsPremium?.(item)) {
await PremiumService.verify(async () => {
menuRef.current?.hide();
await updateValue(item);
setCurrentValue(item);
});
return;
}
const onChange = (item) => {
menuRef.current?.hide(); menuRef.current?.hide();
setTrashInterval(item); await updateValue(item);
db.settings.setTrashCleanupInterval(item); setCurrentValue(item);
}; };
return ( return (
@@ -78,40 +104,42 @@ export const TrashIntervalSelector = () => {
padding: 12 padding: 12
}} }}
> >
<Paragraph> <Paragraph>{formatValue(currentValue)}</Paragraph>
{trashInterval === -1 ? "Never" : trashInterval + " days"}
</Paragraph>
<Icon color={colors.icon} name="menu-down" size={SIZE.md} /> <Icon color={colors.icon} name="menu-down" size={SIZE.md} />
</PressableButton> </PressableButton>
} }
> >
{[-1, 7, 30, 365].map((item) => ( {options.map((item) => (
<MenuItem <MenuItem
key={item.toString()} key={getItemKey(item)}
onPress={async () => { onPress={async () => {
if (item === -1) {
await PremiumService.verify(() => {
onChange(item);
});
return;
}
onChange(item); onChange(item);
}} }}
style={{ style={{
backgroundColor: backgroundColor: compareValue(currentValue, item)
trashInterval === item ? colors.nav : "transparent", ? colors.nav
: "transparent",
width: "100%", width: "100%",
maxWidth: width maxWidth: width
}} }}
textStyle={{ textStyle={{
fontSize: SIZE.md, fontSize: SIZE.md,
color: trashInterval === item ? colors.accent : colors.pri color: compareValue(currentValue, item)
? colors.accent
: colors.pri
}} }}
> >
{item === -1 ? "Never" : item + " days"} {formatValue(item)}
</MenuItem> </MenuItem>
))} ))}
</Menu> </Menu>
</View> </View>
); );
}; }
export function createSettingsPicker<T>(props: PickerOptions<T>) {
const Selector = () => {
return <SettingsPicker {...props} />;
};
return Selector;
}

View File

@@ -0,0 +1,130 @@
/*
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 { db } from "../../../common/database";
import { ToastEvent } from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { useSettingStore } from "../../../stores/use-setting-store";
import { MenuItemsList } from "../../../utils/constants";
import { createSettingsPicker } from ".";
import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
import { DATE_FORMATS, TIME_FORMATS } from "@notesnook/core/common";
import dayjs from "dayjs";
export const FontPicker = createSettingsPicker({
getValue: () => useSettingStore.getState().settings.defaultFontFamily,
updateValue: (item) => {
SettingsService.set({
defaultFontFamily: item
});
},
formatValue: (item) => {
return getFontById(typeof item === "object" ? item.id : item).title;
},
getItemKey: (item) => item.id,
options: getFonts(),
compareValue: (current, item) => current === item.id
});
export const HomePicker = createSettingsPicker({
getValue: () => useSettingStore.getState().settings.homepage,
updateValue: (item) => {
SettingsService.set({ homepage: item.name });
ToastEvent.show({
heading: "Homepage set to " + item.name,
message: "Restart the app for changes to take effect.",
type: "success"
});
},
formatValue: (item) => {
return typeof item === "object" ? item.name : item;
},
getItemKey: (item) => item.name,
options: MenuItemsList.slice(0, MenuItemsList.length - 1),
compareValue: (current, item) => current === item.name,
premium: true
});
export const TrashIntervalPicker = createSettingsPicker({
getValue: () => db.settings.getTrashCleanupInterval(),
updateValue: (item) => {
db.settings.setTrashCleanupInterval(item);
},
formatValue: (item) => {
return item === -1 ? "Never" : item + " days";
},
getItemKey: (item) => item.toString(),
options: [-1, 7, 30, 365],
compareValue: (current, item) => current === item,
premium: true
});
export const DateFormatPicker = createSettingsPicker({
getValue: () => db.settings.getDateFormat(),
updateValue: (item) => {
db.settings.setDateFormat(item);
useSettingStore.setState({
dateFormat: item
});
},
formatValue: (item) => {
return `${item} (${dayjs().format(item)})`;
},
getItemKey: (item) => item,
options: DATE_FORMATS,
compareValue: (current, item) => current === item
});
const TimeFormats = {
"12-hour": "hh:mm A",
"24-hour": "HH:mm"
};
export const TimeFormatPicker = createSettingsPicker({
getValue: () => db.settings.getTimeFormat(),
updateValue: (item) => {
db.settings.setTimeFormat(item);
useSettingStore.setState({
timeFormat: item
});
},
formatValue: (item) => {
return `${item} (${dayjs().format(TimeFormats[item])})`;
},
getItemKey: (item) => item,
options: TIME_FORMATS,
compareValue: (current, item) => current === item
});
export const BackupReminderPicker = createSettingsPicker({
getValue: () => useSettingStore.getState().settings.reminder,
updateValue: (item) => {
SettingsService.set({ reminder: item });
},
formatValue: (item) => {
return item === "useroff"
? "Never"
: item.slice(0, 1).toUpperCase() + item.slice(1);
},
getItemKey: (item) => item,
options: ["useroff", "daily", "weekly", "monthly"],
compareValue: (current, item) => current === item,
premium: true,
onCheckOptionIsPremium: (item) => item !== "useroff"
});

View File

@@ -35,14 +35,14 @@ import ExportNotesSheet from "../../components/sheets/export-notes";
import { Issue } from "../../components/sheets/github/issue"; import { Issue } from "../../components/sheets/github/issue";
import { Progress } from "../../components/sheets/progress"; import { Progress } from "../../components/sheets/progress";
import { Update } from "../../components/sheets/update"; import { Update } from "../../components/sheets/update";
import { useVaultStatus, VaultStatusType } from "../../hooks/use-vault-status"; import { VaultStatusType, useVaultStatus } from "../../hooks/use-vault-status";
import BackupService from "../../services/backup"; import BackupService from "../../services/backup";
import BiometicService from "../../services/biometrics"; import BiometicService from "../../services/biometrics";
import { import {
ToastEvent,
eSendEvent, eSendEvent,
openVault, openVault,
presentSheet, presentSheet
ToastEvent
} from "../../services/event-manager"; } from "../../services/event-manager";
import { setLoginMessage } from "../../services/message"; import { setLoginMessage } from "../../services/message";
import Navigation from "../../services/navigation"; import Navigation from "../../services/navigation";
@@ -53,7 +53,6 @@ import Sync from "../../services/sync";
import { clearAllStores } from "../../stores"; import { clearAllStores } from "../../stores";
import { useSettingStore } from "../../stores/use-setting-store"; import { useSettingStore } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store"; import { useUserStore } from "../../stores/use-user-store";
import { AndroidModule } from "../../utils";
import { getColorScheme, toggleDarkMode } from "../../utils/color-scheme/utils"; import { getColorScheme, toggleDarkMode } from "../../utils/color-scheme/utils";
import { SUBSCRIPTION_STATUS } from "../../utils/constants"; import { SUBSCRIPTION_STATUS } from "../../utils/constants";
import { import {
@@ -63,6 +62,7 @@ import {
eOpenRecoveryKeyDialog, eOpenRecoveryKeyDialog,
eOpenRestoreDialog eOpenRestoreDialog
} from "../../utils/events"; } from "../../utils/events";
import { NotesnookModule } from "../../utils/notesnook-module";
import { sleep } from "../../utils/time"; import { sleep } from "../../utils/time";
import { MFARecoveryCodes, MFASheet } from "./2fa"; import { MFARecoveryCodes, MFASheet } from "./2fa";
import AppLock from "./app-lock"; import AppLock from "./app-lock";
@@ -762,7 +762,7 @@ export const settingsGroups: SettingSection[] = [
modifer: () => { modifer: () => {
const settings = SettingsService.get(); const settings = SettingsService.get();
Platform.OS === "android" Platform.OS === "android"
? AndroidModule.setSecureMode(!settings.privacyScreen) ? NotesnookModule.setSecureMode(!settings.privacyScreen)
: enabled(true); : enabled(true);
SettingsService.set({ privacyScreen: !settings.privacyScreen }); SettingsService.set({ privacyScreen: !settings.privacyScreen });

View File

@@ -16,6 +16,34 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Platform } from "react-native";
import {
beginBackgroundTask,
endBackgroundTask
} from "react-native-begin-background-task";
async function doInBackground(callback: () => Promise<void>) {
if (Platform.OS === "ios") {
try {
const bgTaskId = await beginBackgroundTask();
const result = await callback();
await endBackgroundTask(bgTaskId);
return result;
} catch (e) {
return (e as Error).message;
}
} else {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
try {
const result = await callback();
resolve(result);
} catch (e) {
resolve((e as Error).message);
}
});
}
}
// import BackgroundFetch from "react-native-background-fetch"; // import BackgroundFetch from "react-native-background-fetch";
// import { DatabaseLogger, db } from "../common/database"; // import { DatabaseLogger, db } from "../common/database";
@@ -120,4 +148,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// start, // start,
// registerHeadlessTask // registerHeadlessTask
// }; // };
export {}; export default { doInBackground };

View File

@@ -26,11 +26,11 @@ import { presentDialog } from "../components/dialog/functions";
import { DatabaseLogger, db } from "../common/database"; import { DatabaseLogger, db } from "../common/database";
import storage from "../common/database/storage"; import storage from "../common/database/storage";
import { eCloseSheet } from "../utils/events"; import { eCloseSheet } from "../utils/events";
import { sanitizeFilename } from "../utils/sanitizer";
import { sleep } from "../utils/time"; import { sleep } from "../utils/time";
import { eSendEvent, presentSheet, ToastEvent } from "./event-manager"; import { eSendEvent, presentSheet, ToastEvent } from "./event-manager";
import SettingsService from "./settings"; import SettingsService from "./settings";
import PremiumService from "./premium"; import PremiumService from "./premium";
import { sanitizeFilename } from "@notesnook/common";
const MS_DAY = 86400000; const MS_DAY = 86400000;
const MS_WEEK = MS_DAY * 7; const MS_WEEK = MS_DAY * 7;

View File

@@ -25,9 +25,10 @@ import * as ScopedStorage from "react-native-scoped-storage";
import RNFetchBlob from "react-native-blob-util"; import RNFetchBlob from "react-native-blob-util";
import { DatabaseLogger, db } from "../common/database/index"; import { DatabaseLogger, db } from "../common/database/index";
import Storage from "../common/database/storage"; import Storage from "../common/database/storage";
import { toTXT } from "../utils"; import { convertNoteToText } from "../utils/note-to-text";
import { sanitizeFilename } from "../utils/sanitizer";
import { sleep } from "../utils/time"; import { sleep } from "../utils/time";
import { sanitizeFilename } from "@notesnook/common";
const MIMETypes = { const MIMETypes = {
txt: "text/plain", txt: "text/plain",
@@ -149,7 +150,7 @@ async function exportAs(type, note, bulk) {
} }
break; break;
case "txt": case "txt":
data = await toTXT(note); data = await convertNoteToText(note);
break; break;
} }

View File

@@ -22,8 +22,8 @@ import Orientation from "react-native-orientation";
import { enabled } from "react-native-privacy-snapshot"; import { enabled } from "react-native-privacy-snapshot";
import { MMKV } from "../common/database/mmkv"; import { MMKV } from "../common/database/mmkv";
import { SettingStore, useSettingStore } from "../stores/use-setting-store"; import { SettingStore, useSettingStore } from "../stores/use-setting-store";
import { AndroidModule } from "../utils";
import { getColorScheme } from "../utils/color-scheme/utils"; import { getColorScheme } from "../utils/color-scheme/utils";
import { NotesnookModule } from "../utils/notesnook-module";
import { scale, updateSize } from "../utils/size"; import { scale, updateSize } from "../utils/size";
import { DDS } from "./device-detection"; import { DDS } from "./device-detection";
import { setAutobackOffMessage } from "./message"; import { setAutobackOffMessage } from "./message";
@@ -63,13 +63,13 @@ function init() {
function setPrivacyScreen(settings: SettingStore["settings"]) { function setPrivacyScreen(settings: SettingStore["settings"]) {
if (settings.privacyScreen || settings.appLockMode === "background") { if (settings.privacyScreen || settings.appLockMode === "background") {
if (Platform.OS === "android") { if (Platform.OS === "android") {
AndroidModule.setSecureMode(true); NotesnookModule.setSecureMode(true);
} else { } else {
enabled(true); enabled(true);
} }
} else { } else {
if (Platform.OS === "android") { if (Platform.OS === "android") {
AndroidModule.setSecureMode(false); NotesnookModule.setSecureMode(false);
} else { } else {
enabled(false); enabled(false);
} }

View File

@@ -17,15 +17,15 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import NetInfo from "@react-native-community/netinfo";
import { EVENTS } from "@notesnook/core/common"; import { EVENTS } from "@notesnook/core/common";
import { initAfterSync } from "../stores/index"; import NetInfo from "@react-native-community/netinfo";
import { SyncStatus, useUserStore } from "../stores/use-user-store";
import { doInBackground } from "../utils";
import { db } from "../common/database"; import { db } from "../common/database";
import { DatabaseLogger } from "../common/database/index"; import { DatabaseLogger } from "../common/database/index";
import { initAfterSync } from "../stores/index";
import { SyncStatus, useUserStore } from "../stores/use-user-store";
import { ToastEvent } from "./event-manager"; import { ToastEvent } from "./event-manager";
import SettingsService from "./settings"; import SettingsService from "./settings";
import BackgroundSync from "./background-sync";
export const ignoredMessages = [ export const ignoredMessages = [
"Sync already running", "Sync already running",
@@ -61,7 +61,7 @@ const run = async (
let error = null; let error = null;
try { try {
let res = await doInBackground(async () => { let res = await BackgroundSync.doInBackground(async () => {
try { try {
await db.sync(full, forced); await db.sync(full, forced);
return true; return true;

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import create, { State } from "zustand"; import create, { State } from "zustand";
import { history } from "../utils";
type Item = { type Item = {
id: string; id: string;
@@ -30,23 +29,16 @@ export interface SelectionStore extends State {
setAll: (all: Array<unknown>) => void; setAll: (all: Array<unknown>) => void;
setSelectionMode: (mode: boolean) => void; setSelectionMode: (mode: boolean) => void;
setSelectedItem: (item: Item) => void; setSelectedItem: (item: Item) => void;
clearSelection: (noanimation: boolean) => void; clearSelection: () => void;
} }
export const useSelectionStore = create<SelectionStore>((set, get) => ({ export const useSelectionStore = create<SelectionStore>((set, get) => ({
selectedItemsList: [], selectedItemsList: [],
selectionMode: false, selectionMode: false,
setAll: (all) => { setAll: (all) => {
history.selectedItemsList = all as never[];
set({ selectedItemsList: all }); set({ selectedItemsList: all });
}, },
setSelectionMode: (mode) => { setSelectionMode: (mode) => {
if (!mode) {
history.selectedItemsList = [];
history.selectionMode = false;
} else {
history.selectionMode = true;
}
set({ set({
selectionMode: mode, selectionMode: mode,
selectedItemsList: mode ? get().selectedItemsList : [] selectedItemsList: mode ? get().selectedItemsList : []
@@ -61,17 +53,13 @@ export const useSelectionStore = create<SelectionStore>((set, get) => ({
selectedItems.push(item); selectedItems.push(item);
} }
selectedItems = [...new Set(selectedItems)]; selectedItems = [...new Set(selectedItems)];
history.selectedItemsList = selectedItems as never[];
history.selectionMode =
selectedItems.length > 0 ? get().selectionMode : false;
set({ set({
selectedItemsList: selectedItems, selectedItemsList: selectedItems,
selectionMode: history.selectionMode selectionMode: get().selectionMode
}); });
}, },
clearSelection: () => { clearSelection: () => {
history.selectedItemsList = [];
history.selectionMode = false;
set({ selectionMode: false, selectedItemsList: [] }); set({ selectionMode: false, selectedItemsList: [] });
} }
})); }));

View File

@@ -18,10 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Platform, StatusBar } from "react-native"; import { Platform, StatusBar } from "react-native";
import { AndroidModule } from "..";
import { eSendEvent } from "../../services/event-manager"; import { eSendEvent } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store"; import { useThemeStore } from "../../stores/use-theme-store";
import { eThemeUpdated } from "../events"; import { eThemeUpdated } from "../events";
import { NotesnookModule } from "../notesnook-module";
export const ACCENT = { export const ACCENT = {
color: "#008837", color: "#008837",
@@ -137,7 +137,7 @@ export function setColorScheme(colors = COLOR_SCHEME, accent = ACCENT) {
false false
); );
if (Platform.OS === "android") { if (Platform.OS === "android") {
AndroidModule.setBackgroundColor(COLOR_SCHEME.bg); NotesnookModule.setBackgroundColor(COLOR_SCHEME.bg);
StatusBar.setBackgroundColor("transparent", false); StatusBar.setBackgroundColor("transparent", false);
StatusBar.setTranslucent(true, false); StatusBar.setTranslucent(true, false);
} }

View File

@@ -100,16 +100,6 @@ export const SUBSCRIPTION_PROVIDER = {
} }
}; };
export const WeekDayNames = {
0: "Sunday",
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday"
};
export const MenuItemsList = [ export const MenuItemsList = [
{ {
name: "Notes", name: "Notes",
@@ -214,15 +204,3 @@ export const BUTTON_TYPES = {
opacity: 0.12 opacity: 0.12
} }
}; };
export const bgTaskOptions = {
taskName: "notesnookSync",
taskTitle: "Notesnook Sync",
taskDesc: "Syncing your notes.",
taskIcon: {
name: "ic_stat_name",
type: "drawable"
},
color: "#ffffff",
linkingURI: "com.streetwriters.notesnook://launch"
};

View File

@@ -0,0 +1,28 @@
/*
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/>.
*/
export function getElevationStyle(elevation: number) {
return {
elevation,
shadowColor: "black",
shadowOffset: { width: 0.3 * elevation, height: 0.5 * elevation },
shadowOpacity: 0.2,
shadowRadius: 0.7 * elevation
};
}

View File

@@ -18,18 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Linking } from "react-native"; import { Linking } from "react-native";
import { history } from "."; import { db } from "../common/database";
import { presentDialog } from "../components/dialog/functions";
import { eSendEvent, ToastEvent } from "../services/event-manager"; import { eSendEvent, ToastEvent } from "../services/event-manager";
import Navigation from "../services/navigation"; import Navigation from "../services/navigation";
import SearchService from "../services/search"; import SearchService from "../services/search";
import { useSelectionStore } from "../stores/use-selection-store";
import { useMenuStore } from "../stores/use-menu-store"; import { useMenuStore } from "../stores/use-menu-store";
import { db } from "../common/database";
import { eClearEditor, eOnTopicSheetUpdate } from "./events";
import { useRelationStore } from "../stores/use-relation-store"; import { useRelationStore } from "../stores/use-relation-store";
import { presentDialog } from "../components/dialog/functions"; import { useSelectionStore } from "../stores/use-selection-store";
import { eClearEditor, eOnTopicSheetUpdate } from "./events";
function deleteConfirmDialog(items, type, context) { function confirmDeleteAllNotes(items, type, context) {
return new Promise((resolve) => { return new Promise((resolve) => {
presentDialog({ presentDialog({
title: `Delete ${ title: `Delete ${
@@ -68,33 +67,25 @@ export const deleteItems = async (item, context) => {
}); });
return; return;
} }
if (item && item.id && history.selectedItemsList.length === 0) { if (
history.selectedItemsList = []; item &&
history.selectedItemsList.push(item); item.id &&
useSelectionStore.getState().selectedItemsList.length === 0
) {
useSelectionStore.getState().setSelectedItem(item);
} }
let notes = history.selectedItemsList.filter((i) => i.type === "note"); const { selectedItemsList } = useSelectionStore.getState();
let notebooks = history.selectedItemsList.filter(
(i) => i.type === "notebook"
);
let topics = history.selectedItemsList.filter((i) => i.type === "topic");
let reminders = history.selectedItemsList.filter(
(i) => i.type === "reminder"
);
let routesForUpdate = [ let notes = selectedItemsList.filter((i) => i.type === "note");
"TaggedNotes", let notebooks = selectedItemsList.filter((i) => i.type === "notebook");
"ColoredNotes", let topics = selectedItemsList.filter((i) => i.type === "topic");
"TopicNotes", let reminders = selectedItemsList.filter((i) => i.type === "reminder");
"Favorites",
"Notes"
];
if (reminders.length > 0) { if (reminders.length > 0) {
for (let reminder of reminders) { for (let reminder of reminders) {
await db.reminders.remove(reminder.id); await db.reminders.remove(reminder.id);
} }
routesForUpdate.push("Reminders");
useRelationStore.getState().update(); useRelationStore.getState().update();
} }
@@ -111,12 +102,11 @@ export const deleteItems = async (item, context) => {
} }
await db.notes.delete(note.id); await db.notes.delete(note.id);
} }
routesForUpdate.push("Trash");
eSendEvent(eClearEditor); eSendEvent(eClearEditor);
} }
if (topics?.length > 0) { if (topics?.length > 0) {
const result = await deleteConfirmDialog(topics, "topic", context); const result = await confirmDeleteAllNotes(topics, "topic", context);
if (result.delete) { if (result.delete) {
for (const topic of topics) { for (const topic of topics) {
if (result.deleteNotes) { if (result.deleteNotes) {
@@ -127,7 +117,6 @@ export const deleteItems = async (item, context) => {
} }
await db.notebooks.notebook(topic.notebookId).topics.delete(topic.id); await db.notebooks.notebook(topic.notebookId).topics.delete(topic.id);
} }
routesForUpdate.push("Notebook", "Notebooks");
useMenuStore.getState().setMenuPins(); useMenuStore.getState().setMenuPins();
ToastEvent.show({ ToastEvent.show({
heading: `${topics.length > 1 ? "Topics" : "Topic"} deleted`, heading: `${topics.length > 1 ? "Topics" : "Topic"} deleted`,
@@ -137,7 +126,7 @@ export const deleteItems = async (item, context) => {
} }
if (notebooks?.length > 0) { if (notebooks?.length > 0) {
const result = await deleteConfirmDialog(notebooks, "notebook", context); const result = await confirmDeleteAllNotes(notebooks, "notebook", context);
if (result.delete) { if (result.delete) {
let ids = notebooks.map((i) => i.id); let ids = notebooks.map((i) => i.id);
@@ -156,17 +145,17 @@ export const deleteItems = async (item, context) => {
} }
} }
await db.notebooks.delete(...ids); await db.notebooks.delete(...ids);
routesForUpdate.push("Notebook", "Notebooks");
useMenuStore.getState().setMenuPins(); useMenuStore.getState().setMenuPins();
} }
} }
Navigation.queueRoutesForUpdate(); Navigation.queueRoutesForUpdate();
let msgPart = history.selectedItemsList.length === 1 ? " item" : " items"; let message = `${selectedItemsList.length} ${
let message = history.selectedItemsList.length + msgPart + " moved to trash."; selectedItemsList.length === 1 ? "item" : "items"
} moved to trash.`;
let itemsCopy = [...history.selectedItemsList]; let deletedItems = [...selectedItemsList];
if ( if (
topics.length === 0 && topics.length === 0 &&
reminders.length === 0 && reminders.length === 0 &&
@@ -178,13 +167,12 @@ export const deleteItems = async (item, context) => {
func: async () => { func: async () => {
let trash = db.trash.all; let trash = db.trash.all;
let ids = []; let ids = [];
for (var i = 0; i < itemsCopy.length; i++) { for (var i = 0; i < deletedItems.length; i++) {
let it = itemsCopy[i]; let it = deletedItems[i];
let trashItem = trash.find((item) => item.id === it.id); let trashItem = trash.find((item) => item.id === it.id);
ids.push(trashItem.id); ids.push(trashItem.id);
} }
await db.trash.restore(...ids); await db.trash.restore(...ids);
Navigation.queueRoutesForUpdate(); Navigation.queueRoutesForUpdate();
useMenuStore.getState().setMenuPins(); useMenuStore.getState().setMenuPins();
useMenuStore.getState().setColorNotes(); useMenuStore.getState().setColorNotes();
@@ -193,9 +181,8 @@ export const deleteItems = async (item, context) => {
actionText: "Undo" actionText: "Undo"
}); });
} }
history.selectedItemsList = [];
Navigation.queueRoutesForUpdate(); Navigation.queueRoutesForUpdate();
useSelectionStore.getState().clearSelection(true); useSelectionStore.getState().clearSelection();
useMenuStore.getState().setMenuPins(); useMenuStore.getState().setMenuPins();
useMenuStore.getState().setColorNotes(); useMenuStore.getState().setColorNotes();
SearchService.updateAndSearch(); SearchService.updateAndSearch();
@@ -204,8 +191,7 @@ export const deleteItems = async (item, context) => {
export const openLinkInBrowser = async (link) => { export const openLinkInBrowser = async (link) => {
try { try {
const url = link; Linking.openURL(link);
Linking.openURL(url);
} catch (error) { } catch (error) {
console.log(error.message); console.log(error.message);
} }

View File

@@ -1,177 +0,0 @@
/*
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 { Dimensions, NativeModules, Platform } from "react-native";
import {
beginBackgroundTask,
endBackgroundTask
} from "react-native-begin-background-task";
import RNTooltips from "react-native-tooltips";
import { db } from "../common/database";
import { tabBarRef } from "./global-refs";
let prevTarget = null;
export const TOOLTIP_POSITIONS = {
LEFT: 1,
RIGHT: 2,
TOP: 3,
BOTTOM: 4
};
export const sortSettings = {
sort: "default",
/**
* @type {"desc" | "asc"}
*/
sortOrder: "desc"
};
export const editing = {
currentlyEditing: false,
isFullscreen: false,
actionAfterFirstSave: {
type: null
},
isFocused: false,
focusType: null,
movedAway: true,
tooltip: false,
isRestoringState: false
};
export const selection = {
data: [],
type: null,
selectedItems: []
};
export const history = {
selectedItemsList: [],
selectionMode: false
};
export function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
export const AndroidModule = NativeModules.NNativeModule;
export let dWidth = Dimensions.get("window").width;
export let dHeight = Dimensions.get("window").height;
export const InteractionManager = {
runAfterInteractions: (func, time = 300) => setTimeout(func, time)
};
export function getElevation(elevation) {
return {
elevation,
shadowColor: "black",
shadowOffset: { width: 0.3 * elevation, height: 0.5 * elevation },
shadowOpacity: 0.2,
shadowRadius: 0.7 * elevation
};
}
export async function doInBackground(cb) {
if (Platform.OS === "ios") {
let bgTaskId;
try {
bgTaskId = await beginBackgroundTask();
let res = await cb();
await endBackgroundTask(bgTaskId);
return res;
} catch (e) {
return e.message;
}
} else {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (res) => {
try {
let result = await cb();
res(result);
} catch (e) {
res(e.message);
}
});
}
}
export function nth(n) {
return (
["st", "nd", "rd"][(((((n < 0 ? -n : n) + 90) % 100) - 10) % 10) - 1] ||
"th"
);
}
export function setWidthHeight(size) {
dWidth = size.width;
dHeight = size.height;
}
export function getTotalNotes(item) {
if (!item || (item.type !== "notebook" && item.type !== "topic")) return 0;
if (item.type === "topic") {
return (
db.notebooks.notebook(item.notebookId)?.topics.topic(item.id)
?.totalNotes || 0
);
}
return db.notebooks.notebook(item.id)?.totalNotes || 0;
}
export async function toTXT(note, template = true) {
let text;
if (note.locked) {
text = await db.notes.note(note.id).export("txt", note.content, template);
} else {
text = await db.notes.note(note.id).export("txt", undefined, template);
}
return text;
}
export function showTooltip(event, text, position = 2) {
if (!event._targetInst?.ref?.current) return;
prevTarget && RNTooltips.Dismiss(prevTarget);
prevTarget = null;
prevTarget = event._targetInst.ref.current;
RNTooltips.Show(prevTarget, tabBarRef.current?.node?.current, {
text: text,
tintColor: "#000000",
corner: Platform.OS === "ios" ? 5 : 40,
textSize: 14,
position: position,
duration: 1000,
autoHide: true,
clickToHide: true
});
}
export function toTitleCase(value) {
if (!value) return;
return value.slice(0, 1).toUpperCase() + value.slice(1);
}

View File

@@ -0,0 +1,32 @@
/*
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 { db } from "../common/database";
import { NoteType } from "./types";
export async function convertNoteToText(note: NoteType, template = true) {
let text;
if (note.locked) {
text = await db.notes
?.note(note.id)
.export("txt", note.content as unknown as string, template);
} else {
text = await db.notes?.note(note.id).export("txt", undefined, template);
}
return text;
}

View File

@@ -0,0 +1,35 @@
/*
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 { NativeModules, Platform } from "react-native";
interface NotesnookModuleInterface {
getActivityName: () => Promise<string>;
setBackgroundColor: (color: string) => void;
setSecureMode: (enabled: boolean) => void;
}
export const NotesnookModule: NotesnookModuleInterface = Platform.select({
ios: {
getActivityName: () => {},
setBackgroundColor: () => {},
setSecureMode: () => {}
},
android: NativeModules.NNativeModule
});

View File

@@ -17,115 +17,5 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { formatDate } from "@notesnook/core/utils/date";
import { db } from "../../common/database";
import { formatReminderTime } from "@notesnook/core/collections/reminders";
export const sleep = (duration: number) => export const sleep = (duration: number) =>
new Promise((resolve) => setTimeout(() => resolve(true), duration)); new Promise((resolve) => setTimeout(() => resolve(true), duration));
export function timeSince(date: number) {
const seconds = Math.floor((Date.now() - date) / 1000);
let interval = Math.floor(seconds / 31536000);
if (interval > 0.9) {
return interval < 2 ? interval + "yr ago" : interval + "yr ago";
}
interval = Math.floor(seconds / 2592000);
if (interval > 0.9) {
return interval < 2 ? interval + "mo ago" : interval + "mo ago";
}
interval = Math.floor(seconds / (86400 * 7));
if (interval > 0.9) {
if (interval === 4) return "1mo ago";
return interval < 2 ? interval + "w ago" : interval + "w ago";
}
interval = Math.floor(seconds / 86400);
if (interval > 0.9) {
return interval < 2 ? interval + "d ago" : interval + "d ago";
}
interval = Math.floor(seconds / 3600);
if (interval > 0.9) {
return interval < 2 ? interval + "h ago" : interval + "h ago";
}
interval = Math.floor(seconds / 60);
if (interval > 0.9) {
return interval < 2 ? interval + "m ago" : interval + "m ago";
}
return Math.floor(seconds) < 0 ? "0s ago" : Math.floor(seconds) + "s ago";
}
export const timeConverter = (timestamp: number | undefined | null) => {
if (!timestamp) return;
const d = new Date(timestamp); // Convert the passed timestamp to milliseconds
const yyyy = d.getFullYear();
const dd = ("0" + d.getDate()).slice(-2); // Add leading 0.
const currentDay = d.getDay();
const hh = d.getHours();
let h = hh;
const min = ("0" + d.getMinutes()).slice(-2); // Add leading 0.
let ampm = "AM";
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const months = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
];
if (hh > 12) {
h = hh - 12;
ampm = "PM";
} else if (hh === 12) {
h = 12;
ampm = "PM";
} else if (hh === 0) {
h = 12;
}
// ie: 2013-02-18, 8:35 AM
const time =
days[currentDay] +
" " +
dd +
" " +
months[d.getMonth()] +
", " +
yyyy +
", " +
h +
":" +
min +
" " +
ampm;
return time;
};
export function getFormattedDate(
date: any,
type: "time" | "date-time" | "date" = "date-time"
) {
return formatDate(date, {
dateFormat: db.settings?.getDateFormat() as string,
timeFormat: db.settings?.getTimeFormat() as string,
type: type
});
}
export function getFormattedReminderTime(reminder: any, short = false) {
return formatReminderTime(reminder, short, {
dateFormat: db.settings?.getDateFormat() as string,
timeFormat: db.settings?.getTimeFormat() as string
});
}

View File

@@ -0,0 +1,53 @@
/*
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 RNTooltips from "react-native-tooltips";
import { tabBarRef } from "./global-refs";
import { Platform } from "react-native";
export const POSITIONS = {
LEFT: 1,
RIGHT: 2,
TOP: 3,
BOTTOM: 4
};
let prevTarget: any = null;
function show(event: any, text: string, position = 2) {
if (!event._targetInst?.ref?.current) return;
prevTarget && RNTooltips.Dismiss(prevTarget);
prevTarget = null;
prevTarget = event._targetInst.ref.current;
RNTooltips.Show(prevTarget, tabBarRef.current?.node?.current, {
text: text,
tintColor: "#000000",
corner: Platform.OS === "ios" ? 5 : 40,
textSize: 14,
position: position,
duration: 1000,
autoHide: true,
clickToHide: true
});
}
const NativeTooltip = {
show,
POSITIONS
};
export default NativeTooltip;

View File

@@ -33,6 +33,7 @@
"@notesnook/editor": "*", "@notesnook/editor": "*",
"@notesnook/editor-mobile": "*", "@notesnook/editor-mobile": "*",
"react-native-swiper-flatlist": "3.2.2", "react-native-swiper-flatlist": "3.2.2",
"@notesnook/logger": "*" "@notesnook/logger": "*",
"@notesnook/common": "*"
} }
} }

View File

@@ -274,10 +274,10 @@ index 69065f0..0000000
-export default forwardRef(Menu); -export default forwardRef(Menu);
diff --git a/node_modules/react-native-reanimated-material-menu/src/Menu.tsx b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx diff --git a/node_modules/react-native-reanimated-material-menu/src/Menu.tsx b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx
new file mode 100644 new file mode 100644
index 0000000..20469ee index 0000000..c3bc1c9
--- /dev/null --- /dev/null
+++ b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx +++ b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx
@@ -0,0 +1,289 @@ @@ -0,0 +1,303 @@
+import React from 'react'; +import React from 'react';
+ +
+import { +import {
@@ -288,6 +288,7 @@ index 0000000..20469ee
+ LayoutChangeEvent, + LayoutChangeEvent,
+ Modal, + Modal,
+ Platform, + Platform,
+ ScrollView,
+ StatusBar, + StatusBar,
+ StyleSheet, + StyleSheet,
+ TouchableWithoutFeedback, + TouchableWithoutFeedback,
@@ -325,6 +326,7 @@ index 0000000..20469ee
+ +
+const EASING = Easing.bezier(0.4, 0, 0.2, 1); +const EASING = Easing.bezier(0.4, 0, 0.2, 1);
+const SCREEN_INDENT = 8; +const SCREEN_INDENT = 8;
+const SCREEN_INDENT_VERTICAL = 80;
+ +
+class Menu extends React.Component<MenuProps, State> { +class Menu extends React.Component<MenuProps, State> {
+ _container: View | null = null; + _container: View | null = null;
@@ -384,7 +386,7 @@ index 0000000..20469ee
+ } + }
+ +
+ const { width, height } = e.nativeEvent.layout; + const { width, height } = e.nativeEvent.layout;
+ + let timeout:any = 0;
+ this.setState( + this.setState(
+ { + {
+ menuState: States.Animating, + menuState: States.Animating,
@@ -405,7 +407,17 @@ index 0000000..20469ee
+ easing: EASING, + easing: EASING,
+ useNativeDriver: false, + useNativeDriver: false,
+ }), + }),
+ ]).start(); + ]).start(({finished}) => {
+ if (finished) {
+ clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ this.setState({
+ menuState: States.Shown
+ })
+ },20)
+
+ }
+ });
+ }, + },
+ ); + );
+ }; + };
@@ -464,7 +476,7 @@ index 0000000..20469ee
+ +
+ // Adjust position of menu + // Adjust position of menu
+ let { left, top } = this.state; + let { left, top } = this.state;
+ const transforms = []; + const transforms:any[] = [];
+ +
+ if ( + if (
+ (isRTL && left + buttonWidth - menuWidth > SCREEN_INDENT) || + (isRTL && left + buttonWidth - menuWidth > SCREEN_INDENT) ||
@@ -472,7 +484,7 @@ index 0000000..20469ee
+ ) { + ) {
+ transforms.push({ + transforms.push({
+ translateX: Animated.multiply(menuSizeAnimation.x, -1), + translateX: Animated.multiply(menuSizeAnimation.x, -1),
+ }); + } as never);
+ +
+ left = Math.min(windowWidth - SCREEN_INDENT, left + buttonWidth); + left = Math.min(windowWidth - SCREEN_INDENT, left + buttonWidth);
+ } else if (left < SCREEN_INDENT) { + } else if (left < SCREEN_INDENT) {
@@ -480,26 +492,26 @@ index 0000000..20469ee
+ } + }
+ +
+ // Flip by Y axis if menu hits bottom screen border + // Flip by Y axis if menu hits bottom screen border
+ if (top > windowHeight - menuHeight - SCREEN_INDENT) { + if (top > windowHeight - menuHeight - SCREEN_INDENT_VERTICAL) {
+ transforms.push({ + transforms.push({
+ translateY: Animated.multiply(menuSizeAnimation.y, -1), + translateY: Animated.multiply(menuSizeAnimation.y, -1),
+ }); + } as never);
+ +
+ top = windowHeight - SCREEN_INDENT; + top = windowHeight - SCREEN_INDENT_VERTICAL;
+ top = Math.min(windowHeight - SCREEN_INDENT, top + buttonHeight); + top = Math.min(windowHeight - SCREEN_INDENT_VERTICAL, top + buttonHeight);
+ } else if (top < SCREEN_INDENT) { + } else if (top < SCREEN_INDENT_VERTICAL) {
+ top = SCREEN_INDENT; + top = SCREEN_INDENT_VERTICAL;
+ } + }
+ +
+ const shadowMenuContainerStyle = { + const shadowMenuContainerStyle = {
+ opacity: opacityAnimation, + opacity: opacityAnimation,
+ transform: transforms, + transform: transforms,
+ maxHeight: 500,
+ top, + top,
+ +
+ // Switch left to right for rtl devices + // Switch left to right for rtl devices
+ ...(isRTL ? { right: left } : { left }), + ...(isRTL ? { right: left } : { left }),
+ }; + };
+
+ const { menuState } = this.state; + const { menuState } = this.state;
+ const animationStarted = menuState === States.Animating; + const animationStarted = menuState === States.Animating;
+ const modalVisible = menuState === States.Shown || animationStarted; + const modalVisible = menuState === States.Shown || animationStarted;
@@ -529,7 +541,9 @@ index 0000000..20469ee
+ style={[styles.shadowMenuContainer, shadowMenuContainerStyle, style]} + style={[styles.shadowMenuContainer, shadowMenuContainerStyle, style]}
+ > + >
+ <Animated.View style={[styles.menuContainer, animationStarted && menuSize]}> + <Animated.View style={[styles.menuContainer, animationStarted && menuSize]}>
+ {children} + <ScrollView showsVerticalScrollIndicator={menuState !== States.Animating} >
+ {children}
+ </ScrollView>
+ </Animated.View> + </Animated.View>
+ </Animated.View> + </Animated.View>
+ </View> + </View>
@@ -568,3 +582,19 @@ index 0000000..20469ee
+ +
+export default Menu +export default Menu
\ No newline at end of file \ No newline at end of file
diff --git a/node_modules/react-native-reanimated-material-menu/src/MenuItem.js b/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
index 120a870..d6459c0 100644
--- a/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
+++ b/node_modules/react-native-reanimated-material-menu/src/MenuItem.js
@@ -14,6 +14,11 @@ const Touchable =
? TouchableNativeFeedback
: TouchableHighlight;
+/**
+ *
+ * @param {any} param0
+ * @returns
+ */
function MenuItem({
children,
disabled,

View File

@@ -31,7 +31,7 @@ import {
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import Icon from "react-native-vector-icons/MaterialCommunityIcons"; import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../app/common/database"; import { db } from "../app/common/database";
import { getElevation } from "../app/utils"; import { getElevationStyle } from "../app/utils/elevation";
import { initDatabase, useShareStore } from "./store"; import { initDatabase, useShareStore } from "./store";
const ListItem = ({ item, mode, close }) => { const ListItem = ({ item, mode, close }) => {
@@ -262,7 +262,7 @@ export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
alignSelf: "center", alignSelf: "center",
overflow: "hidden", overflow: "hidden",
zIndex: 999, zIndex: 999,
...getElevation(quicknote ? 1 : 5), ...getElevationStyle(quicknote ? 1 : 5),
...extra ...extra
}} }}
> >

View File

@@ -45,7 +45,8 @@ import { db } from "../app/common/database";
import { MMKV } from "../app/common/database/mmkv"; import { MMKV } from "../app/common/database/mmkv";
import Storage from "../app/common/database/storage"; import Storage from "../app/common/database/storage";
import { eSendEvent } from "../app/services/event-manager"; import { eSendEvent } from "../app/services/event-manager";
import { formatBytes, getElevation } from "../app/utils"; import { getElevationStyle } from "../app/utils/elevation";
import { formatBytes } from "@notesnook/common";
import { eOnLoadNote } from "../app/utils/events"; import { eOnLoadNote } from "../app/utils/events";
import { Editor } from "./editor"; import { Editor } from "./editor";
import { Search } from "./search"; import { Search } from "./search";
@@ -459,7 +460,7 @@ const ShareView = ({ quicknote = false }) => {
backgroundColor: colors.bg, backgroundColor: colors.bg,
height: 50 + insets.top, height: 50 + insets.top,
paddingTop: insets.top, paddingTop: insets.top,
...getElevation(1), ...getElevationStyle(1),
marginTop: -insets.top, marginTop: -insets.top,
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",

View File

@@ -26,6 +26,7 @@
"@notesnook/logger": "*", "@notesnook/logger": "*",
"@notesnook/streamable-fs": "*", "@notesnook/streamable-fs": "*",
"@notesnook/theme": "*", "@notesnook/theme": "*",
"@notesnook/common": "*",
"@notesnook/web-clipper": "*", "@notesnook/web-clipper": "*",
"@react-pdf-viewer/core": "^3.12.0", "@react-pdf-viewer/core": "^3.12.0",
"@react-pdf-viewer/toolbar": "^3.12.0", "@react-pdf-viewer/toolbar": "^3.12.0",

View File

@@ -23,7 +23,7 @@ import { db } from "./db";
async function download(hash: string) { async function download(hash: string) {
const attachment = db.attachments?.attachment(hash); const attachment = db.attachments?.attachment(hash);
if (!attachment) return; if (!attachment) return;
const downloadResult = await db.fs.downloadFile( const downloadResult = await db.fs?.downloadFile(
attachment.metadata.hash, attachment.metadata.hash,
attachment.metadata.hash, attachment.metadata.hash,
attachment.chunkSize, attachment.chunkSize,

View File

@@ -299,7 +299,7 @@ const AttachmentMenuItems: MenuItem[] = [
onClick: async ({ attachment, status }) => { onClick: async ({ attachment, status }) => {
const isDownloading = status?.type === "download"; const isDownloading = status?.type === "download";
if (isDownloading) { if (isDownloading) {
await db.fs.cancel(attachment.metadata.hash, "download"); await db.fs?.cancel(attachment.metadata.hash, "download");
} else await saveAttachment(attachment.metadata.hash); } else await saveAttachment(attachment.metadata.hash);
} }
}, },
@@ -311,7 +311,7 @@ const AttachmentMenuItems: MenuItem[] = [
onClick: async ({ attachment, status }) => { onClick: async ({ attachment, status }) => {
const isDownloading = status?.type === "upload"; const isDownloading = status?.type === "upload";
if (isDownloading) { if (isDownloading) {
await db.fs.cancel(attachment.metadata.hash, "upload"); await db.fs?.cancel(attachment.metadata.hash, "upload");
} else } else
await reuploadAttachment( await reuploadAttachment(
attachment.metadata.type, attachment.metadata.type,

View File

@@ -31,7 +31,7 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
) { ) {
if (signal) if (signal)
signal.onabort = async () => { signal.onabort = async () => {
await db.fs.cancel(GROUP_ID, "download"); await db.fs?.cancel(GROUP_ID, "download");
}; };
let index = 0; let index = 0;
@@ -46,7 +46,7 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
onProgress && onProgress(index); onProgress && onProgress(index);
const attachment = attachments[index++]; const attachment = attachments[index++];
await db.fs.downloadFile( await db.fs?.downloadFile(
GROUP_ID, GROUP_ID,
attachment.metadata.hash, attachment.metadata.hash,
attachment.chunkSize, attachment.chunkSize,

View File

@@ -361,7 +361,7 @@ function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
const user = await db.user?.getUser(); const user = await db.user?.getUser();
if (!user) throw new Error("User not authenticated"); if (!user) throw new Error("User not authenticated");
await db.storage.write(`_uk_@${user.email}@_k`, form.recoveryKey); await db.storage?.write(`_uk_@${user.email}@_k`, form.recoveryKey);
await db.sync(true, true); await db.sync(true, true);
navigate("backup"); navigate("backup");
}} }}

32
packages/common/.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.now
__diff_output__
dist
public/workbox
scripts/secrets
test-results

View File

@@ -0,0 +1,3 @@
*
!dist/**/*
!package.json

View File

169
packages/common/package-lock.json generated Normal file
View File

@@ -0,0 +1,169 @@
{
"name": "@notesnook/common",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/common",
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"devDependencies": {
"@types/react": "18.2.9",
"react": "18.2.0",
"timeago.js": "4.0.2",
"typescript": "^4.8.2"
},
"peerDependencies": {
"@notesnook/core": "*",
"react": "*",
"timeago.js": "4.0.2"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
"node_modules/@types/react": {
"version": "18.2.9",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.9.tgz",
"integrity": "sha512-pL3JAesUkF7PEQGxh5XOwdXGV907te6m1/Qe1ERJLgomojS6Ne790QiA7GUl434JEkFA2aAaB6qJ5z4e1zJn/w==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
},
"node_modules/csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"dev": true
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dev": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/timeago.js": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz",
"integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==",
"dev": true
},
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
}
},
"dependencies": {
"@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
"@types/react": {
"version": "18.2.9",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.9.tgz",
"integrity": "sha512-pL3JAesUkF7PEQGxh5XOwdXGV907te6m1/Qe1ERJLgomojS6Ne790QiA7GUl434JEkFA2aAaB6qJ5z4e1zJn/w==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
},
"csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"dev": true
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0"
}
},
"timeago.js": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz",
"integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==",
"dev": true
},
"typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true
}
}
}

View File

@@ -0,0 +1,43 @@
{
"name": "@notesnook/common",
"version": "1.0.0",
"description": "A set of common utilities shared across multiple Notesnook clients and services.",
"main": "dist/index.js",
"scripts": {
"pub": "tsc && np",
"build": "tsc"
},
"repository": {
"type": "git",
"url": "git+https://github.com/streetwriters/notesnook.git"
},
"keywords": [
"logger"
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/streetwriters/notesnook/issues"
},
"homepage": "https://github.com/streetwriters/notesnook#readme",
"np": {
"tests": false,
"releaseDraft": false,
"message": "chore: bump version to %s"
},
"publishConfig": {
"registry": "https://npm.pkg.github.com",
"access": "restricted"
},
"devDependencies": {
"typescript": "^4.8.2",
"@notesnook/core": "*",
"timeago.js": "4.0.2",
"react": "18.2.0",
"@types/react": "18.2.9"
},
"peerDependencies": {
"@notesnook/core": "*",
"timeago.js": "4.0.2",
"react": "*"
}
}

View File

@@ -0,0 +1,24 @@
/*
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/>.
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
import Database from "@notesnook/core/api/index";
export const database = new Database();

View File

@@ -0,0 +1,88 @@
/*
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 { useEffect, useState } from "react";
import { TDate, format, register } from "timeago.js";
const shortLocale: [string, string][] = [
["now", "now"],
["%ss", "in %ss"],
["1m", "in 1m"],
["%sm", "in %sm"],
["1h", "in 1h"],
["%sh", "in %sh"],
["1d", "in 1d"],
["%sd", "in %sd"],
["1w", "in 1w"],
["%sw", "in %sw"],
["1mo", "in 1mo"],
["%smo", "in %smo"],
["1yr", "in 1yr"],
["%syr", "in %syr"]
];
const enShortLocale: [string, string][] = [
["now", "now"],
["%ss ago", "in %ss"],
["1m ago", "in 1m"],
["%sm ago", "in %sm"],
["1h ago", "in 1h"],
["%sh ago", "in %sh"],
["1d ago", "in 1d"],
["%sd ago", "in %sd"],
["1w ago", "in 1w"],
["%sw ago", "in %sw"],
["1mo ago", "in 1mo"],
["%smo ago", "in %smo"],
["1yr ago", "in 1yr"],
["%syr ago", "in %syr"]
];
register("short", (_n, index) => shortLocale[index]);
register("en_short", (_n, index) => enShortLocale[index]);
export function getTimeAgo(datetime: TDate, locale = "short") {
return format(datetime, locale);
}
type TimeAgoOptions = {
locale?: "short" | "en_short";
live?: boolean;
interval?: number;
onUpdate?: (timeAgo: string) => void;
};
export function useTimeAgo(
datetime: TDate,
{ locale = "short", live = true, interval = 60000, onUpdate }: TimeAgoOptions
) {
const [timeAgo, setTimeAgo] = useState(getTimeAgo(datetime, locale));
useEffect(() => {
if (!live) return;
const reset = setInterval(() => {
const value = getTimeAgo(datetime, locale);
onUpdate?.(value);
setTimeAgo(value);
}, interval);
return () => {
clearInterval(reset);
};
}, [datetime, interval, locale, live, onUpdate]);
return timeAgo;
}

View File

@@ -0,0 +1,27 @@
/*
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/>.
*/
export * from "./database";
export * from "./utils/file";
export * from "./utils/number";
export * from "./utils/total-notes";
export * from "./utils/time";
export * from "./hooks/use-time-ago";

View File

@@ -17,6 +17,17 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
export function formatBytes(bytes: number, decimals = 2) {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
/*jshint node:true*/ /*jshint node:true*/
/** /**
@@ -69,7 +80,7 @@ function sanitize(input: string, replacement: string) {
export function sanitizeFilename( export function sanitizeFilename(
input: string, input: string,
options: { replacement: string } options?: { replacement: string }
) { ) {
const replacement = (options && options.replacement) || ""; const replacement = (options && options.replacement) || "";
return sanitize(input, replacement); return sanitize(input, replacement);

View File

@@ -0,0 +1,26 @@
/*
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/>.
*/
export function nth(number: number) {
return (
["st", "nd", "rd"][
(((((number < 0 ? -number : number) + 90) % 100) - 10) % 10) - 1
] || "th"
);
}

View File

@@ -0,0 +1,40 @@
/*
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 { formatDate } from "@notesnook/core/utils/date";
import { database } from "../database";
import { formatReminderTime } from "@notesnook/core/collections/reminders";
export function getFormattedDate(
date: string | number | Date,
type: "time" | "date-time" | "date" = "date-time"
) {
return formatDate(date, {
dateFormat: database.settings?.getDateFormat() as string,
timeFormat: database.settings?.getTimeFormat() as string,
type: type
});
}
export function getFormattedReminderTime(reminder: any, short = false) {
return formatReminderTime(reminder, short, {
dateFormat: database.settings?.getDateFormat() as string,
timeFormat: database.settings?.getTimeFormat() as string
});
}

View File

@@ -0,0 +1,30 @@
/*
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 { database } from "../database";
export function getTotalNotes(item?: any) {
if (!item || (item.type !== "notebook" && item.type !== "topic")) return 0;
if (item.type === "topic") {
return (
database.notebooks?.notebook(item.notebookId)?.topics.topic(item.id)
?.totalNotes || 0
);
}
return database.notebooks?.notebook(item.id)?.totalNotes || 0;
}

View File

@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "./dist",
"maxNodeModuleJsDepth": 10,
"downlevelIteration": true,
"allowJs": true,
"jsx": "react-jsx"
},
"include": ["src/"]
}