mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-02-24 04:00:59 +01:00
mobile: add disable/snooze function
This commit is contained in:
committed by
Abdullah Atta
parent
484bc02a28
commit
26ec8551f9
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ node_modules
|
||||
dist
|
||||
.DS_STORE
|
||||
nx-cloud.env
|
||||
.idea
|
||||
.idea
|
||||
.eslintcache
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
186
apps/mobile/app/components/sheets/reminder-notify/index.tsx
Normal file
186
apps/mobile/app/components/sheets/reminder-notify/index.tsx
Normal 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}
|
||||
/>
|
||||
)
|
||||
});
|
||||
};
|
||||
@@ -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
|
||||
}}
|
||||
>
|
||||
<>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user