mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
mobile: minor improvements
This commit is contained in:
committed by
Abdullah Atta
parent
7ac8b478ed
commit
32467c36af
@@ -35,6 +35,7 @@ type DialogHeaderProps = {
|
||||
loading?: boolean;
|
||||
title?: string;
|
||||
type?: PressableButtonProps["type"];
|
||||
icon?: string
|
||||
};
|
||||
paragraphColor?: string;
|
||||
padding?: number;
|
||||
@@ -96,6 +97,7 @@ const DialogHeader = ({
|
||||
loading={button.loading}
|
||||
fontSize={13}
|
||||
title={button.title}
|
||||
icon={button.icon}
|
||||
type={button.type || "grayBg"}
|
||||
height={25}
|
||||
/>
|
||||
|
||||
@@ -35,6 +35,10 @@ import { IconButton } from "../../ui/icon-button";
|
||||
import { TimeSince } from "../../ui/time-since";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { getUpcomingReminder } from "../../../utils/time";
|
||||
import { formatReminderTime } from "../../../utils/time/index";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
import ReminderSheet from "../../sheets/reminder";
|
||||
|
||||
const navigateToTopic = (topic) => {
|
||||
TopicNotes.navigate(topic, true);
|
||||
@@ -86,6 +90,8 @@ const NoteItem = ({
|
||||
const compactMode = notesListMode === "compact";
|
||||
const attachmentCount = db.attachments?.ofNote(item.id, "all")?.length || 0;
|
||||
const notebooks = React.useMemo(() => getNotebook(item), [item]);
|
||||
const reminders = db.relations.from(item, "reminder");
|
||||
const current = getUpcomingReminder(reminders);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -154,6 +160,37 @@ const NoteItem = ({
|
||||
</Paragraph>
|
||||
) : null}
|
||||
|
||||
{current && current.date ? (
|
||||
<TouchableOpacity
|
||||
activeOpacity={1}
|
||||
onPress={() => {
|
||||
Properties.present(item);
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: colors.nav,
|
||||
borderRadius: 5,
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: 5,
|
||||
paddingVertical: 3,
|
||||
alignItems: "center",
|
||||
marginTop: 5,
|
||||
justifyContent: "flex-start",
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<Icon name="clock-outline" size={SIZE.md} color={colors.accent} />
|
||||
<Paragraph
|
||||
size={SIZE.xs + 1}
|
||||
color={colors.icon}
|
||||
style={{ marginLeft: 5 }}
|
||||
>
|
||||
{formatReminderTime(current)}
|
||||
</Paragraph>
|
||||
</>
|
||||
</TouchableOpacity>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
|
||||
@@ -104,7 +104,8 @@ const SelectionWrapper = ({
|
||||
overflow: "hidden",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: compactMode ? 8 : 12,
|
||||
borderRadius:isSheet ? 10 : 0
|
||||
borderRadius: isSheet ? 10 : 0,
|
||||
marginBottom: isSheet ? 12 : undefined
|
||||
}}
|
||||
>
|
||||
{item.type === "note" ? (
|
||||
|
||||
@@ -124,7 +124,8 @@ export const Properties = ({
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
alignSelf: "flex-start",
|
||||
marginBottom: 5
|
||||
marginBottom: 5,
|
||||
marginTop: !item.description ? 5 : 0
|
||||
}}
|
||||
>
|
||||
{item.date ? (
|
||||
@@ -249,7 +250,7 @@ Properties.present = (item, buttons = [], isSheet) => {
|
||||
}
|
||||
if (!props[0]) return;
|
||||
presentSheet({
|
||||
context:isSheet ? "local" : undefined,
|
||||
context: isSheet ? "local" : undefined,
|
||||
component: (ref, close) => (
|
||||
<Properties
|
||||
close={() => {
|
||||
|
||||
@@ -16,20 +16,25 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import React, { RefObject, useState } from "react";
|
||||
import React, { RefObject, useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import ActionSheet from "react-native-actions-sheet";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../../common/database";
|
||||
import {
|
||||
presentSheet,
|
||||
PresentSheetOptions
|
||||
PresentSheetOptions,
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import { Reminder } from "../../../services/notifications";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
import List from "../../list";
|
||||
import { useEffect } from "react";
|
||||
import { Reminder } from "../../../services/notifications";
|
||||
import { PressableButtonProps } from "../../ui/pressable";
|
||||
import SheetProvider from "../../sheet-provider";
|
||||
import { Button } from "../../ui/button";
|
||||
import { PressableButtonProps } from "../../ui/pressable";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
|
||||
type RelationsListProps = {
|
||||
actionSheetRef: RefObject<ActionSheet>;
|
||||
@@ -40,6 +45,7 @@ type RelationsListProps = {
|
||||
relationType: "to" | "from";
|
||||
title: string;
|
||||
button?: Button;
|
||||
onAdd: () => void;
|
||||
};
|
||||
|
||||
type Button = {
|
||||
@@ -47,6 +53,11 @@ type Button = {
|
||||
loading?: boolean | undefined;
|
||||
title?: string | undefined;
|
||||
type?: PressableButtonProps["type"];
|
||||
icon?: string;
|
||||
};
|
||||
|
||||
const IconsByType = {
|
||||
reminder: "bell"
|
||||
};
|
||||
|
||||
export const RelationsList = ({
|
||||
@@ -57,27 +68,68 @@ export const RelationsList = ({
|
||||
referenceType,
|
||||
relationType,
|
||||
title,
|
||||
button
|
||||
button,
|
||||
onAdd
|
||||
}: RelationsListProps) => {
|
||||
const relations = useRelationStore();
|
||||
const [items, setItems] = useState<Reminder[]>([]);
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
const hasNoReminders = !items || items.length === 0;
|
||||
|
||||
useEffect(() => {
|
||||
db.relations?.[relationType]?.(
|
||||
{ id: item?.id, type: item.type },
|
||||
referenceType
|
||||
).then((items) => setItems(items as any));
|
||||
}, [item?.id, item?.type, referenceType, relationType]);
|
||||
setItems(
|
||||
db.relations?.[relationType]?.(
|
||||
{ id: item?.id, type: item.type },
|
||||
referenceType
|
||||
) as any
|
||||
);
|
||||
}, [item?.id, item?.type, referenceType, relationType, relations.updater]);
|
||||
console.log(items);
|
||||
return (
|
||||
<View style={{ paddingHorizontal: 12, height: "100%" }}>
|
||||
<View
|
||||
style={{ paddingHorizontal: 12, height: hasNoReminders ? 300 : "100%" }}
|
||||
>
|
||||
<SheetProvider context="local" />
|
||||
<DialogHeader title={title} button={button} />
|
||||
<List
|
||||
listData={items}
|
||||
loading={false}
|
||||
type={referenceType}
|
||||
headerProps={null}
|
||||
isSheet={true}
|
||||
<DialogHeader
|
||||
title={title}
|
||||
button={hasNoReminders ? undefined : button}
|
||||
/>
|
||||
{hasNoReminders ? (
|
||||
<View
|
||||
style={{
|
||||
height: "85%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={IconsByType[referenceType as keyof typeof IconsByType]}
|
||||
size={60}
|
||||
color={colors.icon}
|
||||
/>
|
||||
<Paragraph>
|
||||
No {referenceType}s linked to this {item.type}.
|
||||
</Paragraph>
|
||||
<Button
|
||||
onPress={() => {
|
||||
onAdd?.();
|
||||
}}
|
||||
fontSize={SIZE.sm}
|
||||
// width="100%"
|
||||
type="inverted"
|
||||
icon="plus"
|
||||
title={`Add a ${referenceType}`}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<List
|
||||
listData={items}
|
||||
loading={false}
|
||||
type={referenceType}
|
||||
headerProps={null}
|
||||
isSheet={true}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -87,13 +139,15 @@ RelationsList.present = ({
|
||||
referenceType,
|
||||
relationType,
|
||||
title,
|
||||
button
|
||||
button,
|
||||
onAdd
|
||||
}: {
|
||||
reference: { id: string; type: string };
|
||||
referenceType: string;
|
||||
relationType: "to" | "from";
|
||||
title: string;
|
||||
button: Button;
|
||||
button?: Button;
|
||||
onAdd: () => void;
|
||||
}) => {
|
||||
presentSheet({
|
||||
component: (ref, close, update) => (
|
||||
@@ -106,6 +160,7 @@ RelationsList.present = ({
|
||||
relationType={relationType}
|
||||
title={title}
|
||||
button={button}
|
||||
onAdd={onAdd}
|
||||
/>
|
||||
)
|
||||
});
|
||||
@@ -21,23 +21,23 @@ import { Platform, ScrollView, View } from "react-native";
|
||||
import ActionSheet from "react-native-actions-sheet";
|
||||
import DateTimePickerModal from "react-native-modal-datetime-picker";
|
||||
import {
|
||||
presentSheet,
|
||||
PresentSheetOptions,
|
||||
ToastEvent
|
||||
ToastEvent,
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import { useThemeStore } from "../../../stores/use-theme-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
|
||||
import Notifications, { Reminder } from "../../../services/notifications";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import dayjs from "dayjs";
|
||||
import { db } from "../../../common/database";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { formatReminderTime } from "../../../utils/time";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../../common/database";
|
||||
import Notifications, { Reminder } from "../../../services/notifications";
|
||||
import { useReminderStore } from "../../../stores/use-reminder-store";
|
||||
import { formatReminderTime } from "../../../utils/time";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import DialogHeader from "../../dialog/dialog-header";
|
||||
type ReminderSheetProps = {
|
||||
actionSheetRef: RefObject<ActionSheet>;
|
||||
close?: () => void;
|
||||
@@ -62,7 +62,6 @@ const RecurringModes = {
|
||||
Week: "week",
|
||||
Month: "month"
|
||||
};
|
||||
|
||||
const WeekDays = new Array(7).fill(true);
|
||||
const MonthDays = new Array(31).fill(true);
|
||||
const WeekDayNames = {
|
||||
@@ -81,6 +80,9 @@ const ReminderNotificationModes = {
|
||||
Urgent: "urgent"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
export default function ReminderSheet({
|
||||
actionSheetRef,
|
||||
close,
|
||||
@@ -431,7 +433,7 @@ export default function ReminderSheet({
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginBottom: 12
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
{Object.keys(ReminderNotificationModes).map((mode) => (
|
||||
|
||||
@@ -57,7 +57,7 @@ import {
|
||||
} from "../utils/events";
|
||||
import { deleteItems } from "../utils/functions";
|
||||
import { sleep } from "../utils/time";
|
||||
import { RelationsList } from "../components/sheets/reminders-list/index";
|
||||
import { RelationsList } from "../components/sheets/relations-list/index";
|
||||
|
||||
export const useActions = ({ close = () => null, item }) => {
|
||||
const colors = useThemeStore((state) => state.colors);
|
||||
@@ -820,15 +820,23 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
name: "Reminders",
|
||||
title: "Reminders",
|
||||
icon: "bell",
|
||||
func: () => {
|
||||
func: async () => {
|
||||
close();
|
||||
RelationsList.present({
|
||||
reference: item,
|
||||
referenceType: "reminder",
|
||||
relationType: "from",
|
||||
title: "Reminders"
|
||||
title: "Reminders",
|
||||
onAdd: () => ReminderSheet.present(null, item, true),
|
||||
button: {
|
||||
title: "Add",
|
||||
type: "accent",
|
||||
onPress: () => ReminderSheet.present(null, item, true),
|
||||
icon:"plus"
|
||||
}
|
||||
});
|
||||
},
|
||||
close: true
|
||||
close: false
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ import { useDragState } from "../../settings/editor/state";
|
||||
import { EditorMessage, EditorProps, useEditorType } from "./types";
|
||||
import { EditorEvents, editorState } from "./utils";
|
||||
import { openLinkInBrowser } from "../../../utils/functions";
|
||||
import { RelationsList } from "../../../components/sheets/relations-list";
|
||||
import ReminderSheet from "../../../components/sheets/reminder";
|
||||
|
||||
export const EventTypes = {
|
||||
selection: "editor-event:selection",
|
||||
@@ -77,7 +79,8 @@ export const EventTypes = {
|
||||
properties: "editor-event:properties",
|
||||
fullscreen: "editor-event:fullscreen",
|
||||
link: "editor-event:link",
|
||||
contentchange: "editor-event:content-change"
|
||||
contentchange: "editor-event:content-change",
|
||||
reminders: "editor-event:reminders"
|
||||
};
|
||||
|
||||
const publishNote = async (editor: useEditorType) => {
|
||||
@@ -315,8 +318,32 @@ export const useEditorEvents = (
|
||||
title: editorMessage.value as string
|
||||
});
|
||||
break;
|
||||
|
||||
case EventTypes.reminders:
|
||||
if (!editor.note.current) {
|
||||
ToastEvent.show({
|
||||
heading: "Create a note first to add a reminder",
|
||||
type: "success"
|
||||
});
|
||||
return;
|
||||
}
|
||||
RelationsList.present({
|
||||
reference: editor.note.current as any,
|
||||
referenceType: "reminder",
|
||||
relationType: "from",
|
||||
title: "Reminders",
|
||||
onAdd: () =>
|
||||
ReminderSheet.present(undefined, editor.note.current as any, true)
|
||||
});
|
||||
break;
|
||||
case EventTypes.newtag:
|
||||
if (!editor.note.current) return;
|
||||
if (!editor.note.current) {
|
||||
ToastEvent.show({
|
||||
heading: "Create a note first to add a tag",
|
||||
type: "success"
|
||||
});
|
||||
return;
|
||||
}
|
||||
eSendEvent(eOpenTagsDialog, editor.note.current);
|
||||
break;
|
||||
case EventTypes.tag:
|
||||
|
||||
@@ -60,19 +60,31 @@ let pinned: DisplayedNotification[] = [];
|
||||
const onEvent = async ({ type, detail }: Event) => {
|
||||
const { notification, pressAction, input } = detail;
|
||||
if (type === EventType.PRESS) {
|
||||
if (notification?.data?.type !== "pinnedNote") return;
|
||||
if (notification?.data?.type === "quickNote") return;
|
||||
editorState().movedAway = false;
|
||||
MMKV.removeItem("appState");
|
||||
let noteId = notification?.id;
|
||||
if (notification?.data?.type === "reminder") {
|
||||
const notes = db.relations?.to(
|
||||
{ type: "reminder", id: notification.id?.split("_")[0] as string },
|
||||
"note"
|
||||
);
|
||||
if (!notes || notes.length === 0) return;
|
||||
if (notes && notes.length > 0) {
|
||||
noteId = notes[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
if (useNoteStore?.getState()?.loading === false) {
|
||||
await db.init();
|
||||
await db.notes?.init();
|
||||
loadNote(notification?.id as string, false);
|
||||
loadNote(noteId as string, false);
|
||||
return;
|
||||
}
|
||||
const unsub = useNoteStore.subscribe(
|
||||
(loading) => {
|
||||
if (loading === false) {
|
||||
loadNote(notification?.id as string, true);
|
||||
loadNote(noteId as string, true);
|
||||
}
|
||||
unsub();
|
||||
},
|
||||
@@ -184,6 +196,7 @@ function loadNote(id: string, jump: boolean) {
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
async function getChannelId(id: "silent" | "vibrate" | "urgent" | "default") {
|
||||
switch (id) {
|
||||
case "default":
|
||||
@@ -323,7 +336,7 @@ function getTriggers(
|
||||
];
|
||||
case "permanent":
|
||||
return undefined;
|
||||
case "repeat" : {
|
||||
case "repeat": {
|
||||
switch (recurringMode) {
|
||||
case "day":
|
||||
return [
|
||||
|
||||
32
apps/mobile/app/stores/use-relation-store.ts
Normal file
32
apps/mobile/app/stores/use-relation-store.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2022 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import create, { State } from "zustand";
|
||||
|
||||
export interface RelationStore extends State {
|
||||
updater: number;
|
||||
update: () => void;
|
||||
}
|
||||
|
||||
export const useRelationStore = create<RelationStore>((set, get) => ({
|
||||
updater: 0,
|
||||
update: () => {
|
||||
set({ updater: get().updater + 1 });
|
||||
}
|
||||
}));
|
||||
@@ -120,6 +120,15 @@ export function formatReminderTime(reminder: Reminder) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getUpcomingReminder(reminders: Reminder[]) {
|
||||
const sorted = reminders.sort((a, b) => {
|
||||
const d1 = a.mode === "repeat" ? getUpcomingReminderTime(a) : a.date;
|
||||
const d2 = a.mode === "repeat" ? getUpcomingReminderTime(b) : b.date;
|
||||
return !d1 || !d2 ? 0 : d1 - d2;
|
||||
});
|
||||
return sorted[0];
|
||||
}
|
||||
|
||||
export const sleep = (duration: number) =>
|
||||
new Promise((resolve) => setTimeout(() => resolve(true), duration));
|
||||
|
||||
|
||||
@@ -172,3 +172,5 @@ export interface GroupHeader
|
||||
extends Omit<Entity<"header">, "id" | "dateCreated" | "dateModified"> {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export type ItemReference = { id: string; type: string };
|
||||
|
||||
@@ -25,6 +25,7 @@ import ArrowULeftTopIcon from "mdi-react/ArrowULeftTopIcon";
|
||||
import ArrowURightTopIcon from "mdi-react/ArrowURightTopIcon";
|
||||
import FullscreenIcon from "mdi-react/FullscreenIcon";
|
||||
import MagnifyIcon from "mdi-react/MagnifyIcon";
|
||||
import BellIcon from "mdi-react/BellIcon";
|
||||
import React from "react";
|
||||
import { useSafeArea } from "../hooks/useSafeArea";
|
||||
import { EventTypes, Settings } from "../utils";
|
||||
@@ -77,7 +78,6 @@ export default function Header({
|
||||
position: "sticky",
|
||||
width: "100vw"
|
||||
}}
|
||||
|
||||
>
|
||||
{noHeader ? null : (
|
||||
<div
|
||||
@@ -90,7 +90,7 @@ export default function Header({
|
||||
height: 50,
|
||||
alignItems: "center"
|
||||
}}
|
||||
id='header'
|
||||
id="header"
|
||||
>
|
||||
{settings.deviceMode !== "mobile" && !settings.fullscreen ? (
|
||||
<div />
|
||||
@@ -211,6 +211,32 @@ export default function Header({
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
post(EventTypes.reminders);
|
||||
}}
|
||||
style={{
|
||||
borderWidth: 0,
|
||||
borderRadius: 100,
|
||||
color: "var(--nn_icon)",
|
||||
marginRight: 10,
|
||||
width: 39,
|
||||
height: 39,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
position: "relative"
|
||||
}}
|
||||
>
|
||||
<BellIcon
|
||||
size={25}
|
||||
style={{
|
||||
position: "absolute"
|
||||
}}
|
||||
color="var(--nn_pri)"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
editor?.commands.startSearch();
|
||||
|
||||
@@ -143,7 +143,8 @@ export const EventTypes = {
|
||||
properties: "editor-event:properties",
|
||||
fullscreen: "editor-event:fullscreen",
|
||||
link: "editor-event:link",
|
||||
contentchange: "editor-event:content-change"
|
||||
contentchange: "editor-event:content-change",
|
||||
reminders: "editor-event:reminders"
|
||||
} as const;
|
||||
|
||||
export function isReactNative(): boolean {
|
||||
|
||||
Reference in New Issue
Block a user