mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
mobile: refactor
This commit is contained in:
committed by
Abdullah Atta
parent
c881e74aae
commit
fb616a246e
@@ -16,8 +16,7 @@ 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 "@notesnook/core/api/index";
|
||||
import { database } from "@notesnook/common";
|
||||
import { initalize, logger as dbLogger } from "@notesnook/core/logger";
|
||||
import { Platform } from "react-native";
|
||||
import { MMKVLoader } from "react-native-mmkv-storage";
|
||||
@@ -30,13 +29,8 @@ import * as Gzip from "react-native-gzip";
|
||||
const LoggerStorage = new MMKVLoader()
|
||||
.withInstanceID("notesnook_logs")
|
||||
.initialize();
|
||||
initalize(new KV(LoggerStorage), true);
|
||||
export const DatabaseLogger = dbLogger;
|
||||
|
||||
/**
|
||||
* @type {import("@notesnook/core/api/index").default}
|
||||
*/
|
||||
export var db = new Database(
|
||||
database.setup(
|
||||
Storage,
|
||||
Platform.OS === "ios" ? EventSource : AndroidEventSource,
|
||||
filesystem,
|
||||
@@ -46,7 +40,7 @@ export var db = new Database(
|
||||
}
|
||||
);
|
||||
|
||||
db.host(
|
||||
database.host(
|
||||
__DEV__
|
||||
? {
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
@@ -68,3 +62,8 @@ db.host(
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
}
|
||||
);
|
||||
|
||||
initalize(new KV(LoggerStorage), true);
|
||||
|
||||
export const db = database;
|
||||
export const DatabaseLogger = dbLogger;
|
||||
|
||||
0
apps/mobile/app/common/filesystem/attach-file.ts
Normal file
0
apps/mobile/app/common/filesystem/attach-file.ts
Normal file
@@ -34,7 +34,6 @@ import {
|
||||
import PremiumService from "../../services/premium";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { formatBytes } from "../../utils";
|
||||
import { eCloseAttachmentDialog, eCloseSheet } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
@@ -47,6 +46,7 @@ import { Notice } from "../ui/notice";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { formatBytes } from "@notesnook/common";
|
||||
|
||||
const Actions = ({ attachment, setAttachments, fwdRef }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
|
||||
@@ -23,7 +23,7 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { formatBytes } from "../../utils";
|
||||
import { formatBytes } from "@notesnook/common";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { Keyboard, Platform, View } from "react-native";
|
||||
import Animated, {
|
||||
Easing,
|
||||
@@ -30,10 +30,10 @@ import { notesnook } from "../../../e2e/test.ids";
|
||||
import { editorState } from "../../screens/editor/tiptap/utils";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { getElevation, showTooltip, TOOLTIP_POSITIONS } from "../../utils";
|
||||
import { normalize, SIZE } from "../../utils/size";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import NativeTooltip from "../../utils/tooltip";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import { useCallback } from "react";
|
||||
|
||||
export const FloatingButton = ({
|
||||
title,
|
||||
@@ -116,11 +116,11 @@ export const FloatingButton = ({
|
||||
accentColor={color || "accent"}
|
||||
accentText="light"
|
||||
customStyle={{
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
borderRadius: 100
|
||||
}}
|
||||
onLongPress={(event) => {
|
||||
showTooltip(event, title, TOOLTIP_POSITIONS.LEFT);
|
||||
NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
|
||||
}}
|
||||
onPress={onPress}
|
||||
>
|
||||
|
||||
@@ -21,7 +21,7 @@ import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
|
||||
const DialogContainer = ({ width, height, ...restProps }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -30,7 +30,7 @@ const DialogContainer = ({ width, height, ...restProps }) => {
|
||||
<View
|
||||
{...restProps}
|
||||
style={{
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
width: width || DDS.isTab ? 500 : "85%",
|
||||
maxHeight: height || 450,
|
||||
borderRadius: 10,
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { eCloseSimpleDialog, eOpenSimpleDialog } from "../../utils/events";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Toast } from "../toast";
|
||||
@@ -111,7 +111,7 @@ export const Dialog = ({ context = "global" }) => {
|
||||
};
|
||||
|
||||
const style = {
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 400 : "85%",
|
||||
maxHeight: 450,
|
||||
borderRadius: 5,
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from "../../../services/event-manager";
|
||||
import { useMessageStore } from "../../../stores/use-message-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { getElevation } from "../../../utils";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import {
|
||||
eCloseJumpToDialog,
|
||||
eOpenJumpToDialog,
|
||||
@@ -124,7 +124,7 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => {
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 500 : "85%",
|
||||
backgroundColor: colors.bg,
|
||||
zIndex: 100,
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../../services/event-manager";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { getElevation } from "../../../utils";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { eCloseResultDialog, eOpenResultDialog } from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
@@ -68,7 +68,7 @@ const ResultDialog = () => {
|
||||
<BaseDialog visible={true} onRequestClose={close}>
|
||||
<View
|
||||
style={{
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 350 : "85%",
|
||||
maxHeight: 500,
|
||||
borderRadius: 10,
|
||||
|
||||
@@ -34,7 +34,8 @@ import {
|
||||
} from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import SearchService from "../../../services/search";
|
||||
import { getElevation, toTXT } from "../../../utils";
|
||||
import { convertNoteToText } from "../../../utils/note-to-text";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import {
|
||||
eClearEditor,
|
||||
eCloseActionSheet,
|
||||
@@ -564,7 +565,7 @@ export class VaultDialog extends Component {
|
||||
}
|
||||
|
||||
async _copyNote(note) {
|
||||
Clipboard.setString(await toTXT(note));
|
||||
Clipboard.setString(await convertNoteToText(note));
|
||||
ToastEvent.show({
|
||||
heading: "Note copied",
|
||||
type: "success",
|
||||
@@ -580,7 +581,7 @@ export class VaultDialog extends Component {
|
||||
await Share.open({
|
||||
heading: "Share note",
|
||||
failOnCancel: false,
|
||||
message: await toTXT(note)
|
||||
message: await convertNoteToText(note)
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -678,7 +679,7 @@ export class VaultDialog extends Component {
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 350 : "85%",
|
||||
borderRadius: 10,
|
||||
backgroundColor: colors.bg,
|
||||
|
||||
@@ -92,7 +92,7 @@ export const RightMenus = () => {
|
||||
onRequestClose={() => {
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
anchor={
|
||||
button={
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
menuRef.current?.show();
|
||||
|
||||
@@ -29,7 +29,7 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
@@ -198,7 +198,7 @@ const Intro = ({ navigation }) => {
|
||||
style={{
|
||||
paddingHorizontal: 24,
|
||||
alignSelf: "center",
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
borderRadius: 100
|
||||
}}
|
||||
fontSize={SIZE.md}
|
||||
|
||||
@@ -24,7 +24,7 @@ import { eSendEvent } from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import {
|
||||
eCloseLoading,
|
||||
eOpenLoading,
|
||||
@@ -91,7 +91,7 @@ export const WelcomeNotice = () => {
|
||||
paddingHorizontal: 24,
|
||||
alignSelf: "center",
|
||||
borderRadius: 100,
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
marginTop: 30
|
||||
}}
|
||||
type="accent"
|
||||
|
||||
@@ -37,9 +37,9 @@ import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { AndroidModule } from "../../utils";
|
||||
import { eOpenAnnouncementDialog } from "../../utils/events";
|
||||
import { getGithubVersion } from "../../utils/github-version";
|
||||
import { NotesnookModule } from "../../utils/notesnook-module";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import Migrate from "../sheets/migrate";
|
||||
@@ -175,7 +175,7 @@ const Launcher = React.memo(
|
||||
const onUnlockBiometrics = useCallback(async () => {
|
||||
if (!(await BiometricService.isBiometryAvailable())) return;
|
||||
if (Platform.OS === "android") {
|
||||
const activityName = await AndroidModule.getActivityName();
|
||||
const activityName = await NotesnookModule.getActivityName();
|
||||
if (activityName !== "MainActivity") return;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { View } from "react-native";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import { ToastEvent } from "../../../services/event-manager";
|
||||
import { getTotalNotes } from "../../../utils";
|
||||
import { getTotalNotes } from "@notesnook/common";
|
||||
import { db } from "../../../common/database";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
} from "../../../services/event-manager";
|
||||
import { useEditorStore } from "../../../stores/use-editor-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { history } from "../../../utils";
|
||||
import { eOnLoadNote, eShowMergeDialog } from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
@@ -59,12 +58,14 @@ export const openNote = async (item, isTrash, setSelectedItem, isSheet) => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const { selectedItemsList, selectionMode, clearSelection } =
|
||||
useSelectionStore.getState();
|
||||
|
||||
if (history.selectedItemsList.length > 0 && history.selectionMode) {
|
||||
if (selectedItemsList.length > 0 && selectionMode) {
|
||||
setSelectedItem && setSelectedItem(_note);
|
||||
return;
|
||||
} else {
|
||||
history.selectedItemsList = [];
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
if (_note.conflicted) {
|
||||
|
||||
@@ -22,23 +22,23 @@ import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { TopicNotes } from "../../../screens/notes/topic-notes";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { history } from "../../../utils";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Properties } from "../../properties";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { getFormattedDate } from "../../../utils/time";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
|
||||
const showActionSheet = (item) => {
|
||||
Properties.present(item);
|
||||
};
|
||||
|
||||
const navigateToTopic = (topic) => {
|
||||
if (history.selectedItemsList.length > 0) return;
|
||||
if (useSelectionStore.getState().selectedItemsList.length > 0) return;
|
||||
TopicNotes.navigate(topic, true);
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import { ToastEvent } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useTrashStore } from "../../../stores/use-trash-store";
|
||||
import { history } from "../../../utils";
|
||||
import { db } from "../../../common/database";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import SelectionWrapper from "../selection-wrapper";
|
||||
@@ -49,11 +48,13 @@ const navigateToNotebook = (item, canGoBack) => {
|
||||
|
||||
export const openNotebookTopic = (item) => {
|
||||
const isTrash = item.type === "trash";
|
||||
if (history.selectedItemsList.length > 0 && history.selectionMode) {
|
||||
useSelectionStore.getState().setSelectedItem(item);
|
||||
const { selectedItemsList, setSelectedItem, selectionMode, clearSelection } =
|
||||
useSelectionStore.getState();
|
||||
if (selectedItemsList.length > 0 && selectionMode) {
|
||||
setSelectedItem(item);
|
||||
return;
|
||||
} else {
|
||||
history.selectedItemsList = [];
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
if (isTrash) {
|
||||
|
||||
@@ -36,7 +36,7 @@ import { NoteWrapper } from "../list-items/note/wrapper";
|
||||
import { NotebookWrapper } from "../list-items/notebook/wrapper";
|
||||
import TagItem from "../list-items/tag";
|
||||
import { Empty } from "./empty";
|
||||
import { getTotalNotes } from "../../utils";
|
||||
import { getTotalNotes } from "@notesnook/common";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import ReminderItem from "../list-items/reminder";
|
||||
|
||||
|
||||
@@ -34,10 +34,9 @@ import {
|
||||
import Navigation from "../../services/navigation";
|
||||
import Sync from "../../services/sync";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { dHeight } from "../../utils";
|
||||
import { eOnLoadNote, eShowMergeDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { getFormattedDate, sleep } from "../../utils/time";
|
||||
import { sleep } from "../../utils/time";
|
||||
import BaseDialog from "../dialog/base-dialog";
|
||||
import DialogButtons from "../dialog/dialog-buttons";
|
||||
import DialogContainer from "../dialog/dialog-container";
|
||||
@@ -46,6 +45,8 @@ import { Button } from "../ui/button";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import Seperator from "../ui/seperator";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
|
||||
const MergeConflicts = () => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -57,6 +58,7 @@ const MergeConflicts = () => {
|
||||
const content = useRef(null);
|
||||
const isKeepingConflicted = !keep?.conflicted;
|
||||
const isKeeping = !!keep;
|
||||
const { height } = useSettingStore((state) => state.dimensions);
|
||||
|
||||
const applyChanges = async () => {
|
||||
let _content = keep;
|
||||
@@ -296,7 +298,7 @@ const MergeConflicts = () => {
|
||||
|
||||
<Animated.View
|
||||
style={{
|
||||
height: dHeight / 2 - (50 + insets.top / 2),
|
||||
height: height / 2 - (50 + insets.top / 2),
|
||||
backgroundColor: colors.bg,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.nav
|
||||
@@ -332,7 +334,7 @@ const MergeConflicts = () => {
|
||||
|
||||
<Animated.View
|
||||
style={{
|
||||
height: dHeight / 2 - (50 + insets.top / 2),
|
||||
height: height / 2 - (50 + insets.top / 2),
|
||||
backgroundColor: colors.bg,
|
||||
borderRadius: 10
|
||||
}}
|
||||
|
||||
@@ -26,13 +26,13 @@ import { presentSheet } from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { getFormattedDate, timeSince } from "../../utils/time";
|
||||
import DialogHeader from "../dialog/dialog-header";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import Seperator from "../ui/seperator";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import NotePreview from "./preview";
|
||||
import { getFormattedDate, getTimeAgo } from "@notesnook/common";
|
||||
|
||||
export default function NoteHistory({ note, fwdRef }) {
|
||||
const [history, setHistory] = useState([]);
|
||||
@@ -90,7 +90,7 @@ export default function NoteHistory({ note, fwdRef }) {
|
||||
>
|
||||
<Paragraph>{getDate(item.dateCreated, item.dateModified)}</Paragraph>
|
||||
<Paragraph color={colors.icon} size={SIZE.xs}>
|
||||
{timeSince(item.dateModified)}
|
||||
{getTimeAgo(item.dateModified)}
|
||||
</Paragraph>
|
||||
</PressableButton>
|
||||
),
|
||||
|
||||
@@ -26,7 +26,7 @@ import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent, presentSheet } from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import {
|
||||
eClosePremiumDialog,
|
||||
eCloseSheet,
|
||||
@@ -271,7 +271,7 @@ export const Component = ({ close, promo }) => {
|
||||
position: "absolute",
|
||||
borderRadius: 100,
|
||||
bottom: 30,
|
||||
...getElevation(10)
|
||||
...getElevationStyle(10)
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import {
|
||||
eCloseActionSheet,
|
||||
eCloseSheet,
|
||||
@@ -94,7 +94,7 @@ export const PremiumToast = ({ context = "global", offset = 0 }) => {
|
||||
position: "absolute",
|
||||
backgroundColor: colors.nav,
|
||||
zIndex: 999,
|
||||
...getElevation(20),
|
||||
...getElevationStyle(20),
|
||||
padding: 12,
|
||||
borderRadius: 10,
|
||||
flexDirection: "row",
|
||||
|
||||
@@ -21,8 +21,8 @@ import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { getFormattedDate } from "../../utils/time";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
export const DateMeta = ({ item }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
|
||||
|
||||
@@ -41,11 +41,7 @@ enum EmailChangeSteps {
|
||||
changeEmail
|
||||
}
|
||||
|
||||
export const ChangeEmail = ({
|
||||
actionSheetRef,
|
||||
close,
|
||||
update
|
||||
}: ChangeEmailProps) => {
|
||||
export const ChangeEmail = ({ close }: ChangeEmailProps) => {
|
||||
const [step, setStep] = useState(EmailChangeSteps.verify);
|
||||
const emailChangeData = useRef<{
|
||||
email?: string;
|
||||
|
||||
@@ -29,7 +29,7 @@ import Exporter from "../../../services/exporter";
|
||||
import PremiumService from "../../../services/premium";
|
||||
import { useThemeStore } from "../../../stores/use-theme-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 { sleep } from "../../../utils/time";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
@@ -340,7 +340,7 @@ ExportNotesSheet.present = (notes, allNotes) => {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
borderRadius: 5,
|
||||
paddingVertical: pv
|
||||
},
|
||||
|
||||
@@ -34,7 +34,6 @@ import SettingsService from "../../../services/settings";
|
||||
import { db } from "../../../common/database";
|
||||
import Storage from "../../../common/database/storage";
|
||||
import { eOpenRecoveryKeyDialog } from "../../../utils/events";
|
||||
import { sanitizeFilename } from "../../../utils/sanitizer";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
@@ -44,6 +43,7 @@ import SheetWrapper from "../../ui/sheet";
|
||||
import { QRCode } from "../../ui/svg/lazy";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import { sanitizeFilename } from "@notesnook/common";
|
||||
|
||||
class RecoveryKeySheet extends React.Component {
|
||||
constructor(props) {
|
||||
|
||||
@@ -36,7 +36,6 @@ import { initialize } from "../../../stores";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { eCloseRestoreDialog, eOpenRestoreDialog } from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { getFormattedDate } from "../../../utils/time";
|
||||
import { Dialog } from "../../dialog";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
@@ -45,6 +44,7 @@ import { Button } from "../../ui/button";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import SheetWrapper from "../../ui/sheet";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
|
||||
const RestoreDataSheet = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
@@ -60,11 +60,12 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
import { MMKV } from "../../../common/database/mmkv";
|
||||
import { openEditor } from "../../../screens/notes/common";
|
||||
import { getTotalNotes, history } from "../../../utils";
|
||||
import { getTotalNotes } from "@notesnook/common";
|
||||
import { deleteItems } from "../../../utils/functions";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import { Properties } from "../../properties";
|
||||
import Sort from "../sort";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
|
||||
type ConfigItem = { id: string; type: string };
|
||||
class TopicSheetConfig {
|
||||
@@ -333,7 +334,9 @@ export const TopicsSheet = () => {
|
||||
}}
|
||||
onPress={async () => {
|
||||
//@ts-ignore
|
||||
history.selectedItemsList = selection;
|
||||
useSelectionStore.setState({
|
||||
selectedItemsList: selection
|
||||
});
|
||||
presentDialog({
|
||||
title: `Delete ${
|
||||
selection.length > 1 ? "topics" : "topics"
|
||||
@@ -345,7 +348,7 @@ export const TopicsSheet = () => {
|
||||
negativeText: "Cancel",
|
||||
positivePress: async () => {
|
||||
await deleteItems();
|
||||
history.selectedItemsList = [];
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { eHideToast, eShowToast } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { Button } from "../ui/button";
|
||||
@@ -136,7 +136,7 @@ export const Toast = ({ context = "global" }) => {
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
maxWidth: "95%",
|
||||
backgroundColor: colors.nav,
|
||||
minWidth: data?.func ? "95%" : "50%",
|
||||
|
||||
@@ -27,9 +27,9 @@ import Animated, {
|
||||
} from "react-native-reanimated";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { ColorKey, useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { showTooltip, TOOLTIP_POSITIONS } from "../../../utils";
|
||||
import { BUTTON_TYPES } from "../../../utils/constants";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import NativeTooltip from "../../../utils/tooltip";
|
||||
import { ButtonProps } from "../button";
|
||||
import { PressableButton } from "../pressable";
|
||||
import Heading from "../typography/heading";
|
||||
@@ -86,7 +86,7 @@ export const AnimatedButton = ({
|
||||
return;
|
||||
}
|
||||
if (tooltipText) {
|
||||
showTooltip(event, tooltipText, TOOLTIP_POSITIONS.TOP);
|
||||
NativeTooltip.show(event, tooltipText, NativeTooltip.POSITIONS.TOP);
|
||||
}
|
||||
}}
|
||||
disabled={loading}
|
||||
|
||||
@@ -22,15 +22,15 @@ import {
|
||||
ActivityIndicator,
|
||||
ColorValue,
|
||||
TextStyle,
|
||||
ViewStyle,
|
||||
View
|
||||
View,
|
||||
ViewStyle
|
||||
} from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { ColorKey, useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import { showTooltip, TOOLTIP_POSITIONS } from "../../../utils";
|
||||
import { BUTTON_TYPES } from "../../../utils/constants";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import NativeTooltip from "../../../utils/tooltip";
|
||||
import { ProTag } from "../../premium/pro-tag";
|
||||
import { PressableButton, PressableButtonProps } from "../pressable";
|
||||
import Heading from "../typography/heading";
|
||||
@@ -105,7 +105,7 @@ export const Button = ({
|
||||
return;
|
||||
}
|
||||
if (tooltipText) {
|
||||
showTooltip(event, tooltipText, TOOLTIP_POSITIONS.TOP);
|
||||
NativeTooltip.show(event, tooltipText, NativeTooltip.POSITIONS.TOP);
|
||||
}
|
||||
}}
|
||||
disabled={loading}
|
||||
|
||||
@@ -22,10 +22,10 @@ import { ColorValue, GestureResponderEvent, ViewStyle } from "react-native";
|
||||
import Animated, { Layout } from "react-native-reanimated";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
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 { SIZE } from "../../../utils/size";
|
||||
import { PressableButton, PressableButtonProps } from "../pressable";
|
||||
import NativeTooltip from "../../../utils/tooltip";
|
||||
interface IconButtonProps extends PressableButtonProps {
|
||||
name: string;
|
||||
color?: ColorValue;
|
||||
@@ -56,7 +56,7 @@ export const IconButton = ({
|
||||
tooltipText,
|
||||
type = "gray",
|
||||
fwdRef,
|
||||
tooltipPosition = TOOLTIP_POSITIONS.TOP,
|
||||
tooltipPosition = NativeTooltip.POSITIONS.TOP,
|
||||
...restProps
|
||||
}: IconButtonProps) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -67,7 +67,7 @@ export const IconButton = ({
|
||||
return;
|
||||
}
|
||||
if (tooltipText) {
|
||||
showTooltip(event, tooltipText, tooltipPosition);
|
||||
NativeTooltip.show(event, tooltipText, tooltipPosition);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
validateUsername
|
||||
} from "../../../services/validation";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { getElevation } from "../../../utils";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { IconButton } from "../icon-button";
|
||||
import Paragraph from "../typography/paragraph";
|
||||
@@ -344,7 +344,7 @@ const Input = ({
|
||||
paddingVertical: 3,
|
||||
paddingHorizontal: 5,
|
||||
borderRadius: 2.5,
|
||||
...getElevation(2),
|
||||
...getElevationStyle(2),
|
||||
top: 0
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -23,8 +23,8 @@ import { ViewStyle } from "react-native";
|
||||
import { Reminder } from "../../../services/notifications";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { getFormattedReminderTime } from "../../../utils/time";
|
||||
import { Button, ButtonProps } from "../button";
|
||||
import { getFormattedReminderTime } from "@notesnook/common";
|
||||
|
||||
export const ReminderTime = ({
|
||||
checkIsActive = true,
|
||||
|
||||
@@ -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/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useTimeAgo } from "@notesnook/common";
|
||||
import React from "react";
|
||||
import { TextProps } from "react-native";
|
||||
import { timeSince } from "../../../utils/time";
|
||||
import Heading from "../typography/heading";
|
||||
import Paragraph from "../typography/paragraph";
|
||||
interface TimeSinceProps extends TextProps {
|
||||
@@ -34,20 +34,10 @@ export const TimeSince = ({
|
||||
updateFrequency = 30000,
|
||||
bold
|
||||
}: TimeSinceProps) => {
|
||||
const [timeAgo, setTimeAgo] = useState<string | null>(null);
|
||||
const interval = useRef<NodeJS.Timer>();
|
||||
|
||||
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]);
|
||||
const timeAgo = useTimeAgo(time, {
|
||||
interval: updateFrequency,
|
||||
locale: "short"
|
||||
});
|
||||
|
||||
return bold ? (
|
||||
<Heading style={style}>{timeAgo}</Heading>
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
} from "../../assets/images/assets";
|
||||
import { ThemeStore, useThemeStore } from "../../stores/use-theme-store";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { eOpenAddNotebookDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import useRotator from "../../hooks/use-rotator";
|
||||
@@ -89,7 +89,7 @@ const NotebookWelcome = () => {
|
||||
padding: 12,
|
||||
width: "100%",
|
||||
backgroundColor: colors.bg,
|
||||
...getElevation(3),
|
||||
...getElevationStyle(3),
|
||||
borderRadius: 10,
|
||||
marginVertical: 12
|
||||
}}
|
||||
@@ -142,7 +142,7 @@ const notebooks: { id: string; steps: TStep[] } = {
|
||||
padding: 12,
|
||||
width: "100%",
|
||||
backgroundColor: colors.bg,
|
||||
...getElevation(3),
|
||||
...getElevationStyle(3),
|
||||
borderRadius: 10,
|
||||
marginVertical: 12
|
||||
}}
|
||||
|
||||
@@ -50,7 +50,7 @@ import { useSelectionStore } from "../stores/use-selection-store";
|
||||
import { useTagStore } from "../stores/use-tag-store";
|
||||
import { useThemeStore } from "../stores/use-theme-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 {
|
||||
eOnTopicSheetUpdate,
|
||||
@@ -182,7 +182,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
let text = await toTXT(item, false);
|
||||
let text = await convertNoteToText(item, false);
|
||||
let html = text.replace(/\n/g, "<br />");
|
||||
Notifications.displayNotification({
|
||||
title: item.title,
|
||||
@@ -227,7 +227,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
description: "Unlock note to copy to clipboard."
|
||||
});
|
||||
} else {
|
||||
Clipboard.setString(await toTXT(item));
|
||||
Clipboard.setString(await convertNoteToText(item));
|
||||
ToastEvent.show({
|
||||
heading: "Note copied to clipboard",
|
||||
type: "success",
|
||||
@@ -415,7 +415,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
Share.open({
|
||||
title: "Share note to",
|
||||
failOnCancel: false,
|
||||
message: await toTXT(item)
|
||||
message: await convertNoteToText(item)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ import { useNoteStore } from "../stores/use-notes-store";
|
||||
import { useSelectionStore } from "../stores/use-selection-store";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { useThemeStore } from "../stores/use-theme-store";
|
||||
import { history } from "../utils";
|
||||
import { rootNavigatorRef } from "../utils/global-refs";
|
||||
import Auth from "../components/auth";
|
||||
const NativeStack = createNativeStackNavigator();
|
||||
@@ -191,7 +190,7 @@ const _NavigationStack = () => {
|
||||
const clearSelection = useSelectionStore((state) => state.clearSelection);
|
||||
|
||||
const onStateChange = React.useCallback(() => {
|
||||
if (history.selectionMode) {
|
||||
if (useSelectionStore.getState().selectionMode) {
|
||||
clearSelection(true);
|
||||
}
|
||||
hideAllTooltips();
|
||||
|
||||
@@ -59,7 +59,6 @@ import {
|
||||
import { useEditorStore } from "../stores/use-editor-store";
|
||||
import { useSettingStore } from "../stores/use-setting-store";
|
||||
import { useThemeStore } from "../stores/use-theme-store";
|
||||
import { setWidthHeight } from "../utils";
|
||||
import {
|
||||
eClearEditor,
|
||||
eCloseFullscreenEditor,
|
||||
@@ -221,7 +220,6 @@ const _TabsHolder = () => {
|
||||
width: size.width,
|
||||
height: size.height
|
||||
});
|
||||
setWidthHeight(size);
|
||||
DDS.setSize(size, orientation);
|
||||
const nextDeviceMode = DDS.isLargeTablet()
|
||||
? "tablet"
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { useEditorStore } from "../../stores/use-editor-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { NoteType } from "../../utils/types";
|
||||
import { EDITOR_URI } from "./source";
|
||||
@@ -256,7 +256,7 @@ const ReadonlyButton = ({ editor }: { editor: useEditorType }) => {
|
||||
width: 60,
|
||||
height: 60,
|
||||
right: 12,
|
||||
...getElevation(5)
|
||||
...getElevationStyle(5)
|
||||
}}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
@@ -38,7 +38,6 @@ import { useTagStore } from "../../../stores/use-tag-store";
|
||||
import { ThemeStore, useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { eClearEditor, eOnLoadNote } from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { getFormattedDate } from "../../../utils/time";
|
||||
import { NoteType } from "../../../utils/types";
|
||||
import { onNoteCreated } from "../../notes/common";
|
||||
import Commands from "./commands";
|
||||
@@ -53,6 +52,7 @@ import {
|
||||
makeSessionId,
|
||||
post
|
||||
} from "./utils";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
|
||||
export const useEditor = (
|
||||
editorId = "",
|
||||
@@ -139,7 +139,7 @@ export const useEditor = (
|
||||
|
||||
const reset = useCallback(
|
||||
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;
|
||||
loadedImages.current = {};
|
||||
currentContent.current = null;
|
||||
|
||||
@@ -49,9 +49,9 @@ import {
|
||||
import { ThemeStore, useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { eCloseSheet } from "../../utils/events";
|
||||
import { sanitizeFilename } from "../../utils/sanitizer";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { sanitizeFilename } from "@notesnook/common";
|
||||
const mfaMethods: MFAMethod[] = [
|
||||
{
|
||||
id: "app",
|
||||
|
||||
@@ -39,7 +39,7 @@ import SettingsService from "../../services/settings";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { getElevation } from "../../utils";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { eOpenLoginDialog } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
const AppLock = ({ route }) => {
|
||||
@@ -274,7 +274,7 @@ const AppLock = ({ route }) => {
|
||||
style={{
|
||||
paddingHorizontal: 24,
|
||||
alignSelf: "center",
|
||||
...getElevation(5),
|
||||
...getElevationStyle(5),
|
||||
marginTop: 30,
|
||||
borderRadius: 100
|
||||
}}
|
||||
|
||||
@@ -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/>.
|
||||
*/
|
||||
|
||||
import React, { useRef, useState } from "react";
|
||||
import React 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 { DDS } from "../../services/device-detection";
|
||||
import { ToastEvent } from "../../services/event-manager";
|
||||
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 {
|
||||
hexToRGBA,
|
||||
RGB_Linear_Shade,
|
||||
hexToRGBA,
|
||||
switchAccentColor
|
||||
} from "../../utils/color-scheme/utils";
|
||||
import { MenuItemsList } from "../../utils/constants";
|
||||
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 = () => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -18,29 +18,34 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { ReactElement } from "react";
|
||||
import { AccentColorPicker, HomagePageSelector } from "./appearance";
|
||||
import { AutomaticBackupsSelector } from "./backup-restore";
|
||||
import { AccentColorPicker } from "./appearance";
|
||||
import DebugLogs from "./debug";
|
||||
import { ConfigureToolbar } from "./editor/configure-toolbar";
|
||||
import { Licenses } from "./licenses";
|
||||
import SoundPicker from "./sound-picker";
|
||||
import { Subscription } from "./subscription";
|
||||
import { TrashIntervalSelector } from "./trash-interval-selector";
|
||||
import { FontSelector } from "./font-selector";
|
||||
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 } = {
|
||||
colorpicker: <AccentColorPicker />,
|
||||
homeselector: <HomagePageSelector />,
|
||||
autobackups: <AutomaticBackupsSelector />,
|
||||
homeselector: <HomePicker />,
|
||||
autobackups: <BackupReminderPicker />,
|
||||
subscription: <Subscription />,
|
||||
configuretoolbar: <ConfigureToolbar />,
|
||||
"debug-logs": <DebugLogs />,
|
||||
"sound-picker": <SoundPicker />,
|
||||
licenses: <Licenses />,
|
||||
"trash-interval-selector": <TrashIntervalSelector />,
|
||||
"font-selector": <FontSelector />,
|
||||
"trash-interval-selector": <TrashIntervalPicker />,
|
||||
"font-selector": <FontPicker />,
|
||||
"title-format": <TitleFormat />,
|
||||
"date-format-selector": <DateFormatSelector />,
|
||||
"time-format-selector": <TimeFormatSelector />
|
||||
"date-format-selector": <DateFormatPicker />,
|
||||
"time-format-selector": <TimeFormatPicker />
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -33,7 +33,8 @@ import useTimer from "../../hooks/use-timer";
|
||||
import { ToastEvent } from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { hexToRGBA } from "../../utils/color-scheme/utils";
|
||||
import { sanitizeFilename } from "../../utils/sanitizer";
|
||||
import { sanitizeFilename } from "@notesnook/common";
|
||||
|
||||
// function getLevelString(level: number) {
|
||||
// switch (level) {
|
||||
// case LogLevel.Debug:
|
||||
|
||||
@@ -25,7 +25,7 @@ import { presentDialog } from "../../../components/dialog/functions";
|
||||
import { IconButton } from "../../../components/ui/icon-button";
|
||||
import Paragraph from "../../../components/ui/typography/paragraph";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { getElevation } from "../../../utils";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { renderTool } from "./common";
|
||||
import { DraggableItem, useDragState } from "./state";
|
||||
@@ -174,7 +174,7 @@ export const Group = ({
|
||||
width: isDragged ? dimensions.current?.width : "100%",
|
||||
backgroundColor: colors.bg,
|
||||
borderRadius: 10,
|
||||
...getElevation(hover ? 5 : 0),
|
||||
...getElevationStyle(hover ? 5 : 0),
|
||||
marginTop: isSubgroup ? 0 : 10
|
||||
}
|
||||
]}
|
||||
|
||||
@@ -26,7 +26,7 @@ import { IconButton } from "../../../components/ui/icon-button";
|
||||
import { SvgView } from "../../../components/ui/svg";
|
||||
import Paragraph from "../../../components/ui/typography/paragraph";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { getElevation } from "../../../utils";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { renderGroup } from "./common";
|
||||
import { DraggableItem, useDragState } from "./state";
|
||||
@@ -186,7 +186,7 @@ export const Tool = ({
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingLeft: isSubgroup ? 30 : 12,
|
||||
...getElevation(hover ? 3 : 0)
|
||||
...getElevationStyle(hover ? 3 : 0)
|
||||
}}
|
||||
>
|
||||
<View
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -21,25 +21,51 @@ 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 PremiumService from "../../services/premium";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
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 PremiumService from "../../../services/premium";
|
||||
|
||||
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 [trashInterval, setTrashInterval] = useState(
|
||||
db.settings.getTrashCleanupInterval()
|
||||
);
|
||||
const menuRef = useRef();
|
||||
const menuRef = useRef<any>();
|
||||
const [width, setWidth] = useState(0);
|
||||
const [currentValue, setCurrentValue] = useState(getValue());
|
||||
|
||||
const onChange = (item) => {
|
||||
const onChange = async (item: T) => {
|
||||
if (premium && onCheckOptionIsPremium?.(item)) {
|
||||
await PremiumService.verify(async () => {
|
||||
menuRef.current?.hide();
|
||||
setTrashInterval(item);
|
||||
db.settings.setTrashCleanupInterval(item);
|
||||
await updateValue(item);
|
||||
setCurrentValue(item);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
menuRef.current?.hide();
|
||||
await updateValue(item);
|
||||
setCurrentValue(item);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -78,40 +104,42 @@ export const TrashIntervalSelector = () => {
|
||||
padding: 12
|
||||
}}
|
||||
>
|
||||
<Paragraph>
|
||||
{trashInterval === -1 ? "Never" : trashInterval + " days"}
|
||||
</Paragraph>
|
||||
<Paragraph>{formatValue(currentValue)}</Paragraph>
|
||||
<Icon color={colors.icon} name="menu-down" size={SIZE.md} />
|
||||
</PressableButton>
|
||||
}
|
||||
>
|
||||
{[-1, 7, 30, 365].map((item) => (
|
||||
{options.map((item) => (
|
||||
<MenuItem
|
||||
key={item.toString()}
|
||||
key={getItemKey(item)}
|
||||
onPress={async () => {
|
||||
if (item === -1) {
|
||||
await PremiumService.verify(() => {
|
||||
onChange(item);
|
||||
});
|
||||
return;
|
||||
}
|
||||
onChange(item);
|
||||
}}
|
||||
style={{
|
||||
backgroundColor:
|
||||
trashInterval === item ? colors.nav : "transparent",
|
||||
backgroundColor: compareValue(currentValue, item)
|
||||
? colors.nav
|
||||
: "transparent",
|
||||
width: "100%",
|
||||
maxWidth: width
|
||||
}}
|
||||
textStyle={{
|
||||
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>
|
||||
))}
|
||||
</Menu>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export function createSettingsPicker<T>(props: PickerOptions<T>) {
|
||||
const Selector = () => {
|
||||
return <SettingsPicker {...props} />;
|
||||
};
|
||||
return Selector;
|
||||
}
|
||||
130
apps/mobile/app/screens/settings/picker/pickers.jsx
Normal file
130
apps/mobile/app/screens/settings/picker/pickers.jsx
Normal 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"
|
||||
});
|
||||
@@ -35,14 +35,14 @@ import ExportNotesSheet from "../../components/sheets/export-notes";
|
||||
import { Issue } from "../../components/sheets/github/issue";
|
||||
import { Progress } from "../../components/sheets/progress";
|
||||
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 BiometicService from "../../services/biometrics";
|
||||
import {
|
||||
ToastEvent,
|
||||
eSendEvent,
|
||||
openVault,
|
||||
presentSheet,
|
||||
ToastEvent
|
||||
presentSheet
|
||||
} from "../../services/event-manager";
|
||||
import { setLoginMessage } from "../../services/message";
|
||||
import Navigation from "../../services/navigation";
|
||||
@@ -53,7 +53,6 @@ import Sync from "../../services/sync";
|
||||
import { clearAllStores } from "../../stores";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { AndroidModule } from "../../utils";
|
||||
import { getColorScheme, toggleDarkMode } from "../../utils/color-scheme/utils";
|
||||
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
import {
|
||||
@@ -63,6 +62,7 @@ import {
|
||||
eOpenRecoveryKeyDialog,
|
||||
eOpenRestoreDialog
|
||||
} from "../../utils/events";
|
||||
import { NotesnookModule } from "../../utils/notesnook-module";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { MFARecoveryCodes, MFASheet } from "./2fa";
|
||||
import AppLock from "./app-lock";
|
||||
@@ -762,7 +762,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
modifer: () => {
|
||||
const settings = SettingsService.get();
|
||||
Platform.OS === "android"
|
||||
? AndroidModule.setSecureMode(!settings.privacyScreen)
|
||||
? NotesnookModule.setSecureMode(!settings.privacyScreen)
|
||||
: enabled(true);
|
||||
|
||||
SettingsService.set({ privacyScreen: !settings.privacyScreen });
|
||||
|
||||
@@ -16,6 +16,34 @@ 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 { 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 { DatabaseLogger, db } from "../common/database";
|
||||
@@ -120,4 +148,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// start,
|
||||
// registerHeadlessTask
|
||||
// };
|
||||
export {};
|
||||
export default { doInBackground };
|
||||
|
||||
@@ -26,11 +26,11 @@ import { presentDialog } from "../components/dialog/functions";
|
||||
import { DatabaseLogger, db } from "../common/database";
|
||||
import storage from "../common/database/storage";
|
||||
import { eCloseSheet } from "../utils/events";
|
||||
import { sanitizeFilename } from "../utils/sanitizer";
|
||||
import { sleep } from "../utils/time";
|
||||
import { eSendEvent, presentSheet, ToastEvent } from "./event-manager";
|
||||
import SettingsService from "./settings";
|
||||
import PremiumService from "./premium";
|
||||
import { sanitizeFilename } from "@notesnook/common";
|
||||
|
||||
const MS_DAY = 86400000;
|
||||
const MS_WEEK = MS_DAY * 7;
|
||||
|
||||
@@ -25,9 +25,10 @@ import * as ScopedStorage from "react-native-scoped-storage";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import { DatabaseLogger, db } from "../common/database/index";
|
||||
import Storage from "../common/database/storage";
|
||||
import { toTXT } from "../utils";
|
||||
import { sanitizeFilename } from "../utils/sanitizer";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
|
||||
import { sleep } from "../utils/time";
|
||||
import { sanitizeFilename } from "@notesnook/common";
|
||||
|
||||
const MIMETypes = {
|
||||
txt: "text/plain",
|
||||
@@ -149,7 +150,7 @@ async function exportAs(type, note, bulk) {
|
||||
}
|
||||
break;
|
||||
case "txt":
|
||||
data = await toTXT(note);
|
||||
data = await convertNoteToText(note);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ import Orientation from "react-native-orientation";
|
||||
import { enabled } from "react-native-privacy-snapshot";
|
||||
import { MMKV } from "../common/database/mmkv";
|
||||
import { SettingStore, useSettingStore } from "../stores/use-setting-store";
|
||||
import { AndroidModule } from "../utils";
|
||||
import { getColorScheme } from "../utils/color-scheme/utils";
|
||||
import { NotesnookModule } from "../utils/notesnook-module";
|
||||
import { scale, updateSize } from "../utils/size";
|
||||
import { DDS } from "./device-detection";
|
||||
import { setAutobackOffMessage } from "./message";
|
||||
@@ -63,13 +63,13 @@ function init() {
|
||||
function setPrivacyScreen(settings: SettingStore["settings"]) {
|
||||
if (settings.privacyScreen || settings.appLockMode === "background") {
|
||||
if (Platform.OS === "android") {
|
||||
AndroidModule.setSecureMode(true);
|
||||
NotesnookModule.setSecureMode(true);
|
||||
} else {
|
||||
enabled(true);
|
||||
}
|
||||
} else {
|
||||
if (Platform.OS === "android") {
|
||||
AndroidModule.setSecureMode(false);
|
||||
NotesnookModule.setSecureMode(false);
|
||||
} else {
|
||||
enabled(false);
|
||||
}
|
||||
|
||||
@@ -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/>.
|
||||
*/
|
||||
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { EVENTS } from "@notesnook/core/common";
|
||||
import { initAfterSync } from "../stores/index";
|
||||
import { SyncStatus, useUserStore } from "../stores/use-user-store";
|
||||
import { doInBackground } from "../utils";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { db } from "../common/database";
|
||||
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 SettingsService from "./settings";
|
||||
import BackgroundSync from "./background-sync";
|
||||
|
||||
export const ignoredMessages = [
|
||||
"Sync already running",
|
||||
@@ -61,7 +61,7 @@ const run = async (
|
||||
let error = null;
|
||||
|
||||
try {
|
||||
let res = await doInBackground(async () => {
|
||||
let res = await BackgroundSync.doInBackground(async () => {
|
||||
try {
|
||||
await db.sync(full, forced);
|
||||
return true;
|
||||
|
||||
@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import create, { State } from "zustand";
|
||||
import { history } from "../utils";
|
||||
|
||||
type Item = {
|
||||
id: string;
|
||||
@@ -30,23 +29,16 @@ export interface SelectionStore extends State {
|
||||
setAll: (all: Array<unknown>) => void;
|
||||
setSelectionMode: (mode: boolean) => void;
|
||||
setSelectedItem: (item: Item) => void;
|
||||
clearSelection: (noanimation: boolean) => void;
|
||||
clearSelection: () => void;
|
||||
}
|
||||
|
||||
export const useSelectionStore = create<SelectionStore>((set, get) => ({
|
||||
selectedItemsList: [],
|
||||
selectionMode: false,
|
||||
setAll: (all) => {
|
||||
history.selectedItemsList = all as never[];
|
||||
set({ selectedItemsList: all });
|
||||
},
|
||||
setSelectionMode: (mode) => {
|
||||
if (!mode) {
|
||||
history.selectedItemsList = [];
|
||||
history.selectionMode = false;
|
||||
} else {
|
||||
history.selectionMode = true;
|
||||
}
|
||||
set({
|
||||
selectionMode: mode,
|
||||
selectedItemsList: mode ? get().selectedItemsList : []
|
||||
@@ -61,17 +53,13 @@ export const useSelectionStore = create<SelectionStore>((set, get) => ({
|
||||
selectedItems.push(item);
|
||||
}
|
||||
selectedItems = [...new Set(selectedItems)];
|
||||
history.selectedItemsList = selectedItems as never[];
|
||||
history.selectionMode =
|
||||
selectedItems.length > 0 ? get().selectionMode : false;
|
||||
|
||||
set({
|
||||
selectedItemsList: selectedItems,
|
||||
selectionMode: history.selectionMode
|
||||
selectionMode: get().selectionMode
|
||||
});
|
||||
},
|
||||
clearSelection: () => {
|
||||
history.selectedItemsList = [];
|
||||
history.selectionMode = false;
|
||||
set({ selectionMode: false, selectedItemsList: [] });
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -18,10 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Platform, StatusBar } from "react-native";
|
||||
import { AndroidModule } from "..";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { eThemeUpdated } from "../events";
|
||||
import { NotesnookModule } from "../notesnook-module";
|
||||
|
||||
export const ACCENT = {
|
||||
color: "#008837",
|
||||
@@ -137,7 +137,7 @@ export function setColorScheme(colors = COLOR_SCHEME, accent = ACCENT) {
|
||||
false
|
||||
);
|
||||
if (Platform.OS === "android") {
|
||||
AndroidModule.setBackgroundColor(COLOR_SCHEME.bg);
|
||||
NotesnookModule.setBackgroundColor(COLOR_SCHEME.bg);
|
||||
StatusBar.setBackgroundColor("transparent", false);
|
||||
StatusBar.setTranslucent(true, false);
|
||||
}
|
||||
|
||||
@@ -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 = [
|
||||
{
|
||||
name: "Notes",
|
||||
@@ -214,15 +204,3 @@ export const BUTTON_TYPES = {
|
||||
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"
|
||||
};
|
||||
|
||||
28
apps/mobile/app/utils/elevation.ts
Normal file
28
apps/mobile/app/utils/elevation.ts
Normal 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
|
||||
};
|
||||
}
|
||||
@@ -18,18 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 Navigation from "../services/navigation";
|
||||
import SearchService from "../services/search";
|
||||
import { useSelectionStore } from "../stores/use-selection-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 { 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) => {
|
||||
presentDialog({
|
||||
title: `Delete ${
|
||||
@@ -68,33 +67,25 @@ export const deleteItems = async (item, context) => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (item && item.id && history.selectedItemsList.length === 0) {
|
||||
history.selectedItemsList = [];
|
||||
history.selectedItemsList.push(item);
|
||||
if (
|
||||
item &&
|
||||
item.id &&
|
||||
useSelectionStore.getState().selectedItemsList.length === 0
|
||||
) {
|
||||
useSelectionStore.getState().setSelectedItem(item);
|
||||
}
|
||||
|
||||
let notes = history.selectedItemsList.filter((i) => i.type === "note");
|
||||
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"
|
||||
);
|
||||
const { selectedItemsList } = useSelectionStore.getState();
|
||||
|
||||
let routesForUpdate = [
|
||||
"TaggedNotes",
|
||||
"ColoredNotes",
|
||||
"TopicNotes",
|
||||
"Favorites",
|
||||
"Notes"
|
||||
];
|
||||
let notes = selectedItemsList.filter((i) => i.type === "note");
|
||||
let notebooks = selectedItemsList.filter((i) => i.type === "notebook");
|
||||
let topics = selectedItemsList.filter((i) => i.type === "topic");
|
||||
let reminders = selectedItemsList.filter((i) => i.type === "reminder");
|
||||
|
||||
if (reminders.length > 0) {
|
||||
for (let reminder of reminders) {
|
||||
await db.reminders.remove(reminder.id);
|
||||
}
|
||||
routesForUpdate.push("Reminders");
|
||||
useRelationStore.getState().update();
|
||||
}
|
||||
|
||||
@@ -111,12 +102,11 @@ export const deleteItems = async (item, context) => {
|
||||
}
|
||||
await db.notes.delete(note.id);
|
||||
}
|
||||
routesForUpdate.push("Trash");
|
||||
eSendEvent(eClearEditor);
|
||||
}
|
||||
|
||||
if (topics?.length > 0) {
|
||||
const result = await deleteConfirmDialog(topics, "topic", context);
|
||||
const result = await confirmDeleteAllNotes(topics, "topic", context);
|
||||
if (result.delete) {
|
||||
for (const topic of topics) {
|
||||
if (result.deleteNotes) {
|
||||
@@ -127,7 +117,6 @@ export const deleteItems = async (item, context) => {
|
||||
}
|
||||
await db.notebooks.notebook(topic.notebookId).topics.delete(topic.id);
|
||||
}
|
||||
routesForUpdate.push("Notebook", "Notebooks");
|
||||
useMenuStore.getState().setMenuPins();
|
||||
ToastEvent.show({
|
||||
heading: `${topics.length > 1 ? "Topics" : "Topic"} deleted`,
|
||||
@@ -137,7 +126,7 @@ export const deleteItems = async (item, context) => {
|
||||
}
|
||||
|
||||
if (notebooks?.length > 0) {
|
||||
const result = await deleteConfirmDialog(notebooks, "notebook", context);
|
||||
const result = await confirmDeleteAllNotes(notebooks, "notebook", context);
|
||||
|
||||
if (result.delete) {
|
||||
let ids = notebooks.map((i) => i.id);
|
||||
@@ -156,17 +145,17 @@ export const deleteItems = async (item, context) => {
|
||||
}
|
||||
}
|
||||
await db.notebooks.delete(...ids);
|
||||
routesForUpdate.push("Notebook", "Notebooks");
|
||||
useMenuStore.getState().setMenuPins();
|
||||
}
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate();
|
||||
|
||||
let msgPart = history.selectedItemsList.length === 1 ? " item" : " items";
|
||||
let message = history.selectedItemsList.length + msgPart + " moved to trash.";
|
||||
let message = `${selectedItemsList.length} ${
|
||||
selectedItemsList.length === 1 ? "item" : "items"
|
||||
} moved to trash.`;
|
||||
|
||||
let itemsCopy = [...history.selectedItemsList];
|
||||
let deletedItems = [...selectedItemsList];
|
||||
if (
|
||||
topics.length === 0 &&
|
||||
reminders.length === 0 &&
|
||||
@@ -178,13 +167,12 @@ export const deleteItems = async (item, context) => {
|
||||
func: async () => {
|
||||
let trash = db.trash.all;
|
||||
let ids = [];
|
||||
for (var i = 0; i < itemsCopy.length; i++) {
|
||||
let it = itemsCopy[i];
|
||||
for (var i = 0; i < deletedItems.length; i++) {
|
||||
let it = deletedItems[i];
|
||||
let trashItem = trash.find((item) => item.id === it.id);
|
||||
ids.push(trashItem.id);
|
||||
}
|
||||
await db.trash.restore(...ids);
|
||||
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
@@ -193,9 +181,8 @@ export const deleteItems = async (item, context) => {
|
||||
actionText: "Undo"
|
||||
});
|
||||
}
|
||||
history.selectedItemsList = [];
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().clearSelection(true);
|
||||
useSelectionStore.getState().clearSelection();
|
||||
useMenuStore.getState().setMenuPins();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
SearchService.updateAndSearch();
|
||||
@@ -204,8 +191,7 @@ export const deleteItems = async (item, context) => {
|
||||
|
||||
export const openLinkInBrowser = async (link) => {
|
||||
try {
|
||||
const url = link;
|
||||
Linking.openURL(url);
|
||||
Linking.openURL(link);
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
32
apps/mobile/app/utils/note-to-text.ts
Normal file
32
apps/mobile/app/utils/note-to-text.ts
Normal 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;
|
||||
}
|
||||
35
apps/mobile/app/utils/notesnook-module.ts
Normal file
35
apps/mobile/app/utils/notesnook-module.ts
Normal 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
|
||||
});
|
||||
@@ -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/>.
|
||||
*/
|
||||
|
||||
import { formatDate } from "@notesnook/core/utils/date";
|
||||
import { db } from "../../common/database";
|
||||
import { formatReminderTime } from "@notesnook/core/collections/reminders";
|
||||
|
||||
export const sleep = (duration: number) =>
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
53
apps/mobile/app/utils/tooltip.ts
Normal file
53
apps/mobile/app/utils/tooltip.ts
Normal 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;
|
||||
@@ -33,6 +33,7 @@
|
||||
"@notesnook/editor": "*",
|
||||
"@notesnook/editor-mobile": "*",
|
||||
"react-native-swiper-flatlist": "3.2.2",
|
||||
"@notesnook/logger": "*"
|
||||
"@notesnook/logger": "*",
|
||||
"@notesnook/common": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,10 +274,10 @@ index 69065f0..0000000
|
||||
-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
|
||||
new file mode 100644
|
||||
index 0000000..20469ee
|
||||
index 0000000..c3bc1c9
|
||||
--- /dev/null
|
||||
+++ b/node_modules/react-native-reanimated-material-menu/src/Menu.tsx
|
||||
@@ -0,0 +1,289 @@
|
||||
@@ -0,0 +1,303 @@
|
||||
+import React from 'react';
|
||||
+
|
||||
+import {
|
||||
@@ -288,6 +288,7 @@ index 0000000..20469ee
|
||||
+ LayoutChangeEvent,
|
||||
+ Modal,
|
||||
+ Platform,
|
||||
+ ScrollView,
|
||||
+ StatusBar,
|
||||
+ StyleSheet,
|
||||
+ TouchableWithoutFeedback,
|
||||
@@ -325,6 +326,7 @@ index 0000000..20469ee
|
||||
+
|
||||
+const EASING = Easing.bezier(0.4, 0, 0.2, 1);
|
||||
+const SCREEN_INDENT = 8;
|
||||
+const SCREEN_INDENT_VERTICAL = 80;
|
||||
+
|
||||
+class Menu extends React.Component<MenuProps, State> {
|
||||
+ _container: View | null = null;
|
||||
@@ -384,7 +386,7 @@ index 0000000..20469ee
|
||||
+ }
|
||||
+
|
||||
+ const { width, height } = e.nativeEvent.layout;
|
||||
+
|
||||
+ let timeout:any = 0;
|
||||
+ this.setState(
|
||||
+ {
|
||||
+ menuState: States.Animating,
|
||||
@@ -405,7 +407,17 @@ index 0000000..20469ee
|
||||
+ easing: EASING,
|
||||
+ 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
|
||||
+ let { left, top } = this.state;
|
||||
+ const transforms = [];
|
||||
+ const transforms:any[] = [];
|
||||
+
|
||||
+ if (
|
||||
+ (isRTL && left + buttonWidth - menuWidth > SCREEN_INDENT) ||
|
||||
@@ -472,7 +484,7 @@ index 0000000..20469ee
|
||||
+ ) {
|
||||
+ transforms.push({
|
||||
+ translateX: Animated.multiply(menuSizeAnimation.x, -1),
|
||||
+ });
|
||||
+ } as never);
|
||||
+
|
||||
+ left = Math.min(windowWidth - SCREEN_INDENT, left + buttonWidth);
|
||||
+ } else if (left < SCREEN_INDENT) {
|
||||
@@ -480,26 +492,26 @@ index 0000000..20469ee
|
||||
+ }
|
||||
+
|
||||
+ // 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({
|
||||
+ translateY: Animated.multiply(menuSizeAnimation.y, -1),
|
||||
+ });
|
||||
+ } as never);
|
||||
+
|
||||
+ top = windowHeight - SCREEN_INDENT;
|
||||
+ top = Math.min(windowHeight - SCREEN_INDENT, top + buttonHeight);
|
||||
+ } else if (top < SCREEN_INDENT) {
|
||||
+ top = SCREEN_INDENT;
|
||||
+ top = windowHeight - SCREEN_INDENT_VERTICAL;
|
||||
+ top = Math.min(windowHeight - SCREEN_INDENT_VERTICAL, top + buttonHeight);
|
||||
+ } else if (top < SCREEN_INDENT_VERTICAL) {
|
||||
+ top = SCREEN_INDENT_VERTICAL;
|
||||
+ }
|
||||
+
|
||||
+ const shadowMenuContainerStyle = {
|
||||
+ opacity: opacityAnimation,
|
||||
+ transform: transforms,
|
||||
+ maxHeight: 500,
|
||||
+ top,
|
||||
+
|
||||
+ // Switch left to right for rtl devices
|
||||
+ ...(isRTL ? { right: left } : { left }),
|
||||
+ };
|
||||
+
|
||||
+ const { menuState } = this.state;
|
||||
+ const animationStarted = menuState === States.Animating;
|
||||
+ const modalVisible = menuState === States.Shown || animationStarted;
|
||||
@@ -529,7 +541,9 @@ index 0000000..20469ee
|
||||
+ style={[styles.shadowMenuContainer, shadowMenuContainerStyle, style]}
|
||||
+ >
|
||||
+ <Animated.View style={[styles.menuContainer, animationStarted && menuSize]}>
|
||||
+ <ScrollView showsVerticalScrollIndicator={menuState !== States.Animating} >
|
||||
+ {children}
|
||||
+ </ScrollView>
|
||||
+ </Animated.View>
|
||||
+ </Animated.View>
|
||||
+ </View>
|
||||
@@ -568,3 +582,19 @@ index 0000000..20469ee
|
||||
+
|
||||
+export default Menu
|
||||
\ 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,
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../app/common/database";
|
||||
import { getElevation } from "../app/utils";
|
||||
import { getElevationStyle } from "../app/utils/elevation";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
|
||||
const ListItem = ({ item, mode, close }) => {
|
||||
@@ -262,7 +262,7 @@ export const Search = ({ close, getKeyboardHeight, quicknote, mode }) => {
|
||||
alignSelf: "center",
|
||||
overflow: "hidden",
|
||||
zIndex: 999,
|
||||
...getElevation(quicknote ? 1 : 5),
|
||||
...getElevationStyle(quicknote ? 1 : 5),
|
||||
...extra
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -45,7 +45,8 @@ import { db } from "../app/common/database";
|
||||
import { MMKV } from "../app/common/database/mmkv";
|
||||
import Storage from "../app/common/database/storage";
|
||||
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 { Editor } from "./editor";
|
||||
import { Search } from "./search";
|
||||
@@ -459,7 +460,7 @@ const ShareView = ({ quicknote = false }) => {
|
||||
backgroundColor: colors.bg,
|
||||
height: 50 + insets.top,
|
||||
paddingTop: insets.top,
|
||||
...getElevation(1),
|
||||
...getElevationStyle(1),
|
||||
marginTop: -insets.top,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"@notesnook/logger": "*",
|
||||
"@notesnook/streamable-fs": "*",
|
||||
"@notesnook/theme": "*",
|
||||
"@notesnook/common": "*",
|
||||
"@notesnook/web-clipper": "*",
|
||||
"@react-pdf-viewer/core": "^3.12.0",
|
||||
"@react-pdf-viewer/toolbar": "^3.12.0",
|
||||
|
||||
@@ -23,7 +23,7 @@ import { db } from "./db";
|
||||
async function download(hash: string) {
|
||||
const attachment = db.attachments?.attachment(hash);
|
||||
if (!attachment) return;
|
||||
const downloadResult = await db.fs.downloadFile(
|
||||
const downloadResult = await db.fs?.downloadFile(
|
||||
attachment.metadata.hash,
|
||||
attachment.metadata.hash,
|
||||
attachment.chunkSize,
|
||||
|
||||
@@ -299,7 +299,7 @@ const AttachmentMenuItems: MenuItem[] = [
|
||||
onClick: async ({ attachment, status }) => {
|
||||
const isDownloading = status?.type === "download";
|
||||
if (isDownloading) {
|
||||
await db.fs.cancel(attachment.metadata.hash, "download");
|
||||
await db.fs?.cancel(attachment.metadata.hash, "download");
|
||||
} else await saveAttachment(attachment.metadata.hash);
|
||||
}
|
||||
},
|
||||
@@ -311,7 +311,7 @@ const AttachmentMenuItems: MenuItem[] = [
|
||||
onClick: async ({ attachment, status }) => {
|
||||
const isDownloading = status?.type === "upload";
|
||||
if (isDownloading) {
|
||||
await db.fs.cancel(attachment.metadata.hash, "upload");
|
||||
await db.fs?.cancel(attachment.metadata.hash, "upload");
|
||||
} else
|
||||
await reuploadAttachment(
|
||||
attachment.metadata.type,
|
||||
|
||||
@@ -31,7 +31,7 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
|
||||
) {
|
||||
if (signal)
|
||||
signal.onabort = async () => {
|
||||
await db.fs.cancel(GROUP_ID, "download");
|
||||
await db.fs?.cancel(GROUP_ID, "download");
|
||||
};
|
||||
|
||||
let index = 0;
|
||||
@@ -46,7 +46,7 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
|
||||
onProgress && onProgress(index);
|
||||
const attachment = attachments[index++];
|
||||
|
||||
await db.fs.downloadFile(
|
||||
await db.fs?.downloadFile(
|
||||
GROUP_ID,
|
||||
attachment.metadata.hash,
|
||||
attachment.chunkSize,
|
||||
|
||||
@@ -361,7 +361,7 @@ function RecoveryKeyMethod(props: BaseRecoveryComponentProps<"method:key">) {
|
||||
|
||||
const user = await db.user?.getUser();
|
||||
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);
|
||||
navigate("backup");
|
||||
}}
|
||||
|
||||
32
packages/common/.gitignore
vendored
Normal file
32
packages/common/.gitignore
vendored
Normal 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
|
||||
3
packages/common/.npmignore
Normal file
3
packages/common/.npmignore
Normal file
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!dist/**/*
|
||||
!package.json
|
||||
0
packages/common/README.md
Normal file
0
packages/common/README.md
Normal file
169
packages/common/package-lock.json
generated
Normal file
169
packages/common/package-lock.json
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
43
packages/common/package.json
Normal file
43
packages/common/package.json
Normal 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": "*"
|
||||
}
|
||||
}
|
||||
24
packages/common/src/database.ts
Normal file
24
packages/common/src/database.ts
Normal 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();
|
||||
88
packages/common/src/hooks/use-time-ago.ts
Normal file
88
packages/common/src/hooks/use-time-ago.ts
Normal 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;
|
||||
}
|
||||
27
packages/common/src/index.ts
Normal file
27
packages/common/src/index.ts
Normal 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";
|
||||
@@ -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/>.
|
||||
*/
|
||||
|
||||
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*/
|
||||
|
||||
/**
|
||||
@@ -69,7 +80,7 @@ function sanitize(input: string, replacement: string) {
|
||||
|
||||
export function sanitizeFilename(
|
||||
input: string,
|
||||
options: { replacement: string }
|
||||
options?: { replacement: string }
|
||||
) {
|
||||
const replacement = (options && options.replacement) || "";
|
||||
return sanitize(input, replacement);
|
||||
26
packages/common/src/utils/number.ts
Normal file
26
packages/common/src/utils/number.ts
Normal 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"
|
||||
);
|
||||
}
|
||||
40
packages/common/src/utils/time.ts
Normal file
40
packages/common/src/utils/time.ts
Normal 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
|
||||
});
|
||||
}
|
||||
30
packages/common/src/utils/total-notes.ts
Normal file
30
packages/common/src/utils/total-notes.ts
Normal 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;
|
||||
}
|
||||
11
packages/common/tsconfig.json
Normal file
11
packages/common/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"maxNodeModuleJsDepth": 10,
|
||||
"downlevelIteration": true,
|
||||
"allowJs": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src/"]
|
||||
}
|
||||
Reference in New Issue
Block a user