mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-07-10 20:41:26 +02:00
mobile: update ui
This commit is contained in:
@@ -32,7 +32,6 @@ import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { IconButton, IconButtonProps } from "../ui/icon-button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
|
||||
@@ -106,7 +105,7 @@ export const Header = ({
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
marginTop: Platform.OS === "android" ? 5 : 0
|
||||
}}
|
||||
>
|
||||
@@ -116,7 +115,6 @@ export const Header = ({
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
borderRadius: Radius.S,
|
||||
paddingHorizontal: Spacing.LEVEL_2,
|
||||
paddingVertical: Spacing.LEVEL_3,
|
||||
backgroundColor: colors.secondary.background,
|
||||
alignItems: "center"
|
||||
|
||||
@@ -162,9 +162,9 @@ const NoteItem = ({
|
||||
);
|
||||
|
||||
const canShowTopStatusBar =
|
||||
attachmentsCount > 0 &&
|
||||
activeStatusIcons.length > 0 &&
|
||||
notebooks?.items?.length &&
|
||||
attachmentsCount > 0 ||
|
||||
activeStatusIcons.length > 0 ||
|
||||
notebooks?.items?.length ||
|
||||
tags?.items.length;
|
||||
|
||||
return (
|
||||
@@ -369,7 +369,7 @@ const NoteItem = ({
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{color ? (
|
||||
{color && compactMode ? (
|
||||
<View
|
||||
style={{
|
||||
width: 8,
|
||||
|
||||
@@ -89,7 +89,9 @@ const SelectionWrapper = ({
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: undefined
|
||||
backgroundColor: isEditingNote
|
||||
? colors.selected.background
|
||||
: undefined
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
|
||||
@@ -27,6 +27,7 @@ import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { db } from "../../common/database";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import {
|
||||
eSendEvent,
|
||||
sendItemUpdateEvent,
|
||||
@@ -38,13 +39,10 @@ import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { refreshNotesPage } from "../../utils/events";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import ColorPicker from "../dialogs/color-picker";
|
||||
import PaywallSheet from "../sheets/paywall";
|
||||
import { Button } from "../ui/button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
|
||||
const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -86,15 +84,17 @@ const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
|
||||
height: 40,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
alignItems: "center",
|
||||
borderWidth: 0
|
||||
}}
|
||||
>
|
||||
{isLinked ? (
|
||||
<Icon
|
||||
<AppIcon
|
||||
testID="icon-check"
|
||||
name="check"
|
||||
color="white"
|
||||
size={AppFontSize.lg}
|
||||
name="check-small"
|
||||
iconFamily="notesnook"
|
||||
color={colors.static.black}
|
||||
size={15}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
|
||||
@@ -97,7 +97,7 @@ export const DateMeta = ({ item }: { item: Item }) => {
|
||||
|
||||
{key === "dateCreated" && item.type === "note" ? (
|
||||
<>
|
||||
<AppIcon name="edit-pencil" size={10} iconFamily="notesnook" />
|
||||
<AppIcon name="edit-pencil" size={16} iconFamily="notesnook" />
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
@@ -63,6 +63,7 @@ import {
|
||||
Tag,
|
||||
TrashItem
|
||||
} from "@notesnook/core";
|
||||
import LineSeparator from "../ui/seperator/line-separator";
|
||||
|
||||
export type PropertiesItem =
|
||||
| Note
|
||||
@@ -131,7 +132,7 @@ export const Properties = ({
|
||||
borderBottomRightRadius: DDS.isLargeTablet() ? 10 : 1,
|
||||
borderBottomLeftRadius: DDS.isLargeTablet() ? 10 : 1,
|
||||
maxHeight: "100%",
|
||||
paddingTop: Spacing.LEVEL_2
|
||||
paddingTop: Spacing.LEVEL_3
|
||||
}}
|
||||
nestedScrollEnabled
|
||||
bounces={false}
|
||||
@@ -159,8 +160,7 @@ export const Properties = ({
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_1,
|
||||
paddingBottom: Spacing.LEVEL_3
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{item.type === "note" && (noteNotebooks.length || tags.length) ? (
|
||||
@@ -248,7 +248,7 @@ export const Properties = ({
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Heading size={AppFontSize.xl}>{item.title}</Heading>
|
||||
<Heading fontSize="XL">{item.title}</Heading>
|
||||
|
||||
{editAction ? (
|
||||
<IconButton
|
||||
@@ -306,92 +306,95 @@ export const Properties = ({
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<DateMeta item={item} />
|
||||
|
||||
{item.type === "note" && colorNotes.length > 0 ? (
|
||||
<Tags close={close} item={item} />
|
||||
) : null}
|
||||
|
||||
{item.type === "note" ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
borderBottomWidth: 1,
|
||||
borderTopWidth: colorNotes.length > 0 ? 1 : 0,
|
||||
borderColor: colors.primary.border,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
paddingTop: colorNotes.length > 0 ? Spacing.LEVEL_2 : 0,
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={async () => {
|
||||
ManageTags.present([item.id]);
|
||||
close?.();
|
||||
}}
|
||||
buttonType={{
|
||||
text: colors.primary.paragraph
|
||||
}}
|
||||
title={strings.addTag()}
|
||||
type={colorNotes?.length ? "accent-outline" : "shade"}
|
||||
icon="plus"
|
||||
iconFamily="notesnook"
|
||||
<LineSeparator paddingVertical={Spacing.LEVEL_3} />
|
||||
) : null}
|
||||
<DateMeta item={item} />.
|
||||
{item.type === "note" && colorNotes.length > 0 ? (
|
||||
<View>
|
||||
<LineSeparator
|
||||
paddingVertical={Spacing.LEVEL_3}
|
||||
style={{
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
width: colorNotes?.length > 0 ? "100%" : "48.5%"
|
||||
paddingTop: Spacing.LEVEL_2
|
||||
}}
|
||||
/>
|
||||
|
||||
{colorNotes.length > 0 ? null : (
|
||||
<Tags close={close} item={item} />
|
||||
<LineSeparator
|
||||
paddingVertical={Spacing.LEVEL_3}
|
||||
style={{
|
||||
paddingBottom: 0
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
{item.type === "note" ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_2,
|
||||
marginTop: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={() => {
|
||||
if (colorFeature && !colorFeature.isAllowed) {
|
||||
ToastManager.show({
|
||||
message: colorFeature.error,
|
||||
type: "info",
|
||||
context: "local",
|
||||
actionText: strings.upgrade(),
|
||||
func: () => {
|
||||
PaywallSheet.present(colorFeature);
|
||||
ToastManager.hide();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
useSettingStore.getState().setSheetKeyboardHandler(false);
|
||||
setVisible(true);
|
||||
onPress={async () => {
|
||||
ManageTags.present([item.id]);
|
||||
close?.();
|
||||
}}
|
||||
title={strings.addColor()}
|
||||
type="secondaryAccented"
|
||||
title={
|
||||
colorNotes?.length
|
||||
? strings.addTag()
|
||||
: strings.dataTypesCamelCase.tag()
|
||||
}
|
||||
type={colorNotes?.length ? "accent-outline" : "shade-plain"}
|
||||
icon="plus"
|
||||
iconFamily="notesnook"
|
||||
style={{
|
||||
width: "48.5%",
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_2
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
width: colorNotes?.length > 0 ? "100%" : "48.5%"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{colorNotes.length > 0 ? null : (
|
||||
<Button
|
||||
onPress={() => {
|
||||
if (colorFeature && !colorFeature.isAllowed) {
|
||||
ToastManager.show({
|
||||
message: colorFeature.error,
|
||||
type: "info",
|
||||
context: "local",
|
||||
actionText: strings.upgrade(),
|
||||
func: () => {
|
||||
PaywallSheet.present(colorFeature);
|
||||
ToastManager.hide();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
useSettingStore.getState().setSheetKeyboardHandler(false);
|
||||
setVisible(true);
|
||||
}}
|
||||
title={strings.dataTypesCamelCase.color()}
|
||||
type="shade-plain"
|
||||
icon="plus"
|
||||
iconFamily="notesnook"
|
||||
style={{
|
||||
width: "48.5%",
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_2
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
marginVertical: Spacing.LEVEL_3,
|
||||
width: "100%",
|
||||
borderBottomWidth: 1,
|
||||
borderColor: colors.primary.separator
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<LineSeparator
|
||||
paddingVertical={Spacing.LEVEL_2}
|
||||
paddingHorizontal={Spacing.LEVEL_3}
|
||||
/>
|
||||
|
||||
<Items
|
||||
item={item}
|
||||
|
||||
@@ -36,10 +36,7 @@ export const Tags = ({ item, close }) => {
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
borderTopWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
paddingVertical: Spacing.LEVEL_3
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<ColorTags item={item} />
|
||||
|
||||
@@ -285,7 +285,16 @@ const Sort = ({
|
||||
await updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
>
|
||||
<Paragraph>
|
||||
<Paragraph
|
||||
color={
|
||||
groupOptions?.sortBy === item
|
||||
? colors.primary.heading
|
||||
: colors.primary.paragraph
|
||||
}
|
||||
fontFamily={
|
||||
groupOptions?.sortBy === item ? "MEDIUM" : "REGULAR"
|
||||
}
|
||||
>
|
||||
{strings.sortByStrings[
|
||||
item as keyof typeof strings.sortByStrings
|
||||
]()}
|
||||
|
||||
@@ -188,7 +188,7 @@ const buttonTypes = (
|
||||
"accent-outline": {
|
||||
primary: "transparent",
|
||||
text: colors.primary.accent,
|
||||
selected: accent || colors.primary.accent,
|
||||
selected: "transparent",
|
||||
borderWidth: 1,
|
||||
borderColor: accent || colors.primary.accent,
|
||||
borderSelectedColor: accent || colors.primary.accent
|
||||
@@ -217,9 +217,9 @@ const buttonTypes = (
|
||||
text: colors.primary.heading,
|
||||
selected: colors.primary.accent,
|
||||
colorOpacity: 0.12,
|
||||
borderWidth: 0.8,
|
||||
borderColor: getColorLinearShade(colors.primary.shade, 0.3, isDark),
|
||||
borderSelectedColor: getColorLinearShade(colors.primary.shade, 0.3, isDark)
|
||||
borderWidth: 0
|
||||
// borderColor: getColorLinearShade(colors.primary.shade, 0.3, isDark),
|
||||
// borderSelectedColor: getColorLinearShade(colors.primary.shade, 0.3, isDark)
|
||||
},
|
||||
error: {
|
||||
primary: colors.error.background,
|
||||
|
||||
@@ -25,17 +25,19 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import { Header } from "../../components/header";
|
||||
import AppIcon from "../../components/ui/AppIcon";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import Input from "../../components/ui/input";
|
||||
import { Pressable } from "../../components/ui/pressable";
|
||||
import Heading from "../../components/ui/typography/heading";
|
||||
import Paragraph from "../../components/ui/typography/paragraph";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import {
|
||||
sendItemUpdateEvent,
|
||||
ToastManager
|
||||
ToastManager,
|
||||
sendItemUpdateEvent
|
||||
} from "../../services/event-manager";
|
||||
import Navigation, { NavigationProps } from "../../services/navigation";
|
||||
import {
|
||||
@@ -44,8 +46,7 @@ import {
|
||||
} from "../../stores/item-selection-store";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { useTagStore } from "../../stores/use-tag-store";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
|
||||
async function updateInitialSelectionState(items: string[]) {
|
||||
const relations = await db.relations
|
||||
@@ -90,18 +91,26 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
|
||||
const [tags, setTags] = useState<Tag[]>();
|
||||
const [query, setQuery] = useState<string>();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
useNavigationFocus(props.navigation, { focusOnInit: true });
|
||||
useNavigationFocus(props.navigation, {
|
||||
focusOnInit: true
|
||||
});
|
||||
const timerRef = useRef<NodeJS.Timeout>(undefined);
|
||||
const [queryExists, setQueryExists] = useState(false);
|
||||
const refreshSelection = useCallback(() => {
|
||||
updateInitialSelectionState(ids).then((selection) => {
|
||||
updateInitialSelectionState(ids).then((initialState) => {
|
||||
const prev = useTagItemSelection.getState();
|
||||
const selection = { ...initialState };
|
||||
for (const id in prev.selection) {
|
||||
if (prev.selection[id] !== prev.initialState[id]) {
|
||||
selection[id] = prev.selection[id];
|
||||
}
|
||||
}
|
||||
useTagItemSelection.setState({
|
||||
initialState: selection,
|
||||
selection: { ...selection }
|
||||
initialState,
|
||||
selection
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ids, tags]);
|
||||
}, [ids]);
|
||||
|
||||
const sortAndSetTags = useCallback(
|
||||
async (items: VirtualizedGrouping<Tag>) => {
|
||||
@@ -130,6 +139,12 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
|
||||
[ids]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
useTagItemSelection.getState().reset();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const refreshTags = useCallback(() => {
|
||||
if (query && query.trim() !== "") {
|
||||
db.lookup
|
||||
@@ -190,109 +205,128 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
|
||||
}
|
||||
|
||||
if (id) {
|
||||
for (const noteId of ids) {
|
||||
await db.relations.add(
|
||||
{
|
||||
id: id,
|
||||
type: "tag"
|
||||
},
|
||||
{
|
||||
id: noteId,
|
||||
type: "note"
|
||||
}
|
||||
);
|
||||
}
|
||||
const { selection } = useTagItemSelection.getState();
|
||||
useTagItemSelection.setState({
|
||||
selection: {
|
||||
...selection,
|
||||
[id]: "selected"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useRelationStore.getState().update();
|
||||
sendItemUpdateEvent(id, "tag");
|
||||
useTagStore.getState().refresh();
|
||||
setQuery(undefined);
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error);
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate();
|
||||
};
|
||||
|
||||
const onPress = useCallback(
|
||||
async (id: string) => {
|
||||
for (const noteId of ids) {
|
||||
try {
|
||||
if (!id) return;
|
||||
const isSelected =
|
||||
useTagItemSelection.getState().initialState[id] === "selected";
|
||||
if (isSelected) {
|
||||
await db.relations.unlink(
|
||||
{
|
||||
id: id,
|
||||
type: "tag"
|
||||
},
|
||||
{
|
||||
id: noteId,
|
||||
type: "note"
|
||||
}
|
||||
const onPress = useCallback((id: string) => {
|
||||
if (!id) return;
|
||||
const { selection } = useTagItemSelection.getState();
|
||||
useTagItemSelection.setState({
|
||||
selection: {
|
||||
...selection,
|
||||
[id]: selection[id] === "selected" ? "deselected" : "selected"
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onSave = useCallback(async () => {
|
||||
const { selection, initialState } = useTagItemSelection.getState();
|
||||
const changedIds = new Set([
|
||||
...Object.keys(selection),
|
||||
...Object.keys(initialState)
|
||||
]);
|
||||
|
||||
try {
|
||||
for (const id of changedIds) {
|
||||
if (selection[id] === initialState[id]) continue;
|
||||
|
||||
const shouldLink = selection[id] === "selected";
|
||||
for (const noteId of ids) {
|
||||
if (shouldLink) {
|
||||
await db.relations.add(
|
||||
{ id, type: "tag" },
|
||||
{ id: noteId, type: "note" }
|
||||
);
|
||||
} else {
|
||||
await db.relations.add(
|
||||
{
|
||||
id: id,
|
||||
type: "tag"
|
||||
},
|
||||
{
|
||||
id: noteId,
|
||||
type: "note"
|
||||
}
|
||||
await db.relations.unlink(
|
||||
{ id, type: "tag" },
|
||||
{ id: noteId, type: "note" }
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
sendItemUpdateEvent(id, "tag");
|
||||
}
|
||||
sendItemUpdateEvent(id, "tag");
|
||||
|
||||
useTagStore.getState().refresh();
|
||||
useRelationStore.getState().update();
|
||||
refreshTags();
|
||||
setTimeout(() => {
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}, 1);
|
||||
refreshSelection();
|
||||
},
|
||||
[ids, refreshSelection, refreshTags]
|
||||
);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
Navigation.goBack();
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error);
|
||||
}
|
||||
}, [ids]);
|
||||
|
||||
const renderTag = useCallback(
|
||||
({ index, item }: { item: Tag; index: number }) => (
|
||||
({ item }: { item: Tag; index: number }) => (
|
||||
<TagItem tag={item} onPress={onPress} />
|
||||
),
|
||||
[onPress]
|
||||
);
|
||||
|
||||
const hasChanges = useTagItemSelection((state) => {
|
||||
const norm = (v?: string) =>
|
||||
v === "selected" ? "s" : v === "intermediate" ? "i" : "n";
|
||||
const changedIds = new Set([
|
||||
...Object.keys(state.selection),
|
||||
...Object.keys(state.initialState)
|
||||
]);
|
||||
for (const id of changedIds) {
|
||||
if (norm(state.selection[id]) !== norm(state.initialState[id])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
backgroundColor: colors.primary.background,
|
||||
gap: DefaultAppStyles.GAP_VERTICAL,
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<Header title={strings.manageTags()} canGoBack />
|
||||
<Header
|
||||
style={{
|
||||
backgroundColor: "transparent"
|
||||
}}
|
||||
title={strings.manageTags()}
|
||||
canGoBack
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
flex: 1
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
flex: 1,
|
||||
gap: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
button={{
|
||||
icon: "magnify",
|
||||
color: colors.primary.accent,
|
||||
size: AppFontSize.lg,
|
||||
icon: "search",
|
||||
iconFamily: "notesnook",
|
||||
color: colors.primary.icon,
|
||||
size: 16,
|
||||
onPress: () => {}
|
||||
}}
|
||||
containerStyle={{
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderWidth: 0
|
||||
}}
|
||||
testID="tag-input"
|
||||
fwdRef={inputRef}
|
||||
autoCapitalize="none"
|
||||
@@ -309,28 +343,12 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
|
||||
placeholder={strings.searchForTags()}
|
||||
/>
|
||||
|
||||
{query && !queryExists ? (
|
||||
<Pressable
|
||||
key={"query_item"}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
onPress={onSubmit}
|
||||
type="selected"
|
||||
>
|
||||
<Heading size={AppFontSize.sm} color={colors.selected.heading}>
|
||||
{strings.add()} {'"' + "#" + query + '"'}
|
||||
</Heading>
|
||||
<Icon
|
||||
name="plus"
|
||||
color={colors.selected.icon}
|
||||
size={AppFontSize.lg}
|
||||
/>
|
||||
</Pressable>
|
||||
) : null}
|
||||
<View
|
||||
style={{
|
||||
height: 1,
|
||||
backgroundColor: colors.primary.border
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
@@ -344,6 +362,37 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
|
||||
keyboardDismissMode="interactive"
|
||||
estimatedItemSize={50}
|
||||
renderItem={renderTag}
|
||||
ListHeaderComponent={
|
||||
query && !queryExists ? (
|
||||
<Pressable
|
||||
key={"query_item"}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: Spacing.LEVEL_2,
|
||||
borderRadius: Radius.XS,
|
||||
marginBottom: Spacing.LEVEL_0
|
||||
}}
|
||||
onPress={onSubmit}
|
||||
type="selected"
|
||||
>
|
||||
<Heading
|
||||
fontSize="SM"
|
||||
fontFamily="MEDIUM"
|
||||
color={colors.selected.heading}
|
||||
>
|
||||
{strings.add()} {'"' + "#" + query + '"'}
|
||||
</Heading>
|
||||
<AppIcon
|
||||
name="plus"
|
||||
iconFamily="material"
|
||||
color={colors.selected.icon}
|
||||
size={AppFontSize.md}
|
||||
/>
|
||||
</Pressable>
|
||||
) : null
|
||||
}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
@@ -368,6 +417,23 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.primary.border,
|
||||
backgroundColor: colors.primary.background,
|
||||
padding: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
title={strings.saveChanges()}
|
||||
type="accent"
|
||||
width="100%"
|
||||
disabled={!hasChanges}
|
||||
onPress={onSave}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
@@ -389,65 +455,49 @@ const TagItem = ({
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const selection = useTagItemSelection((state) =>
|
||||
tag?.id ? state.selection[tag?.id] : false
|
||||
tag?.id ? state.selection[tag?.id] : undefined
|
||||
);
|
||||
const selected = selection === "selected" || selection === "intermediate";
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginVertical: 5,
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_1,
|
||||
justifyContent: "flex-start",
|
||||
height: 40
|
||||
padding: Spacing.LEVEL_2,
|
||||
borderRadius: Radius.XS,
|
||||
marginBottom: Spacing.LEVEL_0
|
||||
}}
|
||||
onPress={() => {
|
||||
if (!tag) return;
|
||||
onPress(tag.id);
|
||||
}}
|
||||
type="plain"
|
||||
type={selected ? "selected" : "transparent"}
|
||||
>
|
||||
{!tag ? null : (
|
||||
<Icon
|
||||
size={22}
|
||||
onPress={() => {
|
||||
if (!tag) return;
|
||||
onPress(tag.id);
|
||||
}}
|
||||
color={
|
||||
selection === "selected" || selection === "intermediate"
|
||||
? colors.selected.icon
|
||||
: colors.primary.icon
|
||||
}
|
||||
style={{
|
||||
marginRight: 6
|
||||
}}
|
||||
testID={
|
||||
selection === "selected"
|
||||
? "check-circle-outline"
|
||||
: selection === "intermediate"
|
||||
? "minus-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
name={
|
||||
selection === "selected"
|
||||
? "check-circle-outline"
|
||||
: selection === "intermediate"
|
||||
? "minus-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{tag ? (
|
||||
<Paragraph size={AppFontSize.sm}>{"#" + tag?.title}</Paragraph>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
width: 200,
|
||||
height: 30,
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<AppIcon
|
||||
name={
|
||||
selection === "selected"
|
||||
? "checkbox"
|
||||
: selection === "intermediate"
|
||||
? "checkbox-intermediate"
|
||||
: "box-empty"
|
||||
}
|
||||
iconFamily="notesnook"
|
||||
size={16}
|
||||
color={
|
||||
selected
|
||||
? [colors.selected.accent, colors.static.white]
|
||||
: colors.secondary.icon
|
||||
}
|
||||
/>
|
||||
<Paragraph
|
||||
fontSize="SM"
|
||||
color={selected ? colors.primary.heading : colors.secondary.paragraph}
|
||||
>
|
||||
{"#" + tag?.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
1
packages/icons/svgs/checkbox-intermediate.svg
Normal file
1
packages/icons/svgs/checkbox-intermediate.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Design Icons by Pictogrammers - https://github.com/Templarian/MaterialDesign/blob/master/LICENSE --><path fill="currentColor" d="M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m0 16H5V5h14zm-2-2H7V7h10z"/></svg>
|
||||
|
After Width: | Height: | Size: 342 B |
@@ -6209,6 +6209,10 @@ msgstr "Save account recovery key"
|
||||
msgid "Save and continue"
|
||||
msgstr "Save and continue"
|
||||
|
||||
#: src/strings.ts:2942
|
||||
msgid "Save changes"
|
||||
msgstr "Save changes"
|
||||
|
||||
#: src/strings.ts:1066
|
||||
msgid "Save data recovery key"
|
||||
msgstr "Save data recovery key"
|
||||
|
||||
@@ -6183,6 +6183,10 @@ msgstr ""
|
||||
msgid "Save and continue"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2942
|
||||
msgid "Save changes"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:1066
|
||||
msgid "Save data recovery key"
|
||||
msgstr ""
|
||||
|
||||
@@ -2938,5 +2938,6 @@ Continue without attachments?`,
|
||||
t`Something went wrong while preparing your debug logs. This can happen if no storage location was selected.`,
|
||||
shareZip: () => t`Share ZIP`,
|
||||
tryDownloadLogsAgain: () => t`Try again`,
|
||||
createSubnotebook: () => t`Create sub-notebook`
|
||||
createSubnotebook: () => t`Create sub-notebook`,
|
||||
saveChanges: () => t`Save changes`
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user