mobile: push ui updates

This commit is contained in:
Ammar Ahmed
2026-06-29 11:27:03 +05:00
committed by Abdullah Atta
parent 7a2bf15015
commit eebd6542e7
48 changed files with 3729 additions and 3168 deletions

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { KeyboardTypeOptions, TextInput } from "react-native";
import { KeyboardTypeOptions, TextInput, TextInputProps } from "react-native";
import { eSendEvent } from "../../services/event-manager";
import { eCloseSimpleDialog, eOpenSimpleDialog } from "../../utils/events";
import { ButtonProps } from "../ui/button";
@@ -58,6 +58,7 @@ export type DialogInfo = {
validators: FieldValidator[];
defaultValue?: string;
ref: RefObject<TextInput | null>;
inputProps?: TextInputProps;
}[];
onFormSubmit?: (form: FormRef) => Promise<boolean>;
};

View File

@@ -39,7 +39,7 @@ import { defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { sleep } from "../../utils/time";
import { Toast } from "../toast";
import { Button } from "../ui/button";
import { Checkbox } from "../ui/checkbox";
import Input from "../ui/input";
import { FormInput, type FormRef } from "../ui/input/form-input";
import { Notice } from "../ui/notice";
@@ -233,6 +233,7 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
>
{dialogInfo.form.items.map((item, index) => (
<FormInput
{...item.inputProps}
key={item.name}
fwdRef={item.ref}
name={item.name}
@@ -289,29 +290,16 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
) : null}
{dialogInfo.check ? (
<>
<Button
onPress={() => {
setChecked(!checked);
}}
icon={
checked
? "check-circle-outline"
: "checkbox-blank-circle-outline"
}
iconColor={
checked ? colors.secondary.icon : colors.primary.icon
}
style={{
justifyContent: "flex-start"
}}
height={35}
iconSize={20}
width="100%"
title={dialogInfo.check.info}
type={checked ? dialogInfo.check.type || "plain" : "plain"}
/>
</>
<Checkbox
checked={checked}
onPress={() => {
setChecked(!checked);
}}
title={dialogInfo.check.info}
style={{
marginTop: -Spacing.LEVEL_1
}}
/>
) : null}
</View>
<DialogButtons

View File

@@ -107,7 +107,6 @@ export const Header = ({
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
marginBottom: Spacing.LEVEL_3,
marginTop: Platform.OS === "android" ? 5 : 0
}}
>

View File

@@ -71,6 +71,7 @@ export const SearchResult = (props: SearchResultProps) => {
}}
onLongPress={async () => {
const note = await db.notes.note(props.item.id);
if (!note) return;
Properties.present(note);
}}
onPress={async () => openNote()}
@@ -151,6 +152,7 @@ export const SearchResult = (props: SearchResultProps) => {
}}
onLongPress={async () => {
const note = await db.notes.note(props.item.id);
if (!note) return;
Properties.present(note);
}}
onPress={() => {

View File

@@ -24,7 +24,6 @@ import { AppFontSize } from "../../utils/size";
import Paragraph from "../ui/typography/paragraph";
import { getFormattedDate } from "@notesnook/common";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../utils/styles";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import { db } from "../../common/database";
import { Item, Note } from "@notesnook/core";
@@ -60,7 +59,11 @@ export const DateMeta = ({ item }: { item: Item }) => {
alignItems: "center"
}}
>
<View>
<View
style={{
gap: Spacing.LEVEL_0
}}
>
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
{strings.dateDescFromKey(
key as
@@ -73,7 +76,7 @@ export const DateMeta = ({ item }: { item: Item }) => {
</Paragraph>
<Paragraph
size={AppFontSize.xs}
color={colors.primary.paragraph}
color={colors.primary.heading}
fontFamily="MEDIUM"
onPress={
item.type !== "note"
@@ -127,9 +130,6 @@ export const DateMeta = ({ item }: { item: Item }) => {
<View
style={{
borderTopWidth: 1,
borderColor: colors.primary.border,
paddingVertical: Spacing.LEVEL_2,
flexDirection: "row",
gap: Spacing.LEVEL_2
}}

View File

@@ -20,7 +20,7 @@ import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { View } from "react-native";
import { FlatList } from "react-native-actions-sheet";
import { ScrollView } from "react-native-actions-sheet";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
import {
@@ -54,17 +54,53 @@ import Navigation from "../../services/navigation";
import { useIsFeatureAvailable } from "@notesnook/common";
import PaywallSheet from "../sheets/paywall";
import { useSettingStore } from "../../stores/use-setting-store";
import { Action, useActions } from "../../hooks/use-actions";
import {
Color,
Note,
Notebook,
Reminder,
Tag,
TrashItem
} from "@notesnook/core";
export const Properties = ({ close = () => {}, item, buttons = [] }) => {
export type PropertiesItem =
| Note
| Notebook
| Tag
| Color
| Reminder
| TrashItem;
export const Properties = ({
close,
item,
buttons = []
}: {
close?: (ctx?: string | undefined) => void;
item: PropertiesItem;
buttons: Action[];
}) => {
const { colors } = useThemeColors();
const colorFeature = useIsFeatureAvailable("colors");
const [noteNotebooks, setNoteNotebooks] = useState([]);
const [tags, setTags] = useState([]);
const [noteNotebooks, setNoteNotebooks] = useState<Notebook[]>([]);
const [tags, setTags] = useState<Tag[]>([]);
const [visible, setVisible] = useState(false);
const colorNotes = useMenuStore((state) => state.colorNotes);
const actions = useActions({
item,
close: () => {
close?.();
}
});
const editAction = actions.find(
(action) => action.id === "edit-notebook" || action.id === "rename-tag"
);
useEffect(() => {
async function getNotebooks() {
let filteredNotebooks = await db.relations.to(item, "notebook").resolve();
const filteredNotebooks = await db.relations
.to(item, "notebook")
.resolve();
return filteredNotebooks || [];
}
if (item.type === "note") {
@@ -87,7 +123,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
}
return (
<FlatList
<ScrollView
keyboardShouldPersistTaps="always"
keyboardDismissMode="none"
style={{
@@ -95,39 +131,39 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
borderBottomRightRadius: DDS.isLargeTablet() ? 10 : 1,
borderBottomLeftRadius: DDS.isLargeTablet() ? 10 : 1,
maxHeight: "100%",
paddingTop: Spacing.LEVEL_3
paddingTop: Spacing.LEVEL_2
}}
nestedScrollEnabled
bounces={false}
data={[0]}
keyExtractor={() => "properties-scroll-item"}
renderItem={() => (
>
<View>
{item.type === "note" ? (
<ColorPicker
visible={visible}
setVisible={setVisible}
onColorAdded={async (color) => {
await db.relations.to(item, "color").unlink();
await db.relations.add(color, item);
useRelationStore.getState().update();
useMenuStore.getState().setColorNotes();
Navigation.queueRoutesForUpdate();
sendItemUpdateEvent(color.id, "color");
eSendEvent(refreshNotesPage);
}}
/>
) : null}
<View
style={{
gap: Spacing.LEVEL_1
paddingHorizontal: Spacing.LEVEL_3
}}
>
{item.type === "note" ? (
<ColorPicker
visible={visible}
setVisible={setVisible}
onColorAdded={async (color) => {
await db.relations.to(item, "color").unlink();
await db.relations.add(color, item);
useRelationStore.getState().update();
useMenuStore.getState().setColorNotes();
Navigation.queueRoutesForUpdate();
sendItemUpdateEvent(color.id, "color");
eSendEvent(refreshNotesPage);
}}
/>
) : null}
<View
style={{
paddingHorizontal: Spacing.LEVEL_3
gap: Spacing.LEVEL_1,
paddingBottom: Spacing.LEVEL_3
}}
>
<View>
{item.type === "note" && (noteNotebooks.length || tags.length) ? (
<View
style={{
flexDirection: "row",
@@ -183,95 +219,101 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
) : null
)}
</View>
) : null}
<View
style={{
flexDirection: "row",
justifyContent: "space-between"
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "space-between"
alignItems: "center",
flexShrink: 1,
gap: Spacing.LEVEL_1
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
flexShrink: 1,
gap: Spacing.LEVEL_1
}}
>
{item.type === "color" ? (
<Pressable
type="accent"
accentColor={item.colorCode}
accentText={colors.static.white}
style={{
width: 8,
height: 8,
borderRadius: 100
}}
/>
) : item.type === "tag" ? (
<AppIcon
name="shopping-mode"
iconFamily="evilicons"
size={AppFontSize.lg}
color={colors.primary.icon}
/>
) : null}
{item.type === "color" ? (
<Pressable
type="accent"
accentColor={item.colorCode}
accentText={colors.static.white}
style={{
width: 8,
height: 8,
borderRadius: 100
}}
/>
) : null}
<Heading size={AppFontSize.xl}>{item.title}</Heading>
</View>
<Heading size={AppFontSize.xl}>{item.title}</Heading>
{item.type === "note" ? (
{editAction ? (
<IconButton
name="square-out"
name="edit-pencil"
iconFamily="notesnook"
type="plain"
color={colors.primary.icon}
size={AppFontSize.lg}
style={{
alignSelf: "flex-start"
}}
onPress={() => {
close();
eSendEvent(eOnLoadNote, {
item: item,
newTab: true
});
if (!DDS.isTab) {
fluidTabsRef.current?.goToPage("editor");
}
}}
size={AppFontSize.md}
onPress={editAction.onPress}
/>
) : null}
</View>
{(item.type === "notebook" || item.type === "reminder") &&
item.description ? (
<Paragraph>{item.description}</Paragraph>
) : null}
{item.type === "reminder" ? (
<ReminderTime
reminder={item}
{item.type === "note" ? (
<IconButton
name="square-out"
iconFamily="notesnook"
type="plain"
color={colors.primary.icon}
size={AppFontSize.lg}
style={{
justifyContent: "flex-start",
borderWidth: 0,
alignSelf: "flex-start",
backgroundColor: "transparent",
paddingHorizontal: 0,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
alignSelf: "flex-start"
}}
onPress={() => {
close?.();
eSendEvent(eOnLoadNote, {
item: item,
newTab: true
});
if (!DDS.isTab) {
fluidTabsRef.current?.goToPage("editor");
}
}}
fontSize={AppFontSize.xs}
/>
) : null}
</View>
<DateMeta item={item} />
{item.type === "note" && colorNotes.length > 0 ? (
<Tags close={close} item={item} />
{(item.type === "notebook" || item.type === "reminder") &&
item.description ? (
<Paragraph>{item.description}</Paragraph>
) : null}
{item.type === "reminder" ? (
<ReminderTime
reminder={item}
style={{
justifyContent: "flex-start",
borderWidth: 0,
alignSelf: "flex-start",
backgroundColor: "transparent",
paddingHorizontal: 0,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
fontSize={AppFontSize.xs}
/>
) : null}
</View>
<DateMeta item={item} />
{item.type === "note" && colorNotes.length > 0 ? (
<Tags close={close} item={item} />
) : null}
{item.type === "note" ? (
<View
style={{
flexDirection: "row",
@@ -286,7 +328,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
<Button
onPress={async () => {
ManageTags.present([item.id]);
close();
close?.();
}}
buttonType={{
text: colors.primary.paragraph
@@ -333,37 +375,54 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
/>
)}
</View>
</View>
) : null}
</View>
{/* {item.type === "note" ? (
<Notebooks note={item} close={close} />
) : null} */}
<Items
item={item}
buttons={buttons}
close={() => {
close();
<View
style={{
paddingHorizontal: Spacing.LEVEL_3
}}
>
<View
style={{
marginVertical: Spacing.LEVEL_3,
width: "100%",
borderBottomWidth: 1,
borderColor: colors.primary.separator
}}
/>
{DDS.isTab ? (
<View
style={{
height: 20
}}
/>
) : null}
<SheetProvider context="properties" />
<Dialog context="properties" />
</View>
)}
/>
<Items
item={item}
buttons={buttons}
actions={actions}
close={() => {
close?.();
}}
/>
{DDS.isTab ? (
<View
style={{
height: 20
}}
/>
) : null}
<SheetProvider context="properties" />
<Dialog context="properties" />
</View>
</ScrollView>
);
};
Properties.present = async (item, isSheet, buttons = []) => {
Properties.present = async (
item: PropertiesItem,
isSheet: boolean = false,
buttons: Action[] = []
) => {
if (!item) return;
let type = item?.type;
const type = item?.type;
let dbItem;
switch (type) {
case "trash":
@@ -387,15 +446,12 @@ Properties.present = async (item, isSheet, buttons = []) => {
}
}
if (!dbItem) return;
presentSheet({
context: isSheet ? "local" : undefined,
component: (ref, close) => (
<Properties
close={close}
actionSheetRef={ref}
item={dbItem}
buttons={buttons}
/>
<Properties close={close} item={dbItem} buttons={buttons} />
)
});
};

View File

@@ -18,19 +18,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Item } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { Dimensions, View } from "react-native";
import SwiperFlatList from "react-native-swiper-flatlist";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { Action, ActionId, useActions } from "../../hooks/use-actions";
import { Action, ActionId } from "../../hooks/use-actions";
import { useStoredRef } from "../../hooks/use-stored-ref";
import { DDS } from "../../services/device-detection";
import { useSettingStore } from "../../stores/use-setting-store";
import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import AppIcon from "../ui/AppIcon";
import { Button } from "../ui/button";
import { Pressable } from "../ui/pressable";
import Paragraph from "../ui/typography/paragraph";
import { Radius, Spacing } from "../../common/design/spacing";
@@ -64,10 +63,18 @@ const BOTTOM_BAR_ITEMS: ActionId[] = [
"trash"
];
const PREFERENCE_ITEMS: ActionId[] = [
"add-shortcut",
"pin",
"default-notebook",
"default-tag",
"default-homepage"
];
const COLUMN_BAR_ITEMS: ActionId[] = [
"select",
"add-notebook",
"edit-notebook",
// "edit-notebook",
"move-notes",
"move-notebook",
"edit-reminder",
@@ -79,7 +86,7 @@ const COLUMN_BAR_ITEMS: ActionId[] = [
"add-shortcut",
"reorder",
"rename-color",
"rename-tag",
// "rename-tag",
"launcher-shortcut",
"copy-id",
"copy-link",
@@ -91,11 +98,13 @@ const COLUMN_BAR_ITEMS: ActionId[] = [
export const Items = ({
item,
close,
buttons
buttons,
actions
}: {
item: Item;
close: () => void;
buttons: Action[];
actions: Action[];
}) => {
const { colors } = useThemeColors();
const topBarSorting = useStoredRef<{ [name: string]: number }>(
@@ -103,14 +112,12 @@ export const Items = ({
{}
);
const dimensions = useSettingStore((state) => state.dimensions);
const actions = useActions({ item, close });
const selectedActions = actions.filter((i) => !i.hidden);
const deviceMode = useSettingStore((state) => state.deviceMode);
const width = Math.min(dimensions.width, 600);
const shouldShrink =
Dimensions.get("window").fontScale > 1 &&
Dimensions.get("window").width < 450;
Dimensions.get("window").fontScale > 1 && dimensions.width < 450;
const columnItemsCount = deviceMode === "tablet" ? 7 : shouldShrink ? 4 : 5;
@@ -136,16 +143,23 @@ export const Items = ({
BOTTOM_BAR_ITEMS.indexOf(a.id) > BOTTOM_BAR_ITEMS.indexOf(b.id) ? 1 : -1
);
const preferenceItems = selectedActions
.filter((item) => PREFERENCE_ITEMS.indexOf(item.id) > -1)
.sort((a, b) =>
PREFERENCE_ITEMS.indexOf(a.id) > PREFERENCE_ITEMS.indexOf(b.id) ? 1 : -1
);
const columnItems = selectedActions
.filter((item) => COLUMN_BAR_ITEMS.indexOf(item.id) > -1)
.filter(
(item) =>
COLUMN_BAR_ITEMS.indexOf(item.id) > -1 &&
PREFERENCE_ITEMS.indexOf(item.id) === -1
)
.sort((a, b) =>
COLUMN_BAR_ITEMS.indexOf(a.id) > COLUMN_BAR_ITEMS.indexOf(b.id) ? 1 : -1
);
const topBarItemHeight = Math.min(
(width - (topBarItems.length * 10 + 14)) / topBarItems.length,
60
);
const actionItems = [...buttons, ...columnItems];
const renderRowItem = React.useCallback(
({ item }: { item: Action }) => (
@@ -170,9 +184,10 @@ export const Items = ({
marginBottom: 6
}}
>
<Icon
<AppIcon
allowFontScaling
name={item.icon}
iconFamily="notesnook"
size={
DDS.isTab
? AppFontSize.xxl
@@ -208,33 +223,79 @@ export const Items = ({
]
);
const renderColumnItem = React.useCallback(
const renderPreferenceItem = React.useCallback(
(item: Action) => (
<Button
<Pressable
key={item.id}
buttonType={{
text: item.checked
? item.activeColor || colors.primary.accent
: item.id === "delete" || item.id === "trash"
? colors.error.paragraph
: colors.primary.paragraph
}}
testID={"icon-" + item.id}
onPress={item.onPress}
title={item.title}
icon={item.icon}
type={item.checked ? "inverted" : "plain"}
fontSize={AppFontSize.sm}
type={item.checked ? "shade" : "transparent"}
testID={"icon-" + item.id}
style={{
borderRadius: 0,
justifyContent: "flex-start",
alignSelf: "flex-start",
width: "100%",
opacity: item.locked ? 0.5 : 1
width: "48.5%",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
gap: Spacing.LEVEL_1,
paddingVertical: Spacing.LEVEL_2,
paddingHorizontal: Spacing.LEVEL_2,
borderRadius: Radius.XS,
borderWidth: item.checked ? 0 : 1,
borderColor: colors.primary.border,
opacity: item.locked ? 0.7 : 1
}}
/>
>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: Spacing.LEVEL_1,
flexShrink: 1
}}
>
<AppIcon
name={item.icon}
iconFamily="notesnook"
allowFontScaling
size={AppFontSize.md}
color={item.checked ? colors.primary.icon : colors.secondary.icon}
/>
<Paragraph
numberOfLines={1}
fontSize="XS"
fontFamily="MEDIUM"
color={
item.checked
? colors.primary.paragraph
: colors.secondary.paragraph
}
style={{ flexShrink: 1 }}
>
{item.title}
</Paragraph>
</View>
<AppIcon
name={item.checked ? "toggle-on" : "toggle-off"}
iconFamily="notesnook"
size={16}
color={
item.checked
? [colors.primary.accent, colors.primary.background]
: [colors.disabled.icon, colors.primary.background]
}
/>
</Pressable>
),
[colors.error.paragraph, colors.primary.accent, colors.primary.paragraph]
[
colors.disabled.icon,
colors.primary.accent,
colors.primary.background,
colors.primary.border,
colors.primary.icon,
colors.primary.paragraph,
colors.secondary.icon,
colors.secondary.paragraph
]
);
const renderTopBarItem = React.useCallback(
@@ -274,8 +335,9 @@ export const Items = ({
paddingHorizontal: Spacing.LEVEL_2
}}
>
<Icon
<AppIcon
name={item.icon}
iconFamily="notesnook"
allowFontScaling
size={16}
color={
@@ -351,11 +413,7 @@ export const Items = ({
};
return (
<View
style={{
gap: DefaultAppStyles.GAP
}}
>
<View>
{item.type === "note" ? (
<>
<View>
@@ -416,9 +474,61 @@ export const Items = ({
</View>
</>
) : (
<View>
{buttons.map(renderColumnItem)}
{columnItems.map(renderColumnItem)}
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP
}}
>
{preferenceItems.length > 0 ? (
<View style={{ gap: Spacing.LEVEL_2 }}>
<Paragraph
fontFamily="MEDIUM"
fontSize="SM"
color={colors.secondary.paragraph}
>
{strings.preferences()}
</Paragraph>
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
gap: Spacing.LEVEL_1
}}
>
{preferenceItems.map(renderPreferenceItem)}
</View>
</View>
) : null}
<View
style={{
marginVertical: Spacing.LEVEL_3,
width: "100%",
borderBottomWidth: 1,
borderColor: colors.primary.separator
}}
/>
{actionItems.length > 0 ? (
<View style={{ gap: Spacing.LEVEL_2 }}>
<Paragraph
fontFamily="MEDIUM"
fontSize="SM"
color={colors.secondary.paragraph}
>
{strings.actionsHeading()}
</Paragraph>
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
gap: Spacing.LEVEL_1
}}
>
{actionItems.map((item) => renderRowItem({ item }))}
</View>
</View>
) : null}
</View>
)}
</View>

View File

@@ -31,7 +31,7 @@ import { Properties } from "../properties";
import { useSideBarDraggingStore } from "./dragging-store";
import { MenuItem } from "./menu-item";
import { Default_Drag_Action } from "../../hooks/use-actions";
import { Spacing } from "../../common/design/spacing";
export const ColorSection = React.memo(
function ColorSection() {

View File

@@ -49,6 +49,7 @@ import { isFeatureAvailable } from "@notesnook/common";
import PaywallSheet from "../sheets/paywall";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { Spacing } from "../../common/design/spacing";
import { createFormRef, validators } from "../ui/input/form-input";
/**
* Simple Tab View Implementation for the Side bar
@@ -428,25 +429,34 @@ const TabBar = (props: SimpleTabBarProps) => {
}
presentDialog({
title: strings.addTag(),
inputLabel: "Enter title",
inputPlaceholder: "eg. journal",
input: true,
positiveText: "Add",
positivePress: async (tag) => {
if (tag) {
await db.tags.add({
title: tag
});
useTagStore.getState().refresh();
return true;
form: {
formRef: createFormRef({
title: ""
}),
items: [
{
label: strings.enterTitle(),
name: "title",
placeholder: "eg. journal",
ref: React.createRef(),
validators: [validators.required(strings.allFieldsRequired())]
}
],
onFormSubmit: async (form) => {
try {
if (!form.validate()) return false;
await db.tags.add({
title: form.getValue("title").trim()
});
useTagStore.getState().refresh();
return true;
} catch (e) {
form.setError("title", (e as Error).message);
return false;
}
}
ToastManager.show({
context: "local",
type: "error",
message: strings.allFieldsRequired()
});
return false;
}
},
positiveText: strings.add()
});
}
}}

View File

@@ -175,7 +175,7 @@ export function MenuItem({
item.icon === "crown"
? colors.static.yellow
: isFocused
? colors.primary.icon
? colors.selected.icon
: colors.secondary.icon
}
size={AppFontSize.md}
@@ -196,7 +196,7 @@ export function MenuItem({
<Paragraph
fontSize="XS"
color={
isFocused ? colors.primary.paragraph : colors.secondary.paragraph
isFocused ? colors.selected.paragraph : colors.secondary.paragraph
}
>
{menuItemCount}

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect } from "react";
import { View } from "react-native";
import { View, ViewStyle } from "react-native";
import { StoreApi, UseBoundStore } from "zustand";
import { useTotalNotes } from "../../hooks/use-db-item";
import {
@@ -38,6 +38,7 @@ import { useRelationStore } from "../../stores/use-relation-store";
import { Radius, Spacing } from "../../common/design/spacing";
import Heading from "../ui/typography/heading";
import { AddNotebookSheet } from "../sheets/add-notebook";
import { strings } from "@notesnook/intl";
export const NotebookItem = ({
index,
@@ -53,7 +54,10 @@ export const NotebookItem = ({
onLongPress,
onAddNotebook,
canDisableSelectionMode,
disableExpand
disableExpand,
hideNoteCount,
style,
subNotebookButtonStyle
}: {
index: number;
item: TreeItem;
@@ -69,6 +73,9 @@ export const NotebookItem = ({
onAddNotebook?: () => void;
canDisableSelectionMode?: boolean;
disableExpand?: boolean;
hideNoteCount?: boolean;
style?: ViewStyle;
subNotebookButtonStyle?: ViewStyle;
}) => {
const notebook = item.notebook;
const isFocused = focused;
@@ -96,207 +103,222 @@ export const NotebookItem = ({
}, [item.notebook.id, notebook.id, onItemUpdate]);
const itemPadding =
item.depth === 0 ? undefined : item.depth < 6 ? 15 * item.depth : 15 * 5;
item.depth === 0
? undefined
: item.depth < 6
? Spacing.LEVEL_2 * item.depth
: Spacing.LEVEL_2 * 5;
return (
<View
style={{
paddingLeft: itemPadding,
width: "100%",
opacity: item.disabled ? 0.5 : 1,
paddingBottom: Spacing.LEVEL_0
opacity: item.disabled ? 0.5 : 1
}}
>
{item.depth > 0 ? (
<View
style={{
height: "100%",
width: 1,
backgroundColor: colors.primary.border,
top: 0,
bottom: 0,
position: "absolute",
left: itemPadding
}}
/>
) : null}
<Pressable
type={isFocused || selected ? "selected" : "transparent"}
onLongPress={onLongPress}
testID={`notebook-item-${item.depth}-${index}`}
onPress={async () => {
if (selectionEnabled) {
const state = selectionStore.getState();
if (selected) {
state.markAs(item.notebook, "deselected");
return;
}
if (!state.multiSelect) {
const keys = Object.keys(state.selection);
const nextState: any = {};
for (const key in keys) {
nextState[key] = !state.initialState[key]
? undefined
: "deselected";
}
state.setSelection({
[item.notebook.id]: "selected",
...nextState
});
} else {
state.markAs(item.notebook, "selected");
}
if (
selectionStore.getState().getSelectedItemIds().length === 0 &&
canDisableSelectionMode
) {
selectionStore.setState({
enabled: false
});
}
} else {
onPress?.();
}
}}
<View
style={{
justifyContent: "space-between",
width: "100%",
alignItems: "center",
flexDirection: "row",
borderRadius: Radius.XS,
paddingVertical: Spacing.LEVEL_1,
paddingHorizontal: Spacing.LEVEL_1,
marginBottom:
expanded && item.hasChildren ? Spacing.LEVEL_0 : undefined
flexDirection: "row"
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: Spacing.LEVEL_1
}}
>
{item.depth === 0 ? (
<AppIcon
size={AppFontSize.md}
color={
selected || isFocused
? colors.selected.icon
: colors.primary.icon
{item.depth > 0 ? (
<View
style={{
height: "100%",
width: 1,
backgroundColor: colors.primary.border,
marginRight: Spacing.LEVEL_1
}}
/>
) : null}
<Pressable
type={isFocused || selected ? "selected" : "transparent"}
onLongPress={onLongPress}
testID={`notebook-item-${item.depth}-${index}`}
onPress={async () => {
if (selectionEnabled) {
const state = selectionStore.getState();
if (selected) {
state.markAs(item.notebook, "deselected");
return;
}
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
style={{
borderRadius: defaultBorderRadius
}}
iconFamily="notesnook"
name={"bookmark"}
/>
) : null}
<Paragraph
color={
isFocused ? colors.selected.paragraph : colors.secondary.heading
if (!state.multiSelect) {
const keys = Object.keys(state.selection);
const nextState: any = {};
for (const key in keys) {
nextState[key] = !state.initialState[key]
? undefined
: "deselected";
}
state.setSelection({
[item.notebook.id]: "selected",
...nextState
});
} else {
state.markAs(item.notebook, "selected");
}
if (
selectionStore.getState().getSelectedItemIds().length === 0 &&
canDisableSelectionMode
) {
selectionStore.setState({
enabled: false
});
}
} else {
onPress?.();
}
size={AppFontSize.sm}
>
{notebook?.title}
</Paragraph>
</View>
<View
}}
style={{
gap: Spacing.LEVEL_1,
flexDirection: "row",
justifyContent: "space-between",
flexShrink: 1,
alignItems: "center",
justifyContent: "center"
flexDirection: "row",
borderRadius: Radius.XS,
paddingVertical: Spacing.LEVEL_1,
paddingHorizontal: Spacing.LEVEL_1,
marginBottom:
expanded && item.hasChildren ? Spacing.LEVEL_0 : undefined,
...style
}}
>
{selectionEnabled ? (
<View
style={{
justifyContent: "center",
alignItems: "center"
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: Spacing.LEVEL_1
}}
>
{item.depth === 0 ? (
<AppIcon
name={selected ? "checkbox" : "box-empty"}
iconFamily="notesnook"
size={AppFontSize.md}
color={
selected
? [colors.selected.accent, colors.selected.accentForeground]
selected || isFocused
? colors.selected.icon
: colors.primary.icon
}
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
style={{
borderRadius: defaultBorderRadius
}}
iconFamily="notesnook"
name={"bookmark"}
/>
</View>
) : (
<>
<Paragraph fontSize="SM" color={colors.secondary.paragraph}>
{totalNotes?.(notebook?.id) || 0}
</Paragraph>
</>
)}
) : null}
{onAddNotebook ? (
<IconButton
name="plus"
size={AppFontSize.md}
testID={`add-notebook-${index}`}
color={colors.primary.icon}
top={0}
left={0}
bottom={0}
right={40}
style={{
width: 32,
height: 32,
borderRadius: defaultBorderRadius
}}
onPress={() => {
onAddNotebook();
}}
/>
) : null}
{item.hasChildren ? (
<IconButton
size={12}
<Paragraph
color={
selected || isFocused
? colors.selected.icon
: colors.primary.icon
isFocused ? colors.selected.paragraph : colors.primary.paragraph
}
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
onPress={() => {
if (item.hasChildren && !disableExpand) {
onToggleExpanded?.();
size={AppFontSize.sm}
>
{notebook?.title}
</Paragraph>
</View>
<View
style={{
gap: Spacing.LEVEL_1,
flexDirection: "row",
alignItems: "center",
justifyContent: "center"
}}
>
{selectionEnabled ? (
<View
style={{
justifyContent: "center",
alignItems: "center"
}}
>
<AppIcon
name={selected ? "checkbox" : "box-empty"}
iconFamily="notesnook"
size={AppFontSize.md}
color={
selected
? [
colors.selected.accent,
colors.selected.accentForeground
]
: colors.primary.icon
}
/>
</View>
) : (
<>
{item.hasChildren || hideNoteCount ? null : (
<Paragraph fontSize="SM" color={colors.secondary.paragraph}>
{totalNotes?.(notebook?.id) || 0}
</Paragraph>
)}
</>
)}
{onAddNotebook ? (
<IconButton
name="plus"
iconFamily="notesnook"
size={AppFontSize.md}
testID={`add-notebook-${index}`}
color={colors.primary.icon}
top={0}
left={0}
bottom={0}
right={40}
style={{
width: undefined,
height: undefined,
borderRadius: defaultBorderRadius
}}
onPress={() => {
onAddNotebook();
}}
/>
) : null}
{item.hasChildren ? (
<IconButton
size={12}
color={
selected || isFocused
? colors.selected.icon
: colors.primary.icon
}
}}
top={0}
left={20}
bottom={0}
right={20}
style={{
borderRadius: defaultBorderRadius,
width: undefined,
height: undefined
}}
iconFamily="notesnook"
name={expanded ? "chevron-up" : "chevron-down"}
/>
) : null}
</View>
</Pressable>
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
onPress={() => {
if (item.hasChildren && !disableExpand) {
onToggleExpanded?.();
}
}}
top={0}
bottom={0}
right={0}
left={0}
style={{
borderRadius: defaultBorderRadius,
width: undefined,
height: undefined
}}
iconFamily="notesnook"
name={expanded ? "chevron-up" : "chevron-down"}
/>
) : null}
</View>
</Pressable>
</View>
{expanded && item.hasChildren && !selectionEnabled ? (
<View
style={{
width: "100%",
paddingLeft: (item.depth + 1) * 15
flexDirection: "row",
paddingLeft: (item.depth + 1) * Spacing.LEVEL_2
}}
>
<View
@@ -304,22 +326,18 @@ export const NotebookItem = ({
height: "100%",
width: 1,
backgroundColor: colors.primary.border,
top: 0,
bottom: 0,
position: "absolute",
left: (item.depth + 1) * 15
marginRight: Spacing.LEVEL_1
}}
/>
<Pressable
style={{
// borderLeftWidth: 1,
// borderLeftColor: colors.primary.border,
flexDirection: "row",
gap: Spacing.LEVEL_1,
justifyContent: "flex-start",
paddingVertical: Spacing.LEVEL_1,
paddingHorizontal: Spacing.LEVEL_1,
alignItems: "center"
alignItems: "center",
...subNotebookButtonStyle
}}
onPress={() => {
AddNotebookSheet.present(undefined, item.notebook);
@@ -333,8 +351,8 @@ export const NotebookItem = ({
}}
iconFamily="notesnook"
/>
<Heading fontSize="SM">Create sub-notebook</Heading>
</Pressable>{" "}
<Heading fontSize="SM">{strings.createSubnotebook()}</Heading>
</Pressable>
</View>
) : null}
</View>

View File

@@ -105,7 +105,6 @@ export const SideMenuListEmpty = (props: SideMenuListEmptyProps) => {
<Button
title={props.placeholderButtonTitle}
onPress={props.onPressPlaceholderButton}
fontSize={AppFontSize.sm}
style={{
marginTop: Spacing.LEVEL_2,
paddingVertical: Spacing.LEVEL_3,

View File

@@ -29,7 +29,6 @@ import { TreeItem } from "../../stores/create-notebook-tree-stores";
import useNavigationStore from "../../stores/use-navigation-store";
import { useNotebooks } from "../../stores/use-notebook-store";
import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { Properties } from "../properties";
import { NotebookItem } from "./notebook-item";
import { SideMenuHeader } from "./side-menu-header";
@@ -291,8 +290,8 @@ const NotebookItemWrapper = React.memo(
Properties.present(item.notebook, false, [
{
id: "select",
title: strings.select() + " " + strings.dataTypes["notebook"](),
icon: "checkbox-outline",
title: strings.select(),
icon: "check-square",
onPress: () => {
const store = useSideMenuNotebookSelectionStore;
store.setState({

View File

@@ -27,7 +27,6 @@ import { useDBItem, useTotalNotes } from "../../hooks/use-db-item";
import { TaggedNotes } from "../../screens/notes/tagged";
import { presentDialog } from "../dialog/functions";
import Navigation from "../../services/navigation";
import { ToastManager } from "../../services/event-manager";
import useNavigationStore from "../../stores/use-navigation-store";
import { useTags, useTagStore } from "../../stores/use-tag-store";
import { AppFontSize } from "../../utils/size";
@@ -42,6 +41,7 @@ import { useSideMenuTagsSelectionStore } from "./stores";
import { LegendList, LegendListRenderItemProps } from "@legendapp/list";
import { useRelationStore } from "../../stores/use-relation-store";
import { Radius, Spacing } from "../../common/design/spacing";
import { createFormRef, validators } from "../ui/input/form-input";
const TagItem = (props: {
tags: VirtualizedGrouping<Tag>;
@@ -81,8 +81,8 @@ const TagItem = (props: {
Properties.present(item, false, [
{
id: "select",
title: strings.select() + " " + strings.dataTypes["tag"](),
icon: "checkbox-outline",
title: strings.select(),
icon: "check-square",
onPress: () => {
const store = useSideMenuTagsSelectionStore;
store.setState({
@@ -230,26 +230,37 @@ export const SideMenuTags = () => {
const onPressAddTag = React.useCallback(() => {
presentDialog({
title: strings.addTag(),
// paragraph: strings.addTagDesc(),
input: true,
inputLabel: "Enter title",
inputPlaceholder: "eg. journal",
positiveText: strings.add(),
positivePress: async (tag) => {
if (tag) {
await db.tags.add({
title: tag
});
useTagStore.getState().refresh();
return true;
form: {
formRef: createFormRef({
title: ""
}),
items: [
{
label: strings.enterTitle(),
name: "title",
placeholder: "eg. journal",
ref: React.createRef(),
validators: [validators.required(strings.allFieldsRequired())],
inputProps: {
autoCapitalize: "none"
}
}
],
onFormSubmit: async (form) => {
try {
if (!form.validate()) return false;
await db.tags.add({
title: form.getValue("title").trim()
});
useTagStore.getState().refresh();
return true;
} catch (e) {
form.setError("title", (e as Error).message);
return false;
}
}
ToastManager.show({
context: "local",
type: "error",
message: strings.allFieldsRequired()
});
return false;
}
},
positiveText: strings.add()
});
}, []);

View File

@@ -80,13 +80,13 @@ export const TabBarButton = ({
backgroundColor: "transparent",
borderWidth: 0
}}
type={"plain"}
type={"plain"}
>
<Animated.View
style={[
{
backgroundColor: isActive ? colors.primary.shade : undefined,
borderRadius: Radius.XXS,
borderRadius: Radius.XS,
padding: Spacing.LEVEL_1
},
animatedIconStyle
@@ -95,7 +95,7 @@ export const TabBarButton = ({
<AppIcon
name={icon}
iconFamily="notesnook"
color={isActive ? colors.primary.icon : colors.secondary.icon}
color={isActive ? colors.selected.icon : colors.secondary.icon}
size={16}
/>
</Animated.View>

View File

@@ -0,0 +1,116 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { ColorValue, DimensionValue, TextStyle, ViewStyle } from "react-native";
import { FontFamily } from "../../common/design/font";
import { Radius, Spacing } from "../../common/design/spacing";
import { AppFontSize } from "../../utils/size";
import AppIcon from "./AppIcon";
import { Pressable, PressableProps } from "./pressable";
import Paragraph from "./typography/paragraph";
export interface CheckboxProps extends Omit<PressableProps, "style"> {
checked?: boolean;
title?: string | null;
onPress?: () => void;
type?: PressableProps["type"];
style?: ViewStyle;
textStyle?: TextStyle;
fontSize?: number;
fontFamily?: keyof typeof FontFamily;
iconSize?: number;
iconColor?: ColorValue | ColorValue[];
width?: DimensionValue | null;
disabled?: boolean;
/**
* Hide the whole component when set to false.
*
* @default true
*/
visible?: boolean;
}
export const Checkbox = ({
checked,
title = null,
onPress,
type = "shade",
style,
textStyle,
fontSize = AppFontSize.xs,
fontFamily = "MEDIUM",
iconSize = 16,
iconColor,
width = "100%",
disabled,
visible = true,
...restProps
}: CheckboxProps) => {
const { colors } = useThemeColors();
if (!visible) return null;
return (
<Pressable
{...restProps}
onPress={onPress}
disabled={disabled}
type={type}
style={{
width: width || undefined,
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
flexShrink: 1,
gap: Spacing.LEVEL_1,
paddingHorizontal: Spacing.LEVEL_2,
paddingVertical: Spacing.LEVEL_2,
borderRadius: Radius.XS,
opacity: disabled ? 0.5 : 1,
...style
}}
>
<AppIcon
name={checked ? "checkbox" : "box-empty"}
iconFamily="notesnook"
size={iconSize}
color={
iconColor ||
(checked
? [colors.primary.accent, colors.primary.accentForeground]
: colors.primary.icon)
}
/>
{title ? (
<Paragraph
numberOfLines={1}
size={fontSize}
fontFamily={fontFamily}
color={colors.primary.paragraph}
style={[{ flexShrink: 1 }, textStyle]}
>
{title}
</Paragraph>
) : null}
</Pressable>
);
};

View File

@@ -0,0 +1,116 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { ColorValue, DimensionValue, TextStyle, ViewStyle } from "react-native";
import { FontFamily } from "../../../common/design/font";
import { Radius, Spacing } from "../../../common/design/spacing";
import { AppFontSize } from "../../../utils/size";
import AppIcon from "../AppIcon";
import { Pressable, PressableProps } from "../pressable";
import Paragraph from "../typography/paragraph";
export interface CheckboxProps extends Omit<PressableProps, "style"> {
checked?: boolean;
title?: string | null;
onPress?: () => void;
type?: PressableProps["type"];
style?: ViewStyle;
textStyle?: TextStyle;
fontSize?: number;
fontFamily?: keyof typeof FontFamily;
iconSize?: number;
iconColor?: ColorValue | ColorValue[];
width?: DimensionValue | null;
disabled?: boolean;
/**
* Hide the whole component when set to false.
*
* @default true
*/
visible?: boolean;
}
export const Checkbox = ({
checked,
title = null,
onPress,
type = "shade",
style,
textStyle,
fontSize = AppFontSize.xs,
fontFamily = "MEDIUM",
iconSize = 16,
iconColor,
width = "100%",
disabled,
visible = true,
...restProps
}: CheckboxProps) => {
const { colors } = useThemeColors();
if (!visible) return null;
return (
<Pressable
{...restProps}
onPress={onPress}
disabled={disabled}
type={type}
style={{
width: width || undefined,
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
flexShrink: 1,
gap: Spacing.LEVEL_1,
paddingHorizontal: Spacing.LEVEL_2,
paddingVertical: Spacing.LEVEL_2,
borderRadius: Radius.XS,
opacity: disabled ? 0.5 : 1,
...style
}}
>
<AppIcon
name={checked ? "checkbox" : "box-empty"}
iconFamily="notesnook"
size={iconSize}
color={
iconColor ||
(checked
? [colors.primary.accent, colors.primary.accentForeground]
: colors.primary.icon)
}
/>
{title ? (
<Paragraph
numberOfLines={1}
size={fontSize}
fontFamily={fontFamily}
color={colors.primary.paragraph}
style={[{ flexShrink: 1 }, textStyle]}
>
{title}
</Paragraph>
) : null}
</Pressable>
);
};

View File

@@ -219,7 +219,6 @@ const Input = ({
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: Spacing.LEVEL_2,
paddingRight: Spacing.LEVEL_3,
...containerStyle
};
@@ -230,6 +229,7 @@ const Input = ({
onPress && loading ? colors.primary.accent : colors.primary.paragraph,
paddingTop: Spacing.LEVEL_3,
paddingBottom: Spacing.LEVEL_3,
lineHeight: fontSize + fontSize * 0.3,
flexGrow: 1,
flexShrink: 1,
fontFamily: "Inter-Regular",

View File

@@ -18,21 +18,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { View } from "react-native";
import { DimensionValue, View, ViewStyle } from "react-native";
import { useThemeColors } from "@notesnook/theme";
import { Spacing } from "../../../common/design/spacing";
const LineSeparator = ({ padding }: { padding: keyof typeof Spacing }) => {
const LineSeparator = ({
style,
paddingHorizontal,
paddingVertical,
stroke = 1
}: {
style?: ViewStyle;
paddingHorizontal?: DimensionValue;
paddingVertical?: DimensionValue;
stroke?: number;
}) => {
const { colors } = useThemeColors();
return (
<View
style={{
width: "100%",
height: 1,
backgroundColor: colors.primary.border
// marginVertical: Spacing[padding]
}}
/>
style={[
style,
{
paddingHorizontal,
paddingVertical
}
]}
>
<View
style={{
width: "100%",
height: stroke,
backgroundColor: colors.primary.separator
}}
/>
</View>
);
};

View File

@@ -488,7 +488,7 @@ export const useActions = ({
actions.push({
id: "rename-tag",
title: strings.rename(),
icon: "square-edit-outline",
icon: "mode-edit",
onPress: renameTag
});
}
@@ -497,7 +497,7 @@ export const useActions = ({
actions.push({
id: "rename-color",
title: strings.rename(),
icon: "square-edit-outline",
icon: "mode-edit",
onPress: renameColor
});
}
@@ -544,7 +544,7 @@ export const useActions = ({
{
id: "delete",
title: strings.delete(),
icon: "delete",
icon: "trash-alt",
onPress: deleteTrashItem
}
);
@@ -557,7 +557,7 @@ export const useActions = ({
title: isPinnedToMenu
? strings.removeShortcut()
: strings.addShortcut(),
icon: isPinnedToMenu ? "link-variant-remove" : "link-variant",
icon: "arrow-square-out",
onPress: createMenuShortcut,
isToggle: true,
checked: isPinnedToMenu,
@@ -570,7 +570,7 @@ export const useActions = ({
? strings.removeAsDefault()
: strings.setAsDefault(),
hidden: item.type !== "tag",
icon: "pound",
icon: "check",
onPress: async () => {
if (defaultTag === item.id) {
await db.settings.setDefaultTag(undefined);
@@ -605,7 +605,7 @@ export const useActions = ({
{
id: "add-notebook",
title: strings.addNotebook(),
icon: "plus",
icon: "bookmark",
onPress: async () => {
if (features && !features.notebooks.isAllowed) {
ToastManager.show({
@@ -626,16 +626,16 @@ export const useActions = ({
},
locked: !features?.notebooks.isAllowed
},
{
id: "edit-notebook",
title: strings.editNotebook(),
icon: "square-edit-outline",
onPress: async () => {
close();
await sleep(300);
AddNotebookSheet.present(item);
}
},
// {
// id: "edit-notebook",
// title: strings.editNotebook(),
// icon: "square-edit-outline",
// onPress: async () => {
// close();
// await sleep(300);
// AddNotebookSheet.present(item);
// }
// },
{
id: "default-notebook",
title:
@@ -643,7 +643,7 @@ export const useActions = ({
? strings.removeAsDefault()
: strings.setAsDefault(),
hidden: item.type !== "notebook",
icon: "notebook",
icon: "check",
onPress: async () => {
if (defaultNotebook === item.id) {
await db.settings.setDefaultNotebook(undefined);
@@ -678,7 +678,7 @@ export const useActions = ({
id: "move-notes",
title: strings.addNotes(),
hidden: item.type !== "notebook",
icon: "text",
icon: "file-text",
onPress: () => {
close();
Navigation.navigate("MoveNotes", {
@@ -689,7 +689,7 @@ export const useActions = ({
{
id: "move-notebook",
title: strings.moveNotebookFix(),
icon: "arrow-right-bold-box-outline",
icon: "drive-file-move",
onPress: () => {
close();
Navigation.navigate("MoveNotebook", {
@@ -704,7 +704,7 @@ export const useActions = ({
actions.push({
id: "pin",
title: item.pinned ? strings.unpin() : strings.pin(),
icon: item.pinned ? "pin-off-outline" : "pin-outline",
icon: "pin",
onPress: pinItem,
isToggle: true,
checked: item.pinned,
@@ -720,7 +720,7 @@ export const useActions = ({
actions.push({
id: "default-homepage",
title: isHomepage ? strings.unsetAsHomepage() : strings.setAsHomepage(),
icon: "home-outline",
icon: "house",
isToggle: true,
checked: isHomepage,
onPress: async () => {
@@ -1014,7 +1014,7 @@ export const useActions = ({
{
id: "favorite",
title: !item.favorite ? strings.favorite() : strings.unfavorite(),
icon: item.favorite ? "star-off" : "star-outline",
icon: "star",
onPress: addToFavorites,
isToggle: true,
checked: item.favorite,
@@ -1053,19 +1053,19 @@ export const useActions = ({
{
id: "attachments",
title: strings.attachedFiles(),
icon: "attachment",
icon: "paperclip",
onPress: showAttachments
},
{
id: "history",
title: strings.history(),
icon: "history",
icon: "clock-counter-clockwise",
onPress: openHistory
},
{
id: "reminders",
title: strings.dataTypesPluralCamelCase.reminder(),
icon: "clock-outline",
icon: "clock",
onPress: async () => {
close();
RelationsList.present({
@@ -1095,40 +1095,40 @@ export const useActions = ({
{
id: "copy",
title: strings.copy(),
icon: "content-copy",
icon: "copy",
onPress: copyContent
},
{
id: "share",
title: strings.share(),
icon: "share-variant",
icon: "share",
onPress: shareNote
},
{
id: "read-only",
title: strings.readOnly(),
icon: "pencil-lock",
icon: "pencil-simple-slash",
onPress: toggleReadyOnlyMode,
checked: item.readonly
},
{
id: "local-only",
title: strings.syncOff(),
icon: "sync-off",
icon: "sync-disabled",
onPress: toggleLocalOnly,
checked: item.localOnly
},
{
id: "duplicate",
title: strings.duplicate(),
icon: "content-duplicate",
icon: "duplicate",
onPress: duplicateNote
},
{
id: "add-reminder",
title: strings.remindMe(),
icon: "clock-plus-outline",
icon: "bell",
onPress: async () => {
close();
await sleep(100);
@@ -1138,7 +1138,7 @@ export const useActions = ({
{
id: "lock-unlock",
title: locked ? strings.unlock() : strings.lock(),
icon: locked ? "lock-open-outline" : "key-outline",
icon: "lock",
onPress: addToVault,
checked: locked
},
@@ -1163,16 +1163,16 @@ export const useActions = ({
icon: "book-outline",
onPress: addTo
},
{
id: "add-tag",
title: strings.addTags(),
icon: "pound",
onPress: addTo
},
// {
// id: "add-tag",
// title: strings.addTags(),
// icon: "pound",
// onPress: addTo
// },
{
id: "references",
title: strings.references(),
icon: "vector-link",
icon: "link-alt",
onPress: () => {
ReferencesList.present({
reference: item as ItemReference
@@ -1272,7 +1272,7 @@ export const useActions = ({
item.type !== "notebook" && item.type !== "note"
? strings.doActions.delete.unknown(item.type, 1)
: strings.moveToTrash(),
icon: "delete-outline",
icon: "trash-alt",
type: "error",
onPress: deleteItem,
locked: isPublished
@@ -1289,7 +1289,7 @@ export const useActions = ({
actions.push({
id: "launcher-shortcut",
title: strings.addToHome(),
icon: "cellphone-arrow-down",
icon: "home",
locked: !features?.androidLauncherShortcuts.isAllowed,
onPress: async () => {
if (features && !features?.androidLauncherShortcuts.isAllowed) {

View File

@@ -139,6 +139,7 @@ const showActionsheet = async () => {
.getNoteIdForTab(useTabStore.getState().currentTab!);
if (noteId) {
const note = await db.notes?.note(noteId);
if (!note) return;
Properties.present(note, false);
} else {
ToastManager.show({

View File

@@ -67,7 +67,7 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
onPressDefaultRightButton={openEditor}
/>
<LineSeparator padding="LEVEL_3" />
<LineSeparator />
<DelayLayout wait={loading}>
<List

View File

@@ -55,6 +55,8 @@ import { eSendEvent, ToastManager } from "../../services/event-manager";
import { eUpdateNotebookRoute } from "../../utils/events";
import { isFeatureAvailable } from "@notesnook/common";
import PaywallSheet from "../../components/sheets/paywall";
import { Radius, Spacing } from "../../common/design/spacing";
import LineSeparator from "../../components/ui/seperator/line-separator";
const {
useNotebookExpandedStore,
@@ -136,14 +138,14 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
excludedItems.push(notebook.id);
}
// Exclude and disable items as needed
const filtered = tree.filter(item => !excludedItems.includes(item.notebook.id)).map(
(treeItem) => {
const filtered = tree
.filter((item) => !excludedItems.includes(item.notebook.id))
.map((treeItem) => {
return {
...treeItem,
disabled: disabledItems.includes(treeItem.notebook.id)
}
}
);
};
});
return filtered;
}
filterNotebooks().then((filtered) => {
@@ -157,13 +159,14 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
<NotebookItemWrapper
index={index}
item={item}
hideNoteCount
onPress={async () => {
if (item.disabled) {
ToastManager.show({
type: "info",
"message": "You cannot move the selected notebook(s) here"
})
return;
if (item.disabled) {
ToastManager.show({
type: "info",
message: "You cannot move the selected notebook(s) here"
});
return;
}
const selectedNotebook = item.notebook;
presentDialog({
@@ -223,7 +226,6 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
return (
<SafeAreaView
style={{
gap: DefaultAppStyles.GAP_VERTICAL,
flex: 1,
backgroundColor: colors.primary.background
}}
@@ -233,6 +235,9 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
selectedNotebooks.length,
selectedNotebooks[0].title
)}
style={{
backgroundColor: "transparent"
}}
canGoBack
/>
@@ -244,7 +249,7 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
ListHeaderComponent={
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP
paddingHorizontal: Spacing.LEVEL_3
}}
>
<Input
@@ -255,13 +260,17 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
updateNotebooks();
}, 300);
}}
containerStyle={{
backgroundColor: colors.secondary.background,
borderWidth: 0,
borderRadius: Radius.S
}}
testID="move-notebook-search"
button={{
icon: "plus",
onPress: async () => {
const notebooksFeature = await isFeatureAvailable(
"notebooks"
);
const notebooksFeature =
await isFeatureAvailable("notebooks");
if (!notebooksFeature.isAllowed) {
ToastManager.show({
message: notebooksFeature.error,
@@ -287,17 +296,19 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
color: colors.primary.icon
}}
/>
{moveToTopEnabled && tree.length > 0 ? (
<Button
title={strings.moveToTop()}
style={{
alignSelf: "flex-start",
width: "100%",
justifyContent: "space-between"
justifyContent: "space-between",
marginTop: Spacing.LEVEL_2
}}
icon="arrow-up-bold"
iconPosition="right"
type="secondaryAccented"
type="accent"
onPress={async () => {
for (const notebook of selectedNotebooks) {
if (
@@ -325,6 +336,13 @@ export const MoveNotebook = (props: NavigationProps<"MoveNotebook">) => {
}}
/>
) : null}
<LineSeparator
paddingVertical={Spacing.LEVEL_3}
style={{
paddingBottom: Spacing.LEVEL_0
}}
/>
</View>
}
ListEmptyComponent={
@@ -354,11 +372,13 @@ const NotebookItemWrapper = React.memo(
({
item,
index,
onPress
onPress,
hideNoteCount
}: {
item: TreeItem;
index: number;
onPress: () => void;
hideNoteCount?: boolean;
}) => {
const expanded = useNotebookExpandedStore(
(state) => state.expanded[item.notebook.id]
@@ -393,8 +413,7 @@ const NotebookItemWrapper = React.memo(
return (
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
marginTop: index === 0 ? DefaultAppStyles.GAP : 0
paddingHorizontal: Spacing.LEVEL_3
}}
>
<NotebookItem
@@ -415,6 +434,13 @@ const NotebookItemWrapper = React.memo(
useNotebookTreeStore.getState().removeChildren(item.notebook.id);
}
}}
style={{
paddingVertical: Spacing.LEVEL_2
}}
subNotebookButtonStyle={{
paddingVertical: Spacing.LEVEL_2
}}
hideNoteCount={hideNoteCount}
disableExpand={disableExpand}
selected={selected}
selectionEnabled={selectionEnabled}

View File

@@ -166,6 +166,7 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
rightButton={{
name: "dots-vertical",
onPress: () => {
if (!notebook) return;
Properties.present(notebook);
}
}}

View File

@@ -59,9 +59,14 @@ function confirmDeleteAllNotes(
) {
return new Promise<{ delete: boolean; deleteNotes: boolean }>((resolve) => {
presentDialog({
title: strings.doActions.delete.notebook(items.length),
title: strings.moveToTrash() + "?",
paragraph: `The selected notebook${items.length > 1 ? `s` : ``} will be moved to trash. You can restore them later.`,
positiveText: strings.delete(),
negativeText: strings.cancel(),
icon: "warning-circle",
iconType: "error",
iconFamily: "notesnook",
centered: true,
positivePress: async (_inputValue, value) => {
setTimeout(() => {
resolve({ delete: true, deleteNotes: value });

File diff suppressed because one or more lines are too long