mobile: add disable/snooze function

This commit is contained in:
ammarahm-ed
2023-01-03 10:23:48 +05:00
committed by Abdullah Atta
parent 484bc02a28
commit 26ec8551f9
38 changed files with 680 additions and 372 deletions

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@ node_modules
dist
.DS_STORE
nx-cloud.env
.idea
.idea
.eslintcache

View File

@@ -25,7 +25,7 @@ import Sync from "../../services/sync";
import { useThemeStore } from "../../stores/use-theme-store";
import {
eCloseAnnouncementDialog,
eCloseProgressDialog
eCloseSheet
} from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
@@ -60,11 +60,11 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
)
});
} else if (item.type === "force-sync") {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(300);
Progress.present();
Sync.run("global", true, true, () => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
});
}
};

View File

@@ -37,7 +37,7 @@ import { useThemeStore } from "../../stores/use-theme-store";
import { formatBytes } from "../../utils";
import {
eCloseAttachmentDialog,
eCloseProgressDialog
eCloseSheet
} from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
@@ -71,7 +71,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
useAttachmentStore.getState().remove(attachment.metadata.hash);
}
downloadAttachment(attachment.metadata.hash, false);
eSendEvent(eCloseProgressDialog, contextId);
eSendEvent(eCloseSheet, contextId);
},
icon: "download"
},
@@ -150,7 +150,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
onPress: async () => {
await db.attachments.remove(attachment.metadata.hash, false);
setAttachments([...db.attachments.all]);
eSendEvent(eCloseProgressDialog, contextId);
eSendEvent(eCloseSheet, contextId);
},
icon: "delete-outline"
}
@@ -293,7 +293,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
});
return;
}
eSendEvent(eCloseProgressDialog, contextId);
eSendEvent(eCloseSheet, contextId);
await sleep(150);
eSendEvent(eCloseAttachmentDialog);
await sleep(300);

View File

@@ -26,7 +26,7 @@ import {
ToastEvent
} from "../../services/event-manager";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseProgressDialog } from "../../utils/events";
import { eCloseSheet } from "../../utils/events";
import DialogHeader from "../dialog/dialog-header";
import { Button } from "../ui/button";
import Input from "../ui/input";
@@ -72,7 +72,7 @@ export const ChangePassword = () => {
context: "global"
});
setLoading(false);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
} catch (e) {
setLoading(false);
ToastEvent.show({

View File

@@ -27,7 +27,7 @@ import {
ToastEvent
} from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store";
import { eCloseProgressDialog } from "../../utils/events";
import { eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
@@ -77,7 +77,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
},
(result) => {
if (result) {
eSendEvent(eCloseProgressDialog, "two_factor_verify");
eSendEvent(eCloseSheet, "two_factor_verify");
}
setLoading(false);
}

View File

@@ -23,7 +23,7 @@ import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { eSendEvent } from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store";
import {
eCloseProgressDialog,
eCloseSheet,
eCloseResultDialog,
eOpenPremiumDialog
} from "../../../utils/events";
@@ -82,7 +82,7 @@ export const ProFeatures = ({ count = 6 }) => {
<Paragraph
onPress={async () => {
eSendEvent(eCloseResultDialog);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(300);
eSendEvent(eOpenPremiumDialog);
}}

View File

@@ -17,14 +17,19 @@ 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 {
formatReminderTime,
getUpcomingReminder
} from "@notesnook/core/collections/reminders";
import { decode, EntityLevel } from "entities";
import React from "react";
import { View } from "react-native";
import { TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids";
import { db } from "../../../common/database";
import { TaggedNotes } from "../../../screens/notes/tagged";
import { TopicNotes } from "../../../screens/notes/topic-notes";
import { useRelationStore } from "../../../stores/use-relation-store";
import { useSettingStore } from "../../../stores/use-setting-store";
import { useThemeStore } from "../../../stores/use-theme-store";
import { COLORS_NOTE } from "../../../utils/color-scheme";
@@ -35,10 +40,6 @@ 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);
@@ -92,6 +93,7 @@ const NoteItem = ({
const notebooks = React.useMemo(() => getNotebook(item), [item]);
const reminders = db.relations.from(item, "reminder");
const current = getUpcomingReminder(reminders);
useRelationStore((state) => state.updater);
return (
<>
@@ -160,13 +162,15 @@ const NoteItem = ({
</Paragraph>
) : null}
{current && current.date ? (
{current &&
current.date &&
(current.mode !== "once" || current.date > Date.now()) ? (
<TouchableOpacity
activeOpacity={1}
onPress={() => {
Properties.present(item);
Properties.present(current);
}}
style={{
style={{
backgroundColor: colors.nav,
borderRadius: 5,
flexDirection: "row",

View File

@@ -20,24 +20,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import NoteItem from ".";
import { notesnook } from "../../../../e2e/test.ids";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { useTrashStore } from "../../../stores/use-trash-store";
import { useEditorStore } from "../../../stores/use-editor-store";
import { db } from "../../../common/database";
import { DDS } from "../../../services/device-detection";
import {
eSendEvent,
hideSheet,
openVault,
presentSheet,
ToastEvent
presentSheet
} from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { useEditorStore } from "../../../stores/use-editor-store";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { history } from "../../../utils";
import { db } from "../../../common/database";
import { eOnLoadNote, eShowMergeDialog } from "../../../utils/events";
import { tabBarRef } from "../../../utils/global-refs";
import { presentDialog } from "../../dialog/functions";
import SelectionWrapper from "../selection-wrapper";
import NotePreview from "../../note-history/preview";
import SelectionWrapper from "../selection-wrapper";
const present = () =>
presentDialog({
@@ -46,9 +44,9 @@ const present = () =>
paragraph: "Please sync again to open this note for editing"
});
export const openNote = async (item, isTrash, setSelectedItem) => {
export const openNote = async (item, isTrash, setSelectedItem, isSheet) => {
let _note = item;
if (isSheet) hideSheet();
if (!isTrash) {
_note = db.notes.note(item.id).data;
if (!db.notes.note(item.id)?.synced()) {
@@ -102,7 +100,7 @@ export const openNote = async (item, isTrash, setSelectedItem) => {
};
export const NoteWrapper = React.memo(
function NoteWrapper({ item, index, tags, dateBy }) {
function NoteWrapper({ item, index, tags, dateBy, isSheet }) {
const isTrash = item.type === "trash";
const setSelectedItem = useSelectionStore((state) => state.setSelectedItem);
@@ -111,7 +109,8 @@ export const NoteWrapper = React.memo(
index={index}
height={100}
testID={notesnook.ids.note.get(index)}
onPress={() => openNote(item, isTrash, setSelectedItem)}
onPress={() => openNote(item, isTrash, setSelectedItem, isSheet)}
isSheet={isSheet}
item={item}
>
<NoteItem item={item} dateBy={dateBy} tags={tags} isTrash={isTrash} />

View File

@@ -29,7 +29,7 @@ import Paragraph from "../../ui/typography/paragraph";
import SelectionWrapper from "../selection-wrapper";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import ReminderSheet from "../../sheets/reminder";
import { formatReminderTime } from "../../../utils/time/index";
import { formatReminderTime } from "@notesnook/core/collections/reminders";
const ReminderItem = React.memo(
({
@@ -54,7 +54,12 @@ const ReminderItem = React.memo(
item={item}
isSheet={isSheet}
>
<View>
<View
style={{
flexWrap: "wrap",
flexShrink: 1
}}
>
<Heading
numberOfLines={1}
style={{
@@ -78,10 +83,41 @@ const ReminderItem = React.memo(
<View
style={{
flexDirection: "row"
flexDirection: "row",
flexWrap: "wrap"
}}
>
{item.mode === "repeat" ? (
{item.disabled ? (
<View
style={{
backgroundColor: colors.nav,
borderRadius: 5,
flexDirection: "row",
paddingHorizontal: 5,
paddingVertical: 3,
alignItems: "center",
marginTop: 5,
justifyContent: "flex-start",
alignSelf: "flex-start",
marginRight: 10
}}
>
<Icon
name="bell-off-outline"
size={SIZE.md}
color={colors.errorText}
/>
<Paragraph
size={SIZE.xs + 1}
color={colors.icon}
style={{ marginLeft: 5 }}
>
Disabled
</Paragraph>
</View>
) : null}
{item.mode === "repeat" && item.recurringMode ? (
<View
style={{
backgroundColor: colors.nav,
@@ -108,20 +144,20 @@ const ReminderItem = React.memo(
</Paragraph>
</View>
) : null}
<View
style={{
backgroundColor: colors.nav,
borderRadius: 5,
flexDirection: "row",
paddingHorizontal: 5,
paddingVertical: 3,
alignItems: "center",
marginTop: 5,
justifyContent: "flex-start",
alignSelf: "flex-start"
}}
>
{item.date ? (
{item.date ? (
<View
style={{
backgroundColor: colors.nav,
borderRadius: 5,
flexDirection: "row",
paddingHorizontal: 5,
paddingVertical: 3,
alignItems: "center",
marginTop: 5,
justifyContent: "flex-start",
alignSelf: "flex-start"
}}
>
<>
<Icon
name="clock-outline"
@@ -136,8 +172,8 @@ const ReminderItem = React.memo(
{formatReminderTime(item)}
</Paragraph>
</>
) : null}
</View>
</View>
) : null}
</View>
</View>
<IconButton

View File

@@ -85,10 +85,9 @@ const SelectionWrapper = ({
eUnSubscribeEvent("navigate", closeStrip);
};
}, []);
return (
<PressableButton
customColor={isSheet ? colors.nav : "transparent"}
customColor={isSheet ? colors.transGray : "transparent"}
testID={testID}
onLongPress={_onLongPress}
onPress={_onPress}
@@ -100,7 +99,6 @@ const SelectionWrapper = ({
justifyContent: "space-between",
alignItems: "center",
width: "100%",
borderRadius: 0,
overflow: "hidden",
paddingHorizontal: 12,
paddingVertical: compactMode ? 8 : 12,

View File

@@ -84,6 +84,11 @@ const RenderItem = ({ item, index, type, ...restArgs }) => {
);
};
/**
*
* @param {any} param0
* @returns
*/
const List = ({
listData,
type,
@@ -97,7 +102,8 @@ const List = ({
screen,
ListHeader,
warning,
isSheet = false
isSheet = false,
onMomentumScrollEnd
}) => {
const colors = useThemeStore((state) => state.colors);
const scrollRef = useRef();
@@ -168,8 +174,10 @@ const List = ({
data={listData}
renderItem={renderItem}
onScroll={_onScroll}
nestedScrollEnabled={true}
onMomentumScrollEnd={() => {
tabBarRef.current?.unlock();
onMomentumScrollEnd?.();
}}
getItemType={(item) => item.itemType || item.type}
estimatedItemSize={isCompactModeEnabled ? 60 : 100}

View File

@@ -29,7 +29,7 @@ import { useEditorStore } from "../../stores/use-editor-store";
import { useSelectionStore } from "../../stores/use-selection-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useTrashStore } from "../../stores/use-trash-store";
import { eCloseProgressDialog, eOnLoadNote } from "../../utils/events";
import { eCloseSheet, eOnLoadNote } from "../../utils/events";
import { sleep } from "../../utils/time";
import { Dialog } from "../dialog";
import DialogHeader from "../dialog/dialog-header";
@@ -59,7 +59,7 @@ export default function NotePreview({ session, content, note }) {
heading: "Restore successful",
type: "success"
});
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
return;
}
await db.noteHistory.restore(session.id);
@@ -71,8 +71,8 @@ export default function NotePreview({ session, content, note }) {
});
}
}
eSendEvent(eCloseProgressDialog, "note_history");
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet, "note_history");
eSendEvent(eCloseSheet);
Navigation.queueRoutesForUpdate(
"Notes",
"Favorites",
@@ -103,7 +103,7 @@ export default function NotePreview({ session, content, note }) {
type: "success",
context: "local"
});
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
},
positiveType:"error"
});

View File

@@ -30,7 +30,7 @@ import { useUserStore } from "../../stores/use-user-store";
import { getElevation } from "../../utils";
import {
eClosePremiumDialog,
eCloseProgressDialog,
eCloseSheet,
eOpenLoginDialog
} from "../../utils/events";
import { SIZE } from "../../utils/size";
@@ -194,7 +194,7 @@ export const Component = ({ close, promo }) => {
try {
await db.user.activateTrial();
eSendEvent(eClosePremiumDialog);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(300);
Walkthrough.present("trialstarted", false, true);
} catch (e) {

View File

@@ -33,7 +33,7 @@ import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import {
eClosePremiumDialog,
eCloseProgressDialog,
eCloseSheet,
eCloseSimpleDialog,
eOpenLoginDialog
} from "../../utils/events";
@@ -154,7 +154,7 @@ export const PricingPlans = ({
user.id
);
setBuying(false);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
eSendEvent(eClosePremiumDialog);
await sleep(500);
presentSheet({
@@ -162,7 +162,7 @@ export const PricingPlans = ({
paragraph:
"Your Notesnook Pro subscription will be activated soon. If your account is not upgraded to Notesnook Pro, your money will be refunded to you. In case of any issues, please reach out to us at support@streetwriters.co",
action: async () => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
},
icon: "check",
actionText: "Continue"
@@ -226,7 +226,7 @@ export const PricingPlans = ({
try {
await db.user.activateTrial();
eSendEvent(eClosePremiumDialog);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(300);
Walkthrough.present("trialstarted", false, true);
} catch (e) {
@@ -393,7 +393,7 @@ export const PricingPlans = ({
<Button
onPress={() => {
eSendEvent(eClosePremiumDialog);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
setTimeout(() => {
eSendEvent(eOpenLoginDialog, 1);
}, 400);

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { Platform, View } from "react-native";
import { Platform, View, TouchableOpacity } from "react-native";
import { ScrollView } from "react-native-gesture-handler";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
@@ -27,7 +27,6 @@ import SearchService from "../../services/search";
import { useThemeStore } from "../../stores/use-theme-store";
import { COLORS_NOTE } from "../../utils/color-scheme";
import { SIZE } from "../../utils/size";
import { formatReminderTime } from "../../utils/time";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { ColorTags } from "./color-tags";
@@ -39,6 +38,10 @@ import { Synced } from "./synced";
import { Tags } from "./tags";
import { Topics } from "./topics";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import {
formatReminderTime,
getUpcomingReminder
} from "@notesnook/core/collections/reminders";
export const Properties = ({
close = () => {},
item,
@@ -48,6 +51,8 @@ export const Properties = ({
const colors = useThemeStore((state) => state.colors);
const alias = item.alias || item.title;
const isColor = !!COLORS_NOTE[item.title];
const reminders = db.relations.from(item, "reminder");
const current = getUpcomingReminder(reminders);
const onScrollEnd = () => {
getRef().current?.handleChildScrollEnd();
@@ -244,7 +249,7 @@ Properties.present = (item, buttons = [], isSheet) => {
break;
case "reminder": {
props[0] = db.reminders.reminder(item.id);
props.push(["Edit reminder", "Delete"]);
props.push(["Edit reminder", "Delete", "ReminderOnOff", "ReferencedIn"]);
break;
}
}

View File

@@ -26,7 +26,7 @@ import {
eUnSubscribeEvent
} from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store";
import { eCloseProgressDialog, eOpenProgressDialog } from "../../utils/events";
import { eCloseSheet, eOpenSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { Button } from "../ui/button";
@@ -43,11 +43,11 @@ const SheetProvider = ({ context = "global" }) => {
});
useEffect(() => {
eSubscribeEvent(eOpenProgressDialog, open);
eSubscribeEvent(eCloseProgressDialog, close);
eSubscribeEvent(eOpenSheet, open);
eSubscribeEvent(eCloseSheet, close);
return () => {
eUnSubscribeEvent(eOpenProgressDialog, open);
eUnSubscribeEvent(eCloseProgressDialog, close);
eUnSubscribeEvent(eOpenSheet, open);
eUnSubscribeEvent(eCloseSheet, close);
};
}, [close, open, visible]);

View File

@@ -41,7 +41,7 @@ import Seperator from "../../ui/seperator";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { eSendEvent } from "../../../services/event-manager";
import { eCloseProgressDialog } from "../../../utils/events";
import { eCloseSheet } from "../../../utils/events";
const ExportNotesSheet = ({ notes, update }) => {
const colors = useThemeStore((state) => state.colors);
@@ -262,7 +262,7 @@ const ExportNotesSheet = ({ notes, update }) => {
borderRadius: 100
}}
onPress={async () => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(500);
FileViewer.open(result.filePath, {
showOpenWithDialog: true,

View File

@@ -26,7 +26,7 @@ import { eSendEvent, ToastEvent } from "../../../services/event-manager";
import PremiumService from "../../../services/premium";
import { useThemeStore } from "../../../stores/use-theme-store";
import { useUserStore } from "../../../stores/use-user-store";
import { eCloseProgressDialog } from "../../../utils/events";
import { eCloseSheet } from "../../../utils/events";
import { openLinkInBrowser } from "../../../utils/functions";
import { SIZE } from "../../../utils/size";
import { sleep } from "../../../utils/time";
@@ -69,7 +69,7 @@ Logged in: ${user ? "yes" : "no"}`,
userId: user?.id
});
setLoading(false);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(300);
presentDialog({
title: "Issue reported",

View File

@@ -30,7 +30,7 @@ import {
} from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { useThemeStore } from "../../../stores/use-theme-store";
import { eCloseProgressDialog } from "../../../utils/events";
import { eCloseSheet } from "../../../utils/events";
import { sleep } from "../../../utils/time";
import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
@@ -103,7 +103,7 @@ export default function Migrate() {
return;
}
await db.migrations?.migrate();
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(500);
setLoading(false);
} catch (e) {

View File

@@ -31,7 +31,7 @@ import {
import Navigation from "../../../services/navigation";
import SearchService from "../../../services/search";
import { useThemeStore } from "../../../stores/use-theme-store";
import { eCloseProgressDialog } from "../../../utils/events";
import { eCloseSheet } from "../../../utils/events";
import { SIZE } from "../../../utils/size";
import { Dialog } from "../../dialog";
import DialogHeader from "../../dialog/dialog-header";
@@ -332,7 +332,7 @@ export const MoveNotes = ({
"Notebooks"
);
SearchService.updateAndSearch();
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
}}
title="Move selected notes"
type="accent"

View File

@@ -24,7 +24,7 @@ import { features } from "../../../features";
import { eSendEvent, presentSheet } from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { useThemeStore } from "../../../stores/use-theme-store";
import { eCloseProgressDialog } from "../../../utils/events";
import { eCloseSheet } from "../../../utils/events";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
@@ -78,7 +78,7 @@ const NewFeature = ({ features }: { features: FeatureType[] }) => {
borderRadius: 100
}}
onPress={() => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
}}
/>
</View>

View File

@@ -74,7 +74,7 @@ export const RelationsList = ({
const relations = useRelationStore();
const [items, setItems] = useState<Reminder[]>([]);
const colors = useThemeStore((state) => state.colors);
const hasNoReminders = !items || items.length === 0;
const hasNoRelations = !items || items.length === 0;
useEffect(() => {
setItems(
@@ -84,17 +84,16 @@ export const RelationsList = ({
) as any
);
}, [item?.id, item?.type, referenceType, relationType, relations.updater]);
console.log(items);
return (
<View
style={{ paddingHorizontal: 12, height: hasNoReminders ? 300 : "100%" }}
style={{ paddingHorizontal: 12, height: hasNoRelations ? 300 : "100%" }}
>
<SheetProvider context="local" />
<DialogHeader
title={title}
button={hasNoReminders ? undefined : button}
button={hasNoRelations ? undefined : button}
/>
{hasNoReminders ? (
{hasNoRelations ? (
<View
style={{
height: "85%",
@@ -128,6 +127,9 @@ export const RelationsList = ({
type={referenceType}
headerProps={null}
isSheet={true}
onMomentumScrollEnd={() => {
actionSheetRef?.current?.handleChildScrollEnd();
}}
/>
)}
</View>

View File

@@ -0,0 +1,186 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import dayjs from "dayjs";
import React, { RefObject } from "react";
import { ScrollView, 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
} from "../../../services/event-manager";
import Notifications, { Reminder } from "../../../services/notifications";
import { useThemeStore } from "../../../stores/use-theme-store";
import { SIZE } from "../../../utils/size";
import { ItemReference } from "../../../utils/types";
import List from "../../list";
import { Button } from "../../ui/button";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
type ReminderSheetProps = {
actionSheetRef: RefObject<ActionSheet>;
close?: () => void;
update?: (options: PresentSheetOptions) => void;
reminder?: Reminder;
};
export default function ReminderNotify({
actionSheetRef,
close,
update,
reminder
}: ReminderSheetProps) {
const colors = useThemeStore((state) => state.colors);
const references = db.relations?.to(reminder as ItemReference, "note") || [];
const QuickActions = [
{
title: "5 min",
time: 5
},
{
title: "15 min",
time: 15
},
{
title: "30 min",
time: 30
},
{
title: "1 hour",
time: 60
}
];
const onSnooze = async (time: number) => {
const snoozeTime = Date.now() + time * 60000;
await db.reminders?.add({
...reminder,
snoozeUntil: snoozeTime
});
await Notifications.scheduleNotification(
db.reminders?.reminder(reminder?.id)
);
close?.();
};
return (
<View
style={{
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 12
}}
>
<Heading>{reminder?.title}</Heading>
{reminder?.description && <Paragraph>{reminder?.description}</Paragraph>}
<View
style={{
height: 40,
borderRadius: 100,
paddingHorizontal: 12,
flexDirection: "row",
alignItems: "center"
}}
>
<Icon name="clock-outline" size={20} color={colors.accent} />
<Paragraph style={{ marginLeft: 5 }}>
{dayjs(reminder?.date).format("ddd, YYYY-MM-DD hh:mm A")}
</Paragraph>
</View>
<ScrollView
nestedScrollEnabled
horizontal={true}
contentContainerStyle={{
alignItems: "center",
paddingVertical: 10
}}
showsHorizontalScrollIndicator={false}
style={{
marginTop: 10
}}
>
<Paragraph size={SIZE.xs + 1}>Remind me in:</Paragraph>
{QuickActions.map((item) => {
return (
<Button
type="grayAccent"
key={item.title}
title={item.title}
height={30}
fontSize={SIZE.xs + 1}
style={{ marginLeft: 10, borderRadius: 100 }}
onPress={() => onSnooze(item.time)}
/>
);
})}
</ScrollView>
{references.length > 0 ? (
<View
style={{
width: "100%",
height:
160 * references?.length < 500 ? 160 * references?.length : 500,
borderTopWidth: 1,
borderTopColor: colors.nav,
marginTop: 5,
paddingTop: 5
}}
>
<Paragraph
style={{
color: colors.icon,
fontSize: SIZE.xs + 1,
marginBottom: 10
}}
>
REFERENCED IN
</Paragraph>
<List
listData={references}
loading={false}
type="notes"
headerProps={null}
isSheet={true}
onMomentumScrollEnd={() =>
actionSheetRef.current?.handleChildScrollEnd()
}
/>
</View>
) : null}
</View>
);
}
ReminderNotify.present = (reminder?: Reminder) => {
presentSheet({
component: (ref, close, update) => (
<ReminderNotify
actionSheetRef={ref}
close={close}
update={update}
reminder={reminder}
/>
)
});
};

View File

@@ -21,26 +21,26 @@ 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,
presentSheet
ToastEvent
} 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 { formatReminderTime } from "@notesnook/core/collections/reminders";
import dayjs from "dayjs";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../../common/database";
import Navigation from "../../../services/navigation";
import Notifications, { Reminder } from "../../../services/notifications";
import { useReminderStore } from "../../../stores/use-reminder-store";
import { formatReminderTime } from "../../../utils/time";
import { useRelationStore } from "../../../stores/use-relation-store";
import Paragraph from "../../ui/typography/paragraph";
import DialogHeader from "../../dialog/dialog-header";
type ReminderSheetProps = {
actionSheetRef: RefObject<ActionSheet>;
close?: () => void;
close?: (ctx?: string) => void;
update?: (options: PresentSheetOptions) => void;
reminder?: Reminder;
reference?: { id: string; type: string };
@@ -59,6 +59,7 @@ const ReminderModes =
};
const RecurringModes = {
Daily: "day",
Week: "week",
Month: "month"
};
@@ -80,9 +81,6 @@ const ReminderNotificationModes = {
Urgent: "urgent"
};
export default function ReminderSheet({
actionSheetRef,
close,
@@ -129,9 +127,9 @@ export default function ReminderSheet({
};
const handleConfirm = (date: Date) => {
hideDatePicker();
setTime(dayjs(date).format("hh:mm a"));
setDate(date);
hideDatePicker();
};
function nth(n: number) {
return (
@@ -149,7 +147,7 @@ export default function ReminderSheet({
const joinWith = isSecondLast ? " & " : isLast ? "" : ", ";
return recurringMode === RecurringModes.Week
? WeekDayNames[day as keyof typeof WeekDayNames] + joinWith
: `${day + 1}${nth(day + 1)} ${joinWith}`;
: `${day}${nth(day)} ${joinWith}`;
})
.join("");
return text;
@@ -159,6 +157,7 @@ export default function ReminderSheet({
if (!(await Notifications.checkAndRequestPermissions())) return;
if ((!date && reminderMode !== ReminderModes.Permanent) || !title.current)
return;
date.setSeconds(0, 0);
const reminderId = await db.reminders?.add({
id: reminder?.id,
date: date?.getTime(),
@@ -185,8 +184,17 @@ export default function ReminderSheet({
});
}
Notifications.scheduleNotification(_reminder as Reminder);
useReminderStore.getState().setReminders();
close?.();
useRelationStore.getState().update();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"Notes",
"NotesPage",
"Reminders",
"Favorites",
"TopicNotes"
);
close?.("local");
}
return (
@@ -253,7 +261,7 @@ export default function ReminderSheet({
<View
style={{
flexDirection: "row",
marginBottom: 12,
marginBottom: recurringMode === "day" ? 0 : 12,
alignItems: "center"
}}
>
@@ -313,7 +321,9 @@ export default function ReminderSheet({
</View>
<ScrollView showsHorizontalScrollIndicator={false} horizontal>
{recurringMode === RecurringModes.Week
{recurringMode === RecurringModes.Daily
? null
: recurringMode === RecurringModes.Week
? WeekDays.map((item, index) => (
<Button
key={WeekDayNames[index as keyof typeof WeekDayNames]}
@@ -348,7 +358,9 @@ export default function ReminderSheet({
<Button
key={index + "monthday"}
title={index + 1 + ""}
type={selectedDays.indexOf(index) > -1 ? "accent" : "gray"}
type={
selectedDays.indexOf(index + 1) > -1 ? "accent" : "gray"
}
fontSize={SIZE.sm - 1}
style={{
width: 40,
@@ -356,17 +368,17 @@ export default function ReminderSheet({
borderRadius: 100,
marginRight: 10,
backgroundColor:
selectedDays.indexOf(index) > -1
selectedDays.indexOf(index + 1) > -1
? colors.accent
: colors.bg
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index) > -1) {
days.splice(days.indexOf(index), 1);
if (days.indexOf(index + 1) > -1) {
days.splice(days.indexOf(index + 1), 1);
return [...days];
}
days.push(index);
days.push(index + 1);
return [...days];
});
}}
@@ -433,7 +445,7 @@ export default function ReminderSheet({
<View
style={{
flexDirection: "row",
marginBottom: 12,
marginBottom: 12
}}
>
{Object.keys(ReminderNotificationModes).map((mode) => (
@@ -471,30 +483,43 @@ export default function ReminderSheet({
))}
</View>
)}
{reminderMode === ReminderModes.Once ||
reminderMode === ReminderModes.Permanent ? null : (
<Paragraph
color={colors.icon}
size={SIZE.xs}
style={{ marginTop: 5, marginBottom: 12 }}
<View
style={{
borderRadius: 5,
flexDirection: "row",
paddingVertical: 6,
paddingHorizontal: 12,
alignItems: "center",
justifyContent: "flex-start",
marginBottom: 10,
backgroundColor: colors.nav
}}
>
{selectedDays.length === 7 && recurringMode === RecurringModes.Week
? `The reminder will repeat daily at ${dayjs(date).format(
"hh:mm A"
)}.`
: selectedDays.length === 0
? recurringMode === RecurringModes.Week
? "Select day of the week to repeat the reminder."
: "Select nth day of the month to repeat the reminder."
: `Repeats every${
repeatFrequency > 1 ? " " + repeatFrequency : ""
} ${
repeatFrequency > 1 ? recurringMode + "s" : recurringMode
} on ${getSelectedDaysText(selectedDays)} at ${dayjs(date).format(
"hh:mm A"
)}.`}
</Paragraph>
<>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
{recurringMode === RecurringModes.Daily
? "Repeats daily " + `at ${dayjs(date).format("hh:mm A")}.`
: selectedDays.length === 7 &&
recurringMode === RecurringModes.Week
? `The reminder will repeat daily at ${dayjs(date).format(
"hh:mm A"
)}.`
: selectedDays.length === 0
? recurringMode === RecurringModes.Week
? "Select day of the week to repeat the reminder."
: "Select nth day of the month to repeat the reminder."
: `Repeats every${
repeatFrequency > 1 ? " " + repeatFrequency : ""
} ${
repeatFrequency > 1 ? recurringMode + "s" : recurringMode
} on ${getSelectedDaysText(selectedDays)} at ${dayjs(
date
).format("hh:mm A")}.`}
</Paragraph>
</>
</View>
)}
{reminder && reminder.date ? (
@@ -502,12 +527,12 @@ export default function ReminderSheet({
style={{
borderRadius: 5,
flexDirection: "row",
paddingHorizontal: 5,
paddingVertical: 3,
paddingVertical: 6,
paddingHorizontal: 12,
alignItems: "center",
justifyContent: "flex-start",
marginTop: 12,
marginBottom: 4
marginBottom: 10,
backgroundColor: colors.nav
}}
>
<>

View File

@@ -23,7 +23,7 @@ import { MMKV } from "../../common/database/mmkv";
import { eSendEvent, presentSheet } from "../../services/event-manager";
import { TTip } from "../../services/tip-manager";
import { ThemeStore, useThemeStore } from "../../stores/use-theme-store";
import { eCloseProgressDialog } from "../../utils/events";
import { eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Seperator from "../ui/seperator";
@@ -90,7 +90,7 @@ export const Tip = ({
iconSize={SIZE.xs}
onPress={() => {
MMKV.setItem("neverShowSheetTips", "true");
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
}}
style={{
width: undefined,

View File

@@ -22,7 +22,7 @@ import { LayoutAnimation, View } from "react-native";
import { MMKV } from "../../common/database/mmkv";
import { eSendEvent, presentSheet } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store";
import { eCloseProgressDialog } from "../../utils/events";
import { eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { Button } from "../ui/button";
@@ -98,7 +98,7 @@ export const Walkthrough = ({
next();
return;
case "done":
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(300);
step.button?.action && step.button.action();
return;
@@ -119,7 +119,7 @@ export const Walkthrough = ({
textDecorationLine: "underline"
}}
onPress={async () => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
}}
type="gray"
title="Skip introduction"

View File

@@ -58,6 +58,7 @@ import {
import { deleteItems } from "../utils/functions";
import { sleep } from "../utils/time";
import { RelationsList } from "../components/sheets/relations-list/index";
import { useRelationStore } from "../stores/use-relation-store";
export const useActions = ({ close = () => null, item }) => {
const colors = useThemeStore((state) => state.colors);
@@ -448,20 +449,20 @@ export const useActions = ({ close = () => null, item }) => {
: "This tag will be removed from all notes.",
positivePress: async () => {
const routes = [];
routes.push(
"TaggedNotes",
"ColoredNotes",
"Notes",
"NotesPage",
"Reminders",
"Favorites"
);
if (item.type === "reminder") {
await db.reminders.remove(item.id);
routes.push("Reminders");
} else {
await db.tags.remove(item.id);
useTagStore.getState().setTags();
routes.push(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"Tags"
);
routes.push("Tags");
}
Navigation.queueRoutesForUpdate(...routes);
},
@@ -832,11 +833,48 @@ export const useActions = ({ close = () => null, item }) => {
title: "Add",
type: "accent",
onPress: () => ReminderSheet.present(null, item, true),
icon:"plus"
icon: "plus"
}
});
},
close: false
},
{
name: "ReminderOnOff",
title: !item.disabled ? "Turn off reminder" : "Turn on reminder",
icon: !item.disabled ? "bell-off-outline" : "bell",
func: async () => {
close();
await db.reminders.add({
...item,
disabled: !item.disabled
});
Notifications.scheduleNotification(item);
useRelationStore.getState().update();
Navigation.queueRoutesForUpdate(
"TaggedNotes",
"ColoredNotes",
"TopicNotes",
"Favorites",
"Notes",
"NotesPage",
"Reminders"
);
}
},
{
name: "ReferencedIn",
title: "References",
icon: "link",
func: async () => {
close();
RelationsList.present({
reference: item,
referenceType: "note",
title: "Referenced in",
relationType: "to"
});
}
}
];

View File

@@ -45,7 +45,7 @@ import { DatabaseLogger, db } from "../common/database";
import { MMKV } from "../common/database/mmkv";
import {
eClearEditor,
eCloseProgressDialog,
eCloseSheet,
eOnLoadNote,
refreshNotesPage
} from "../utils/events";
@@ -219,7 +219,7 @@ export const useAppEvents = () => {
const onSyncComplete = useCallback(async () => {
initAfterSync();
setLastSynced(await db.lastSynced());
eSendEvent(eCloseProgressDialog, "sync_progress");
eSendEvent(eCloseSheet, "sync_progress");
}, [setLastSynced]);
const onUrlRecieved = useCallback(

View File

@@ -31,7 +31,7 @@ import {
ToastEvent
} from "../../../services/event-manager";
import PremiumService from "../../../services/premium";
import { eCloseProgressDialog } from "../../../utils/events";
import { eCloseSheet } from "../../../utils/events";
import { sleep } from "../../../utils/time";
import { editorController, editorState } from "./utils";
const FILE_SIZE_LIMIT = 500 * 1024 * 1024;
@@ -131,7 +131,7 @@ const file = async (fileOptions) => {
size: file.size
});
setTimeout(() => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
}, 1000);
} catch (e) {
ToastEvent.show({
@@ -147,7 +147,7 @@ const file = async (fileOptions) => {
const camera = async (options) => {
try {
await db.attachments.generateKey();
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(400);
launchCamera(
{
@@ -170,7 +170,7 @@ const camera = async (options) => {
const gallery = async (options) => {
try {
await db.attachments.generateKey();
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(400);
launchImageLibrary(
{

View File

@@ -48,7 +48,7 @@ import {
} from "../../services/event-manager";
import { ThemeStore, useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseProgressDialog } from "../../utils/events";
import { eCloseSheet } from "../../utils/events";
import { sanitizeFilename } from "../../utils/sanitizer";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
@@ -547,7 +547,7 @@ export const MFARecoveryCodes = ({
if (isSetup) {
onSuccess && onSuccess(method);
} else {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
}
}}
style={{
@@ -604,7 +604,7 @@ const MFASuccess = ({ recovery }: MFAStepProps) => {
type="accent"
width={250}
onPress={() => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
}}
style={{
borderRadius: 100,

View File

@@ -53,7 +53,7 @@ import { AndroidModule } from "../../utils";
import { getColorScheme, toggleDarkMode } from "../../utils/color-scheme/utils";
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
import {
eCloseProgressDialog,
eCloseSheet,
eCloseSimpleDialog,
eOpenAttachmentsDialog,
eOpenLoginDialog,
@@ -276,7 +276,7 @@ export const settingsGroups: SettingSection[] = [
await PremiumService.subscriptions.verify(
currentSubscription
);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
},
icon: "information-outline",
actionText: "Verify"
@@ -401,7 +401,7 @@ export const settingsGroups: SettingSection[] = [
description:
"Turns off syncing completely on this device. Any changes made will remain local only and new changes from your other devices won't sync to this device.",
type: "switch",
property: "disableSync",
property: "disableSync"
},
{
id: "sync-issues-fix",
@@ -416,11 +416,11 @@ export const settingsGroups: SettingSection[] = [
negativeText: "Cancel",
positiveText: "Start",
positivePress: async () => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
await sleep(300);
Progress.present();
Sync.run("global", true, true, () => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
});
}
});

View File

@@ -25,7 +25,7 @@ import RNFetchBlob from "rn-fetch-blob";
import { presentDialog } from "../components/dialog/functions";
import { DatabaseLogger, db } from "../common/database";
import storage from "../common/database/storage";
import { eCloseProgressDialog } from "../utils/events";
import { eCloseSheet } from "../utils/events";
import { sanitizeFilename } from "../utils/sanitizer";
import { sleep } from "../utils/time";
import { eSendEvent, presentSheet, ToastEvent } from "./event-manager";
@@ -130,7 +130,7 @@ async function presentBackupCompleteSheet(backupFilePath) {
},
{
action: async () => {
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
SettingsService.set({
showBackupCompleteSheet: false
});
@@ -173,7 +173,7 @@ async function run(progress, context) {
if (!backup) throw new Error("Backup returned empty.");
} catch (e) {
await sleep(300);
eSendEvent(eCloseProgressDialog);
eSendEvent(eCloseSheet);
ToastEvent.error(e, "Backup failed!");
return null;
}
@@ -216,11 +216,11 @@ async function run(progress, context) {
if (showBackupCompleteSheet) {
presentBackupCompleteSheet(backupFilePath);
} else {
progress && eSendEvent(eCloseProgressDialog);
progress && eSendEvent(eCloseSheet);
}
return backupFilePath;
} catch (e) {
progress && eSendEvent(eCloseProgressDialog);
progress && eSendEvent(eCloseSheet);
ToastEvent.error(e, "Backup failed!");
return null;
}

View File

@@ -23,9 +23,10 @@ import { RefObject } from "react";
import ActionSheet from "react-native-actions-sheet";
import Config from "react-native-config";
import {
eCloseSheet,
eHideToast,
eOnNoteEdited,
eOpenProgressDialog,
eOpenSheet,
eOpenVaultDialog,
eShowToast
} from "../utils/events";
@@ -97,7 +98,7 @@ export type PresentSheetOptions = {
| JSX.Element
| ((
ref: RefObject<ActionSheet>,
close?: () => void,
close?: (ctx?: string) => void,
update?: (props: PresentSheetOptions) => void
) => JSX.Element);
disableClosing: boolean;
@@ -116,7 +117,11 @@ export type PresentSheetOptions = {
};
export function presentSheet(data: Partial<PresentSheetOptions>) {
eSendEvent(eOpenProgressDialog, data);
eSendEvent(eOpenSheet, data);
}
export function hideSheet() {
eSendEvent(eCloseSheet);
}
export type ShowToastEvent = {

View File

@@ -28,7 +28,7 @@ import notifee, {
Event,
TriggerNotification
} from "@notifee/react-native";
import dayjs from "dayjs";
import dayjs, { Dayjs } from "dayjs";
import { Platform } from "react-native";
import { db } from "../common/database";
import { MMKV } from "../common/database/mmkv";
@@ -40,6 +40,8 @@ import { DDS } from "./device-detection";
import { eSendEvent } from "./event-manager";
import SettingsService from "./settings";
import { useSettingStore } from "../stores/use-setting-store";
import ReminderNotify from "../components/sheets/reminder-notify";
import { sleep } from "../utils/time";
export type Reminder = {
id: string;
@@ -47,37 +49,65 @@ export type Reminder = {
title: string;
description?: string;
priority: "silent" | "vibrate" | "urgent";
date?: number;
date: number;
mode: "repeat" | "once" | "permanent";
recurringMode: "week" | "month" | "day";
selectedDays: number[];
recurringMode?: "week" | "month" | "day";
selectedDays?: number[];
dateCreated: number;
dateModified: number;
localOnly?: boolean;
snoozeUntil?: number;
disabled?: boolean;
};
let pinned: DisplayedNotification[] = [];
/**
*
* @platform android
*/
async function getNextMonthlyReminderDate(
reminder: Reminder,
year: number
): Promise<Dayjs> {
const currentMonth = dayjs().month();
for (let i = currentMonth; i < 12; i++) {
for (const day of reminder.selectedDays as number[]) {
const date = dayjs(reminder.date).year(year).month(i).date(day);
if (date.daysInMonth() < day || dayjs().isAfter(date)) continue;
if (date.isBefore(dayjs())) continue;
console.log("Next reminder set on", date.year(),date.month(),date.date(),day);
return date;
}
}
return await getNextMonthlyReminderDate(reminder, dayjs().year() + 1);
}
const onEvent = async ({ type, detail }: Event) => {
const { notification, pressAction, input } = detail;
if (type === EventType.DELIVERED && Platform.OS === "android") {
const reminder = db.reminders?.reminder(notification?.id?.split("_")[0]);
if (reminder && reminder.recurringMode === "month") {
await db.init();
await db.notes?.init();
await scheduleNotification(reminder);
}
return;
}
if (type === EventType.PRESS) {
if (notification?.data?.type === "quickNote") return;
editorState().movedAway = false;
MMKV.removeItem("appState");
let noteId = notification?.id;
await db.init();
await db.notes?.init();
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;
}
const reminder = db.reminders?.reminder(notification.id?.split("_")[0]);
await sleep(1000);
ReminderNotify.present(reminder);
}
const noteId = notification?.id;
if (useNoteStore?.getState()?.loading === false) {
await db.init();
await db.notes?.init();
loadNote(noteId as string, false);
return;
}
@@ -124,11 +154,18 @@ const onEvent = async ({ type, detail }: Event) => {
}
};
async function scheduleNotification(reminder: Reminder, payload?: string) {
async function scheduleNotification(
reminder: Reminder | undefined,
payload?: string
) {
if (!reminder) return;
if (useSettingStore.getState().settings.disableReminderNotifications) return;
try {
const { title, description, priority } = reminder;
const triggers = getTriggers(reminder);
await clearAllPendingTriggersForId(reminder.id);
if (reminder.disabled) return;
const triggers = await getTriggers(reminder);
if (!triggers && reminder.mode === "permanent") {
displayNotification({
id: reminder.id,
@@ -139,10 +176,15 @@ async function scheduleNotification(reminder: Reminder, payload?: string) {
});
return;
}
await clearAllPendingTriggersForId(reminder.id);
if (!triggers) return;
for (const trigger of triggers) {
//@ts-ignore
console.log(reminder.title, dayjs(trigger.timestamp));
const iosProperties: { [name: string]: any } = {};
if (priority === "urgent") {
iosProperties["sound"] = "default";
}
const notif = await notifee.createTriggerNotification(
{
id: trigger.id,
@@ -169,8 +211,8 @@ async function scheduleNotification(reminder: Reminder, payload?: string) {
}
},
ios: {
sound: priority === "urgent" ? "default" : undefined,
interruptionLevel: priority === "silent" ? "passive" : "active"
interruptionLevel: priority === "silent" ? "passive" : "active",
...iosProperties
}
},
trigger
@@ -318,85 +360,132 @@ async function checkAndRequestPermissions() {
}
}
function getTriggers(
async function getTriggers(
reminder: Reminder
): (Trigger & { id: string })[] | undefined {
const { date, recurringMode, selectedDays, mode } = reminder;
): Promise<(Trigger & { id: string })[] | undefined> {
const { date, recurringMode, selectedDays, mode, snoozeUntil } = reminder;
const triggers: (Trigger & { id: string })[] = [];
if (snoozeUntil && snoozeUntil > Date.now()) {
triggers.push({
timestamp: snoozeUntil as number,
type: TriggerType.TIMESTAMP,
id: reminder.id + "_snz",
alarmManager: {
allowWhileIdle: true
}
});
}
switch (mode) {
case "once":
return [
{
timestamp: date as number,
type: TriggerType.TIMESTAMP,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
if (date < Date.now()) break;
triggers.push({
timestamp: date as number,
type: TriggerType.TIMESTAMP,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
];
});
break;
case "permanent":
return undefined;
case "repeat": {
switch (recurringMode) {
case "day":
return [
{
timestamp: date as number,
triggers.push({
timestamp: date as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.DAILY,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
});
break;
case "week":
if (!selectedDays) break;
triggers.concat(
selectedDays.length === 7
? [
{
timestamp: date as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.DAILY,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
}
]
: (selectedDays
.map((day) => ({
timestamp: dayjs(date)
.day(day)
.toDate()
.getTime() as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.WEEKLY,
id: `${reminder.id}_${day}`,
alarmManager: {
allowWhileIdle: true
}
}))
.filter(
(trigger) => trigger.timestamp > Date.now()
) as (Trigger & { id: string })[])
);
break;
case "month":
if (!selectedDays) break;
if (selectedDays.length === 31) {
triggers.concat([
{
timestamp: date as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.DAILY,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
}
]);
break;
}
if (Platform.OS === "ios") {
selectedDays
.map((day) => ({
timestamp: dayjs(date).date(day).toDate().getTime() as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.MONTHLY,
id: `${reminder.id}_${day}`,
alarmManager: {
allowWhileIdle: true
}
}))
.filter(
(trigger) => trigger.timestamp > Date.now()
) as (Trigger & { id: string })[];
} else {
const reminderDate = await getNextMonthlyReminderDate(
reminder,
dayjs().year()
);
triggers.push({
timestamp: reminderDate.toDate().getTime() as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.DAILY,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
}
];
case "week":
return selectedDays.length === 7
? [
{
timestamp: date as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.DAILY,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
}
]
: selectedDays.map((day) => ({
timestamp: dayjs(date).day(day).toDate().getTime() as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.WEEKLY,
id: `${reminder.id}_${day}`,
alarmManager: {
allowWhileIdle: true
}
}));
case "month":
return selectedDays.length === 31
? [
{
timestamp: date as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.DAILY,
id: reminder.id,
alarmManager: {
allowWhileIdle: true
}
}
]
: selectedDays.map((day) => ({
timestamp: dayjs(date).date(day).toDate().getTime() as number,
type: TriggerType.TIMESTAMP,
repeatFrequency: RepeatFrequency.WEEKLY,
id: `${reminder.id}_${day}`,
alarmManager: {
allowWhileIdle: true
}
}));
});
}
break;
}
}
}
return triggers;
}
async function unpinQuickNote() {

View File

@@ -117,9 +117,9 @@ export const eOpenPremiumStatusDialog = "549";
export const eClosePremiumStatusDialog = "550";
export const eOpenProgressDialog = "551";
export const eOpenSheet = "551";
export const eCloseProgressDialog = "552";
export const eCloseSheet = "552";
export const eOpenRestoreDialog = "553";

View File

@@ -16,118 +16,6 @@ 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 isBetween from "dayjs/plugin/isBetween";
import isToday from "dayjs/plugin/isToday";
import isTomorrow from "dayjs/plugin/isTomorrow";
import isYesterday from "dayjs/plugin/isYesterday";
import { Reminder } from "../../services/notifications";
import { WeekDayNames } from "../constants";
dayjs.extend(isTomorrow);
dayjs.extend(isYesterday);
dayjs.extend(isToday);
dayjs.extend(isBetween);
function isNext(time: number, type: "week" | "month") {
console.log(dayjs().endOf(type));
return (
dayjs(time).isAfter(dayjs().endOf(type)) &&
dayjs(time).isBetween(
dayjs().endOf(type),
dayjs(dayjs().endOf(type).add(1, "day")).endOf(type)
)
);
}
function isLast(time: number, type: "week" | "month") {
return (
dayjs(time).isBefore(dayjs().endOf(type)) &&
dayjs(time).isBetween(dayjs(time).startOf(type), dayjs().startOf(type))
);
}
function getUpcomingReminderTime(reminder: Reminder) {
const time = reminder.date;
const sorted = reminder.selectedDays.sort((a, b) => a - b);
// If all selected days have passed for current period, i.e week or month
if (
dayjs(time)
.day(sorted[sorted.length - 1])
.isBefore(dayjs())
) {
// select first selected day of next month/week;
return dayjs(time)
.day(sorted[0])
.add(1, reminder.recurringMode)
.toDate()
.getTime();
}
// Choose the nearest reminder time
// If not all selected days have passed yet.
for (const day of reminder.selectedDays) {
if (dayjs(time).day(day).isAfter(dayjs())) {
return dayjs(time).day(day).toDate().getTime();
}
}
return time;
}
export function formatReminderTime(reminder: Reminder) {
const { date } = reminder;
let time = date as number;
if (reminder.mode === "repeat") {
time = getUpcomingReminderTime(reminder) as number;
}
const isPast = dayjs(time).isBefore(dayjs());
if (dayjs(time).isTomorrow()) {
return `"Upcoming": Tomorrow, ${dayjs(time).format("hh:mm A")}`;
}
if (dayjs(time).isYesterday()) {
return `Last occurrence: Yesterday, ${dayjs(time).format("hh:mm A")}`;
}
if (dayjs(time).isToday()) {
return `${isPast ? "Last occurrence" : "Upcoming"}: Today, ${dayjs(
time
).format("hh:mm A")}`;
}
// if (isNext(time, "week")) {
// return `Next ${
// WeekDayNames[dayjs(time).day() as keyof typeof WeekDayNames]
// }, ${dayjs(time).format("hh:mm A")}`;
// }
// if (isLast(time, "week")) {
// return `Last ${
// WeekDayNames[dayjs(time).day() as keyof typeof WeekDayNames]
// }, ${dayjs(time).format("hh:mm A")}`;
// }
// if (isNext(time, "month")) {
// return `${WeekDayNames[dayjs(time).day() as keyof typeof WeekDayNames]}, ${
// dayjs(time).date() + nth(dayjs(time).date())
// } of next month, ${dayjs(time).format("hh:mm A")}`;
// }
return (
`${isPast ? "Last occurrence" : "Upcoming"}: ` +
dayjs(time).format("ddd, DD MMM,YYYY hh:mm A")
);
}
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));

View File

@@ -351,6 +351,8 @@ PODS:
- React-Core
- RNCPushNotificationIOS (1.10.1):
- React-Core
- RNDateTimePicker (6.6.0):
- React-Core
- RNDeviceInfo (8.7.1):
- React-Core
- RNExitApp (1.1.0):
@@ -365,6 +367,11 @@ PODS:
- React-Core
- RNKeychain (4.0.5):
- React
- RNNotifee (7.1.0):
- React-Core
- RNNotifee/NotifeeCore (= 7.1.0)
- RNNotifee/NotifeeCore (7.1.0):
- React-Core
- RNPrivacySnapshot (1.0.0):
- React-Core
- RNReanimated (2.13.0):
@@ -475,6 +482,7 @@ DEPENDENCIES:
- "RNCClipboard (from `../../node_modules/@react-native-clipboard/clipboard`)"
- "RNCMaskedView (from `../../node_modules/@react-native-masked-view/masked-view`)"
- "RNCPushNotificationIOS (from `../../node_modules/@react-native-community/push-notification-ios`)"
- "RNDateTimePicker (from `../../node_modules/@react-native-community/datetimepicker`)"
- RNDeviceInfo (from `../../node_modules/react-native-device-info`)
- RNExitApp (from `../../node_modules/react-native-exit-app`)
- RNFileViewer (from `../../node_modules/react-native-file-viewer`)
@@ -482,6 +490,7 @@ DEPENDENCIES:
- RNGestureHandler (from `../../node_modules/react-native-gesture-handler`)
- RNIap (from `../../node_modules/react-native-iap`)
- RNKeychain (from `../../node_modules/react-native-keychain`)
- "RNNotifee (from `../../node_modules/@notifee/react-native`)"
- RNPrivacySnapshot (from `../../node_modules/react-native-privacy-snapshot`)
- RNReanimated (from `../../node_modules/react-native-reanimated`)
- RNScreens (from `../../node_modules/react-native-screens`)
@@ -616,6 +625,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/@react-native-masked-view/masked-view"
RNCPushNotificationIOS:
:path: "../../node_modules/@react-native-community/push-notification-ios"
RNDateTimePicker:
:path: "../../node_modules/@react-native-community/datetimepicker"
RNDeviceInfo:
:path: "../../node_modules/react-native-device-info"
RNExitApp:
@@ -630,6 +641,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native-iap"
RNKeychain:
:path: "../../node_modules/react-native-keychain"
RNNotifee:
:path: "../../node_modules/@notifee/react-native"
RNPrivacySnapshot:
:path: "../../node_modules/react-native-privacy-snapshot"
RNReanimated:
@@ -721,6 +734,7 @@ SPEC CHECKSUMS:
RNCClipboard: 2834e1c4af68697089cdd455ee4a4cdd198fa7dd
RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a
RNCPushNotificationIOS: 87b8d16d3ede4532745e05b03c42cff33a36cc45
RNDateTimePicker: 818672afa85519722533d017b832ed09539d9ddb
RNDeviceInfo: aad3c663b25752a52bf8fce93f2354001dd185aa
RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
@@ -728,6 +742,7 @@ SPEC CHECKSUMS:
RNGestureHandler: b7a872907ee289ada902127f2554fa1d2c076122
RNIap: d248609d1b8937e63bd904e865c318e9b1457eff
RNKeychain: 840f8e6f13be0576202aefcdffd26a4f54bfe7b5
RNNotifee: 8527a2b9ffc6d0abfd907027e22962a809e39eee
RNPrivacySnapshot: 8eaf571478a353f2e5184f5c803164f22428b023
RNReanimated: f1b109fb8341505ace9d7d2eedd150da1686716b
RNScreens: 34cc502acf1b916c582c60003dc3089fa01dc66d

View File

@@ -194,3 +194,12 @@ function getUpcomingReminderTime(reminder) {
return relativeTime.valueOf();
}
export function getUpcomingReminder(reminders) {
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];
}