diff --git a/apps/mobile/app/components/dialog/dialog-header.tsx b/apps/mobile/app/components/dialog/dialog-header.tsx
index 8b1ab7053..57b26e3ae 100644
--- a/apps/mobile/app/components/dialog/dialog-header.tsx
+++ b/apps/mobile/app/components/dialog/dialog-header.tsx
@@ -35,6 +35,7 @@ type DialogHeaderProps = {
loading?: boolean;
title?: string;
type?: PressableButtonProps["type"];
+ icon?: string
};
paragraphColor?: string;
padding?: number;
@@ -96,6 +97,7 @@ const DialogHeader = ({
loading={button.loading}
fontSize={13}
title={button.title}
+ icon={button.icon}
type={button.type || "grayBg"}
height={25}
/>
diff --git a/apps/mobile/app/components/list-items/note/index.js b/apps/mobile/app/components/list-items/note/index.js
index 35520ba90..0d3de364c 100644
--- a/apps/mobile/app/components/list-items/note/index.js
+++ b/apps/mobile/app/components/list-items/note/index.js
@@ -35,6 +35,10 @@ import { IconButton } from "../../ui/icon-button";
import { TimeSince } from "../../ui/time-since";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
+import { getUpcomingReminder } from "../../../utils/time";
+import { formatReminderTime } from "../../../utils/time/index";
+import { TouchableOpacity } from "react-native";
+import ReminderSheet from "../../sheets/reminder";
const navigateToTopic = (topic) => {
TopicNotes.navigate(topic, true);
@@ -86,6 +90,8 @@ const NoteItem = ({
const compactMode = notesListMode === "compact";
const attachmentCount = db.attachments?.ofNote(item.id, "all")?.length || 0;
const notebooks = React.useMemo(() => getNotebook(item), [item]);
+ const reminders = db.relations.from(item, "reminder");
+ const current = getUpcomingReminder(reminders);
return (
<>
@@ -154,6 +160,37 @@ const NoteItem = ({
) : null}
+ {current && current.date ? (
+ {
+ Properties.present(item);
+ }}
+ style={{
+ backgroundColor: colors.nav,
+ borderRadius: 5,
+ flexDirection: "row",
+ paddingHorizontal: 5,
+ paddingVertical: 3,
+ alignItems: "center",
+ marginTop: 5,
+ justifyContent: "flex-start",
+ alignSelf: "flex-start"
+ }}
+ >
+ <>
+
+
+ {formatReminderTime(current)}
+
+ >
+
+ ) : null}
+
{item.type === "note" ? (
diff --git a/apps/mobile/app/components/properties/index.js b/apps/mobile/app/components/properties/index.js
index 90624dce0..0709254d1 100644
--- a/apps/mobile/app/components/properties/index.js
+++ b/apps/mobile/app/components/properties/index.js
@@ -124,7 +124,8 @@ export const Properties = ({
alignItems: "center",
justifyContent: "flex-start",
alignSelf: "flex-start",
- marginBottom: 5
+ marginBottom: 5,
+ marginTop: !item.description ? 5 : 0
}}
>
{item.date ? (
@@ -249,7 +250,7 @@ Properties.present = (item, buttons = [], isSheet) => {
}
if (!props[0]) return;
presentSheet({
- context:isSheet ? "local" : undefined,
+ context: isSheet ? "local" : undefined,
component: (ref, close) => (
{
diff --git a/apps/mobile/app/components/sheets/reminders-list/index.tsx b/apps/mobile/app/components/sheets/relations-list/index.tsx
similarity index 53%
rename from apps/mobile/app/components/sheets/reminders-list/index.tsx
rename to apps/mobile/app/components/sheets/relations-list/index.tsx
index 3b6447632..108c94f6b 100644
--- a/apps/mobile/app/components/sheets/reminders-list/index.tsx
+++ b/apps/mobile/app/components/sheets/relations-list/index.tsx
@@ -16,20 +16,25 @@ 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 .
*/
-import React, { RefObject, useState } from "react";
+import React, { RefObject, useEffect, useState } from "react";
import { View } from "react-native";
import ActionSheet from "react-native-actions-sheet";
+import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../../common/database";
import {
- presentSheet,
- PresentSheetOptions
+ PresentSheetOptions,
+ presentSheet
} from "../../../services/event-manager";
+import { Reminder } from "../../../services/notifications";
+import { useRelationStore } from "../../../stores/use-relation-store";
+import { useThemeStore } from "../../../stores/use-theme-store";
+import { SIZE } from "../../../utils/size";
import DialogHeader from "../../dialog/dialog-header";
import List from "../../list";
-import { useEffect } from "react";
-import { Reminder } from "../../../services/notifications";
-import { PressableButtonProps } from "../../ui/pressable";
import SheetProvider from "../../sheet-provider";
+import { Button } from "../../ui/button";
+import { PressableButtonProps } from "../../ui/pressable";
+import Paragraph from "../../ui/typography/paragraph";
type RelationsListProps = {
actionSheetRef: RefObject;
@@ -40,6 +45,7 @@ type RelationsListProps = {
relationType: "to" | "from";
title: string;
button?: Button;
+ onAdd: () => void;
};
type Button = {
@@ -47,6 +53,11 @@ type Button = {
loading?: boolean | undefined;
title?: string | undefined;
type?: PressableButtonProps["type"];
+ icon?: string;
+};
+
+const IconsByType = {
+ reminder: "bell"
};
export const RelationsList = ({
@@ -57,27 +68,68 @@ export const RelationsList = ({
referenceType,
relationType,
title,
- button
+ button,
+ onAdd
}: RelationsListProps) => {
+ const relations = useRelationStore();
const [items, setItems] = useState([]);
+ const colors = useThemeStore((state) => state.colors);
+ const hasNoReminders = !items || items.length === 0;
useEffect(() => {
- db.relations?.[relationType]?.(
- { id: item?.id, type: item.type },
- referenceType
- ).then((items) => setItems(items as any));
- }, [item?.id, item?.type, referenceType, relationType]);
+ setItems(
+ db.relations?.[relationType]?.(
+ { id: item?.id, type: item.type },
+ referenceType
+ ) as any
+ );
+ }, [item?.id, item?.type, referenceType, relationType, relations.updater]);
+ console.log(items);
return (
-
+
-
-
+ {hasNoReminders ? (
+
+
+
+ No {referenceType}s linked to this {item.type}.
+
+
+ ) : (
+
+ )}
);
};
@@ -87,13 +139,15 @@ RelationsList.present = ({
referenceType,
relationType,
title,
- button
+ button,
+ onAdd
}: {
reference: { id: string; type: string };
referenceType: string;
relationType: "to" | "from";
title: string;
- button: Button;
+ button?: Button;
+ onAdd: () => void;
}) => {
presentSheet({
component: (ref, close, update) => (
@@ -106,6 +160,7 @@ RelationsList.present = ({
relationType={relationType}
title={title}
button={button}
+ onAdd={onAdd}
/>
)
});
diff --git a/apps/mobile/app/components/sheets/reminder/index.tsx b/apps/mobile/app/components/sheets/reminder/index.tsx
index 1676822a5..e37b3808a 100644
--- a/apps/mobile/app/components/sheets/reminder/index.tsx
+++ b/apps/mobile/app/components/sheets/reminder/index.tsx
@@ -21,23 +21,23 @@ import { Platform, ScrollView, View } from "react-native";
import ActionSheet from "react-native-actions-sheet";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import {
- presentSheet,
PresentSheetOptions,
- ToastEvent
+ ToastEvent,
+ presentSheet
} from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
-import Notifications, { Reminder } from "../../../services/notifications";
-import Paragraph from "../../ui/typography/paragraph";
import dayjs from "dayjs";
-import { db } from "../../../common/database";
-import Navigation from "../../../services/navigation";
-import { formatReminderTime } from "../../../utils/time";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
+import { db } from "../../../common/database";
+import Notifications, { Reminder } from "../../../services/notifications";
import { useReminderStore } from "../../../stores/use-reminder-store";
+import { formatReminderTime } from "../../../utils/time";
+import Paragraph from "../../ui/typography/paragraph";
+import DialogHeader from "../../dialog/dialog-header";
type ReminderSheetProps = {
actionSheetRef: RefObject;
close?: () => void;
@@ -62,7 +62,6 @@ const RecurringModes = {
Week: "week",
Month: "month"
};
-
const WeekDays = new Array(7).fill(true);
const MonthDays = new Array(31).fill(true);
const WeekDayNames = {
@@ -81,6 +80,9 @@ const ReminderNotificationModes = {
Urgent: "urgent"
};
+
+
+
export default function ReminderSheet({
actionSheetRef,
close,
@@ -431,7 +433,7 @@ export default function ReminderSheet({
{Object.keys(ReminderNotificationModes).map((mode) => (
diff --git a/apps/mobile/app/hooks/use-actions.js b/apps/mobile/app/hooks/use-actions.js
index 7ca3c2d28..302cef749 100644
--- a/apps/mobile/app/hooks/use-actions.js
+++ b/apps/mobile/app/hooks/use-actions.js
@@ -57,7 +57,7 @@ import {
} from "../utils/events";
import { deleteItems } from "../utils/functions";
import { sleep } from "../utils/time";
-import { RelationsList } from "../components/sheets/reminders-list/index";
+import { RelationsList } from "../components/sheets/relations-list/index";
export const useActions = ({ close = () => null, item }) => {
const colors = useThemeStore((state) => state.colors);
@@ -820,15 +820,23 @@ export const useActions = ({ close = () => null, item }) => {
name: "Reminders",
title: "Reminders",
icon: "bell",
- func: () => {
+ func: async () => {
+ close();
RelationsList.present({
reference: item,
referenceType: "reminder",
relationType: "from",
- title: "Reminders"
+ title: "Reminders",
+ onAdd: () => ReminderSheet.present(null, item, true),
+ button: {
+ title: "Add",
+ type: "accent",
+ onPress: () => ReminderSheet.present(null, item, true),
+ icon:"plus"
+ }
});
},
- close: true
+ close: false
}
];
diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
index 8de248764..ab85a073a 100644
--- a/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
+++ b/apps/mobile/app/screens/editor/tiptap/use-editor-events.ts
@@ -59,6 +59,8 @@ import { useDragState } from "../../settings/editor/state";
import { EditorMessage, EditorProps, useEditorType } from "./types";
import { EditorEvents, editorState } from "./utils";
import { openLinkInBrowser } from "../../../utils/functions";
+import { RelationsList } from "../../../components/sheets/relations-list";
+import ReminderSheet from "../../../components/sheets/reminder";
export const EventTypes = {
selection: "editor-event:selection",
@@ -77,7 +79,8 @@ export const EventTypes = {
properties: "editor-event:properties",
fullscreen: "editor-event:fullscreen",
link: "editor-event:link",
- contentchange: "editor-event:content-change"
+ contentchange: "editor-event:content-change",
+ reminders: "editor-event:reminders"
};
const publishNote = async (editor: useEditorType) => {
@@ -315,8 +318,32 @@ export const useEditorEvents = (
title: editorMessage.value as string
});
break;
+
+ case EventTypes.reminders:
+ if (!editor.note.current) {
+ ToastEvent.show({
+ heading: "Create a note first to add a reminder",
+ type: "success"
+ });
+ return;
+ }
+ RelationsList.present({
+ reference: editor.note.current as any,
+ referenceType: "reminder",
+ relationType: "from",
+ title: "Reminders",
+ onAdd: () =>
+ ReminderSheet.present(undefined, editor.note.current as any, true)
+ });
+ break;
case EventTypes.newtag:
- if (!editor.note.current) return;
+ if (!editor.note.current) {
+ ToastEvent.show({
+ heading: "Create a note first to add a tag",
+ type: "success"
+ });
+ return;
+ }
eSendEvent(eOpenTagsDialog, editor.note.current);
break;
case EventTypes.tag:
diff --git a/apps/mobile/app/services/notifications.ts b/apps/mobile/app/services/notifications.ts
index 81881d0aa..b3886369e 100644
--- a/apps/mobile/app/services/notifications.ts
+++ b/apps/mobile/app/services/notifications.ts
@@ -60,19 +60,31 @@ let pinned: DisplayedNotification[] = [];
const onEvent = async ({ type, detail }: Event) => {
const { notification, pressAction, input } = detail;
if (type === EventType.PRESS) {
- if (notification?.data?.type !== "pinnedNote") return;
+ if (notification?.data?.type === "quickNote") return;
editorState().movedAway = false;
MMKV.removeItem("appState");
+ let noteId = notification?.id;
+ if (notification?.data?.type === "reminder") {
+ const notes = db.relations?.to(
+ { type: "reminder", id: notification.id?.split("_")[0] as string },
+ "note"
+ );
+ if (!notes || notes.length === 0) return;
+ if (notes && notes.length > 0) {
+ noteId = notes[0].id;
+ }
+ }
+
if (useNoteStore?.getState()?.loading === false) {
await db.init();
await db.notes?.init();
- loadNote(notification?.id as string, false);
+ loadNote(noteId as string, false);
return;
}
const unsub = useNoteStore.subscribe(
(loading) => {
if (loading === false) {
- loadNote(notification?.id as string, true);
+ loadNote(noteId as string, true);
}
unsub();
},
@@ -184,6 +196,7 @@ function loadNote(id: string, jump: boolean) {
}
}, 2000);
}
+
async function getChannelId(id: "silent" | "vibrate" | "urgent" | "default") {
switch (id) {
case "default":
@@ -323,7 +336,7 @@ function getTriggers(
];
case "permanent":
return undefined;
- case "repeat" : {
+ case "repeat": {
switch (recurringMode) {
case "day":
return [
diff --git a/apps/mobile/app/stores/use-relation-store.ts b/apps/mobile/app/stores/use-relation-store.ts
new file mode 100644
index 000000000..37b9375eb
--- /dev/null
+++ b/apps/mobile/app/stores/use-relation-store.ts
@@ -0,0 +1,32 @@
+/*
+This file is part of the Notesnook project (https://notesnook.com/)
+
+Copyright (C) 2022 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 .
+*/
+
+import create, { State } from "zustand";
+
+export interface RelationStore extends State {
+ updater: number;
+ update: () => void;
+}
+
+export const useRelationStore = create((set, get) => ({
+ updater: 0,
+ update: () => {
+ set({ updater: get().updater + 1 });
+ }
+}));
diff --git a/apps/mobile/app/utils/time/index.ts b/apps/mobile/app/utils/time/index.ts
index d78293d2d..0b05ffa28 100644
--- a/apps/mobile/app/utils/time/index.ts
+++ b/apps/mobile/app/utils/time/index.ts
@@ -120,6 +120,15 @@ export function formatReminderTime(reminder: Reminder) {
);
}
+export function getUpcomingReminder(reminders: Reminder[]) {
+ const sorted = reminders.sort((a, b) => {
+ const d1 = a.mode === "repeat" ? getUpcomingReminderTime(a) : a.date;
+ const d2 = a.mode === "repeat" ? getUpcomingReminderTime(b) : b.date;
+ return !d1 || !d2 ? 0 : d1 - d2;
+ });
+ return sorted[0];
+}
+
export const sleep = (duration: number) =>
new Promise((resolve) => setTimeout(() => resolve(true), duration));
diff --git a/apps/mobile/app/utils/types.ts b/apps/mobile/app/utils/types.ts
index be34430ea..0e5e263f0 100644
--- a/apps/mobile/app/utils/types.ts
+++ b/apps/mobile/app/utils/types.ts
@@ -172,3 +172,5 @@ export interface GroupHeader
extends Omit, "id" | "dateCreated" | "dateModified"> {
title: string;
}
+
+export type ItemReference = { id: string; type: string };
diff --git a/packages/editor-mobile/src/components/header.tsx b/packages/editor-mobile/src/components/header.tsx
index a1d1db383..1fd910422 100644
--- a/packages/editor-mobile/src/components/header.tsx
+++ b/packages/editor-mobile/src/components/header.tsx
@@ -25,6 +25,7 @@ import ArrowULeftTopIcon from "mdi-react/ArrowULeftTopIcon";
import ArrowURightTopIcon from "mdi-react/ArrowURightTopIcon";
import FullscreenIcon from "mdi-react/FullscreenIcon";
import MagnifyIcon from "mdi-react/MagnifyIcon";
+import BellIcon from "mdi-react/BellIcon";
import React from "react";
import { useSafeArea } from "../hooks/useSafeArea";
import { EventTypes, Settings } from "../utils";
@@ -77,7 +78,6 @@ export default function Header({
position: "sticky",
width: "100vw"
}}
-
>
{noHeader ? null : (