mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
mobile: fix notebooks & colors
This commit is contained in:
committed by
Abdullah Atta
parent
5afdd88eb7
commit
920598f943
@@ -18,27 +18,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { PropsWithChildren } from "react";
|
||||
import { KeyboardAvoidingView, Platform } from "react-native";
|
||||
import { Platform, View, useWindowDimensions } from "react-native";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
|
||||
|
||||
export const Container = ({ children }: PropsWithChildren) => {
|
||||
const floating = useIsFloatingKeyboard();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const dimensions = useWindowDimensions();
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior="padding"
|
||||
enabled={Platform.OS === "ios" && !floating}
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
overflow: "hidden",
|
||||
paddingTop: Platform.OS === "android" ? 0 : insets.top,
|
||||
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom
|
||||
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom,
|
||||
height: dimensions.height,
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,131 +16,27 @@ 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 { Color } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import ColorPickerWheel from "react-native-wheel-color-picker";
|
||||
import { db } from "../../../common/database";
|
||||
import { useStoredRef } from "../../../hooks/use-stored-ref";
|
||||
import { ToastManager } from "../../../services/event-manager";
|
||||
import { useMenuStore } from "../../../stores/use-menu-store";
|
||||
import { useRelationStore } from "../../../stores/use-relation-store";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import BaseDialog from "../../dialog/base-dialog";
|
||||
import DialogContainer from "../../dialog/dialog-container";
|
||||
import { Toast } from "../../toast";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import { Color } from "@notesnook/core";
|
||||
const arrayOfColors = [
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7",
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#339DFF",
|
||||
"#FF33E9",
|
||||
"#E9FF33",
|
||||
"#FF3395",
|
||||
"#95FF33",
|
||||
"#FF3369",
|
||||
"#6933FF",
|
||||
"#33FFC7"
|
||||
];
|
||||
|
||||
const HEX_COLOR_REGEX_ALPHA =
|
||||
/^#(?:(?:[\da-fA-F]{3}){1,2}|(?:[\da-fA-F]{4}){1,2})$/;
|
||||
|
||||
const convertToColorObjects = (colors: string[]) => {
|
||||
const colorObjects = [];
|
||||
for (let i = 0; i < colors.length; i += 3) {
|
||||
colorObjects.push({
|
||||
colorOne: colors[i],
|
||||
colorTwo: colors[i + 1],
|
||||
colorThree: colors[i + 2]
|
||||
});
|
||||
}
|
||||
return colorObjects;
|
||||
};
|
||||
|
||||
const ColorPicker = ({
|
||||
visible,
|
||||
setVisible,
|
||||
@@ -154,43 +50,7 @@ const ColorPicker = ({
|
||||
const { colors } = useThemeColors();
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
const title = useRef<string>();
|
||||
|
||||
const renderItem = ({
|
||||
item
|
||||
}: {
|
||||
item: { colorOne: string; colorTwo: string; colorThree: string };
|
||||
}) => (
|
||||
<View>
|
||||
{Object.keys(item).map((key) => (
|
||||
<PressableButton
|
||||
key={item[key as keyof typeof item]}
|
||||
type="accent"
|
||||
accentColor={item[key as keyof typeof item]}
|
||||
customStyle={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 10,
|
||||
marginBottom: 10
|
||||
}}
|
||||
onPress={() => {
|
||||
const color = item[key as keyof typeof item];
|
||||
setSelectedColor(color);
|
||||
inputRef.current?.setNativeProps({
|
||||
placeholder: color,
|
||||
text: color
|
||||
});
|
||||
}}
|
||||
>
|
||||
{selectedColor === item[key as keyof typeof item] ? (
|
||||
<Icon name="check" color="white" size={SIZE.lg} />
|
||||
) : null}
|
||||
</PressableButton>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
const colorRef = useStoredRef("color-ref", "#f0f0f0");
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
@@ -205,101 +65,127 @@ const ColorPicker = ({
|
||||
<Toast context="color-picker" />
|
||||
<DialogContainer
|
||||
style={{
|
||||
paddingTop: 0
|
||||
paddingTop: 0,
|
||||
maxHeight: 600
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
padding: 20
|
||||
}}
|
||||
>
|
||||
<FlashList
|
||||
extraData={selectedColor}
|
||||
horizontal
|
||||
data={convertToColorObjects(arrayOfColors)}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
|
||||
<View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingTop: 10,
|
||||
columnGap: 10,
|
||||
marginBottom: 10
|
||||
width: "100%",
|
||||
height: 250,
|
||||
backgroundColor: selectedColor,
|
||||
borderRadius: 10
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
fwdRef={inputRef}
|
||||
placeholder="#f0f0f0"
|
||||
marginBottom={0}
|
||||
onChangeText={(value) => {
|
||||
if (HEX_COLOR_REGEX_ALPHA.test(value)) {
|
||||
setSelectedColor(value);
|
||||
inputRef.current?.setNativeProps({
|
||||
placeholder: value,
|
||||
text: value
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<PressableButton
|
||||
type="accent"
|
||||
accentColor={selectedColor || colors.secondary.background}
|
||||
customStyle={{
|
||||
width: 45,
|
||||
height: 45,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
<ColorPickerWheel
|
||||
onColorChangeComplete={(color) => {
|
||||
if (color === selectedColor) return;
|
||||
colorRef.current = color;
|
||||
setSelectedColor(color);
|
||||
inputRef.current?.setNativeProps({
|
||||
placeholder: color,
|
||||
text: color
|
||||
});
|
||||
}}
|
||||
useNativeLayout={true}
|
||||
autoResetSlider={true}
|
||||
color={colorRef.current}
|
||||
sliderSize={30}
|
||||
thumbSize={25}
|
||||
shadeWheelThumb
|
||||
shadeSliderThumb={true}
|
||||
swatchesOnly={false}
|
||||
swatches={false}
|
||||
useNativeDriver={true}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Input
|
||||
marginBottom={10}
|
||||
onChangeText={(value) => {
|
||||
title.current = value;
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
}}
|
||||
placeholder={title.current || "Color title"}
|
||||
/>
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingTop: 10,
|
||||
columnGap: 10,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
fwdRef={inputRef}
|
||||
placeholder="#f0f0f0"
|
||||
marginBottom={0}
|
||||
onChangeText={(value) => {
|
||||
if (HEX_COLOR_REGEX_ALPHA.test(value)) {
|
||||
colorRef.current = value;
|
||||
setSelectedColor(value);
|
||||
}
|
||||
}}
|
||||
defaultValue={colorRef.current}
|
||||
/>
|
||||
<PressableButton
|
||||
type="accent"
|
||||
accentColor={selectedColor || colors.secondary.background}
|
||||
customStyle={{
|
||||
width: 45,
|
||||
height: 45,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
title="Add color"
|
||||
onPress={async () => {
|
||||
if (!selectedColor)
|
||||
return ToastManager.error(
|
||||
new Error("Select a color"),
|
||||
undefined,
|
||||
"color-picker"
|
||||
);
|
||||
if (!title.current)
|
||||
return ToastManager.error(
|
||||
new Error("Enter a title for the color")
|
||||
);
|
||||
const exists = await db.colors.all.find((v) =>
|
||||
v.and([v(`colorCode`, "==", selectedColor)])
|
||||
);
|
||||
if (exists)
|
||||
return ToastManager.error(
|
||||
new Error(`Color #${selectedColor} already exists`)
|
||||
);
|
||||
<Input
|
||||
marginBottom={10}
|
||||
onChangeText={(value) => {
|
||||
title.current = value;
|
||||
}}
|
||||
defaultValue={title.current}
|
||||
placeholder={title.current || "Color title"}
|
||||
/>
|
||||
|
||||
const id = await db.colors.add({
|
||||
title: title.current,
|
||||
colorCode: selectedColor
|
||||
});
|
||||
useRelationStore.getState().update();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
setVisible(false);
|
||||
const color = await db.colors.color(id);
|
||||
if (color) {
|
||||
onColorAdded?.(color);
|
||||
}
|
||||
}}
|
||||
type={selectedColor ? "grayAccent" : "grayBg"}
|
||||
width="100%"
|
||||
/>
|
||||
<Button
|
||||
title="Add color"
|
||||
onPress={async () => {
|
||||
if (!selectedColor)
|
||||
return ToastManager.error(
|
||||
new Error("Select a color"),
|
||||
undefined,
|
||||
"color-picker"
|
||||
);
|
||||
if (!title.current)
|
||||
return ToastManager.error(
|
||||
new Error("Enter a title for the color")
|
||||
);
|
||||
const exists = await db.colors.all.find((v) =>
|
||||
v.and([v(`colorCode`, "==", selectedColor)])
|
||||
);
|
||||
if (exists)
|
||||
return ToastManager.error(
|
||||
new Error(`Color #${selectedColor} already exists`)
|
||||
);
|
||||
|
||||
const id = await db.colors.add({
|
||||
title: title.current,
|
||||
colorCode: selectedColor
|
||||
});
|
||||
useRelationStore.getState().update();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
setVisible(false);
|
||||
const color = await db.colors.color(id);
|
||||
if (color) {
|
||||
onColorAdded?.(color);
|
||||
}
|
||||
}}
|
||||
type={selectedColor ? "grayAccent" : "grayBg"}
|
||||
width="100%"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</DialogContainer>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -26,6 +26,7 @@ import { presentSheet } from "../../services/event-manager";
|
||||
import { ColorValues } from "../../utils/colors";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import SheetProvider from "../sheet-provider";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import { ReminderTime } from "../ui/reminder-time";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
@@ -34,7 +35,6 @@ import { Items } from "./items";
|
||||
import Notebooks from "./notebooks";
|
||||
import { Synced } from "./synced";
|
||||
import { TagStrip, Tags } from "./tags";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
|
||||
const Line = ({ top = 6, bottom = 6 }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -225,7 +225,8 @@ Properties.present = async (item, buttons = [], isSheet) => {
|
||||
"add-shortcut",
|
||||
"trash",
|
||||
"default-notebook",
|
||||
"add-notebook"
|
||||
"add-notebook",
|
||||
"move-notes"
|
||||
]);
|
||||
break;
|
||||
case "tag":
|
||||
|
||||
@@ -55,7 +55,10 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
async (payload) => {
|
||||
if (!payload.context) payload.context = "global";
|
||||
if (payload.context !== context) return;
|
||||
setData(payload);
|
||||
setData((state) => {
|
||||
if (state?.onClose) state.onClose();
|
||||
return payload;
|
||||
});
|
||||
setVisible(true);
|
||||
if (payload.editor) {
|
||||
editor.current.refocus = false;
|
||||
|
||||
@@ -174,7 +174,8 @@ export const AddNotebookSheet = ({
|
||||
AddNotebookSheet.present = (
|
||||
notebook?: Notebook,
|
||||
parentNotebook?: Notebook,
|
||||
context?: string
|
||||
context?: string,
|
||||
onClose?: () => void
|
||||
) => {
|
||||
presentSheet({
|
||||
context: context,
|
||||
@@ -184,6 +185,7 @@ AddNotebookSheet.present = (
|
||||
parentNotebook={parentNotebook}
|
||||
close={close}
|
||||
/>
|
||||
)
|
||||
),
|
||||
onClose: onClose
|
||||
});
|
||||
};
|
||||
|
||||
@@ -25,8 +25,10 @@ import { Platform, View, useWindowDimensions } from "react-native";
|
||||
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
import { db } from "../../../common/database";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { createItemSelectionStore } from "../../../stores/item-selection-store";
|
||||
import { updateNotebook } from "../../../utils/notebooks";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Dialog } from "../../dialog";
|
||||
@@ -36,7 +38,8 @@ import { IconButton } from "../../ui/icon-button";
|
||||
import { PressableButton } from "../../ui/pressable";
|
||||
import Seperator from "../../ui/seperator";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { useDBItem } from "../../../hooks/use-db-item";
|
||||
|
||||
const useItemSelectionStore = createItemSelectionStore(true);
|
||||
|
||||
export const MoveNotes = ({
|
||||
notebook,
|
||||
@@ -47,11 +50,11 @@ export const MoveNotes = ({
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const currentNotebook = notebook;
|
||||
|
||||
const selectionCount = useItemSelectionStore(
|
||||
(state) => Object.keys(state.selection)?.length > 0
|
||||
);
|
||||
const { height } = useWindowDimensions();
|
||||
const [selectedNoteIds, setSelectedNoteIds] = useState<string[]>([]);
|
||||
const [notes, setNotes] = useState<VirtualizedGrouping<Note>>();
|
||||
const [existingNoteIds, setExistingNoteIds] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
db.notes?.all.sorted(db.settings.getGroupOptions("notes")).then((notes) => {
|
||||
@@ -61,45 +64,26 @@ export const MoveNotes = ({
|
||||
.from(currentNotebook, "note")
|
||||
.get()
|
||||
.then((existingNotes) => {
|
||||
setExistingNoteIds(
|
||||
existingNotes.map((existingNote) => existingNote.toId)
|
||||
);
|
||||
const selection: { [name: string]: any } = {};
|
||||
existingNotes.forEach((rel) => {
|
||||
selection[rel.toId] = "selected";
|
||||
});
|
||||
useItemSelectionStore.setState({
|
||||
selection: selection,
|
||||
initialState: selection
|
||||
});
|
||||
});
|
||||
}, [currentNotebook]);
|
||||
|
||||
const select = React.useCallback(
|
||||
(id: string) => {
|
||||
const index = selectedNoteIds.indexOf(id);
|
||||
if (index > -1) {
|
||||
setSelectedNoteIds((selectedNoteIds) => {
|
||||
const next = [...selectedNoteIds];
|
||||
next.splice(index, 1);
|
||||
return next;
|
||||
});
|
||||
} else {
|
||||
setSelectedNoteIds((selectedNoteIds) => {
|
||||
const next = [...selectedNoteIds];
|
||||
next.push(id);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
},
|
||||
[selectedNoteIds]
|
||||
);
|
||||
return () => {
|
||||
useItemSelectionStore.getState().reset();
|
||||
};
|
||||
}, [currentNotebook]);
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({ index }: { item: boolean; index: number }) => {
|
||||
return (
|
||||
<SelectableNoteItem
|
||||
id={index}
|
||||
items={notes}
|
||||
select={select}
|
||||
selected={(id) => selectedNoteIds?.indexOf(id) > -1}
|
||||
exists={(id) => existingNoteIds.indexOf(id) > -1}
|
||||
/>
|
||||
);
|
||||
return <SelectableNoteItem id={index} items={notes} />;
|
||||
},
|
||||
[existingNoteIds, notes, select, selectedNoteIds]
|
||||
[notes]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -135,12 +119,12 @@ export const MoveNotes = ({
|
||||
data={notes?.placeholders}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
{selectedNoteIds.length > 0 ? (
|
||||
{selectionCount ? (
|
||||
<Button
|
||||
onPress={async () => {
|
||||
await db.notes?.addToNotebook(
|
||||
currentNotebook.id,
|
||||
...selectedNoteIds
|
||||
...useItemSelectionStore.getState().getSelectedItemIds()
|
||||
);
|
||||
updateNotebook(currentNotebook.id);
|
||||
Navigation.queueRoutesForUpdate();
|
||||
@@ -157,26 +141,28 @@ export const MoveNotes = ({
|
||||
|
||||
const SelectableNoteItem = ({
|
||||
id,
|
||||
items,
|
||||
select,
|
||||
selected,
|
||||
exists
|
||||
items
|
||||
}: {
|
||||
id: string | number;
|
||||
items?: VirtualizedGrouping<Note>;
|
||||
select: (id: string) => void;
|
||||
selected?: (id: string) => boolean;
|
||||
exists: (id: string) => boolean;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [item] = useDBItem(id, "note", items);
|
||||
const selected = useItemSelectionStore((state) =>
|
||||
item?.id ? state.selection[item.id] === "selected" : false
|
||||
);
|
||||
const exists = useItemSelectionStore((state) =>
|
||||
item?.id ? state.initialState[item.id] === "selected" : false
|
||||
);
|
||||
|
||||
return !item || exists(item.id) ? null : (
|
||||
return !item || exists ? null : (
|
||||
<PressableButton
|
||||
testID="listitem.select"
|
||||
onPress={() => {
|
||||
if (!item) return;
|
||||
select(item?.id);
|
||||
useItemSelectionStore
|
||||
.getState()
|
||||
.markAs(item, selected ? "deselected" : "selected");
|
||||
}}
|
||||
type={"transparent"}
|
||||
customStyle={{
|
||||
@@ -194,17 +180,15 @@ const SelectableNoteItem = ({
|
||||
}}
|
||||
onPress={() => {
|
||||
if (!item) return;
|
||||
select(item?.id);
|
||||
useItemSelectionStore
|
||||
.getState()
|
||||
.markAs(item, selected ? "deselected" : "selected");
|
||||
}}
|
||||
name={
|
||||
selected?.(item?.id)
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
selected ? "check-circle-outline" : "checkbox-blank-circle-outline"
|
||||
}
|
||||
type="selected"
|
||||
color={
|
||||
selected?.(item?.id) ? colors.selected.icon : colors.primary.icon
|
||||
}
|
||||
color={selected ? colors.selected.icon : colors.primary.icon}
|
||||
/>
|
||||
|
||||
<View
|
||||
|
||||
@@ -18,13 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { Notebook, VirtualizedGrouping } from "@notesnook/core";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { RefreshControl, View, useWindowDimensions } from "react-native";
|
||||
import ActionSheet, { ActionSheetRef } from "react-native-actions-sheet";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
@@ -37,6 +31,7 @@ import { useNotebook } from "../../../hooks/use-notebook";
|
||||
import NotebookScreen from "../../../screens/notebook";
|
||||
import { openEditor } from "../../../screens/notes/common";
|
||||
import { eSendEvent, presentSheet } from "../../../services/event-manager";
|
||||
import { createItemSelectionStore } from "../../../stores/item-selection-store";
|
||||
import useNavigationStore from "../../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { eOnNotebookUpdated } from "../../../utils/events";
|
||||
@@ -50,18 +45,7 @@ import Paragraph from "../../ui/typography/paragraph";
|
||||
import { AddNotebookSheet } from "../add-notebook";
|
||||
import Sort from "../sort";
|
||||
|
||||
const SelectionContext = createContext<{
|
||||
selection: Notebook[];
|
||||
enabled: boolean;
|
||||
setEnabled: (value: boolean) => void;
|
||||
toggleSelection: (item: Notebook) => void;
|
||||
}>({
|
||||
selection: [],
|
||||
enabled: false,
|
||||
setEnabled: (_value: boolean) => {},
|
||||
toggleSelection: (_item: Notebook) => {}
|
||||
});
|
||||
const useSelection = () => useContext(SelectionContext);
|
||||
const useItemSelectionStore = createItemSelectionStore(true, false);
|
||||
|
||||
type NotebookParentProp = {
|
||||
parent?: NotebookParentProp;
|
||||
@@ -106,10 +90,8 @@ export const NotebookSheet = () => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const currentRoute = useNavigationStore((state) => state.currentRoute);
|
||||
const focusedRouteId = useNavigationStore((state) => state.focusedRouteId);
|
||||
|
||||
const enabled = useItemSelectionStore((state) => state.enabled);
|
||||
const canShow = currentRoute === "Notebook";
|
||||
const [selection, setSelection] = useState<Notebook[]>([]);
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
const { colors } = useThemeColors("sheet");
|
||||
const ref = useRef<ActionSheetRef>(null);
|
||||
const currentItem = useRef<string>();
|
||||
@@ -147,29 +129,6 @@ export const NotebookSheet = () => {
|
||||
/>
|
||||
);
|
||||
|
||||
const selectionContext = {
|
||||
selection: selection,
|
||||
enabled,
|
||||
setEnabled,
|
||||
toggleSelection: (item: Notebook) => {
|
||||
setSelection((state) => {
|
||||
const selection = [...state];
|
||||
const index = selection.findIndex(
|
||||
(selected) => selected.id === item.id
|
||||
);
|
||||
if (index > -1) {
|
||||
selection.splice(index, 1);
|
||||
if (selection.length === 0) {
|
||||
setEnabled(false);
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
selection.push(item);
|
||||
return selection;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (canShow) {
|
||||
setImmediate(async () => {
|
||||
@@ -181,8 +140,10 @@ export const NotebookSheet = () => {
|
||||
"Root changed to",
|
||||
nextRoot
|
||||
);
|
||||
setSelection([]);
|
||||
setEnabled(false);
|
||||
useItemSelectionStore.setState({
|
||||
enabled: false,
|
||||
selection: {}
|
||||
});
|
||||
}
|
||||
currentItem.current = nextRoot;
|
||||
const snapPoint = NotebookSheetConfig.get({
|
||||
@@ -200,8 +161,10 @@ export const NotebookSheet = () => {
|
||||
onRequestUpdate();
|
||||
});
|
||||
} else {
|
||||
setSelection([]);
|
||||
setEnabled(false);
|
||||
useItemSelectionStore.setState({
|
||||
enabled: false,
|
||||
selection: {}
|
||||
});
|
||||
ref.current?.hide();
|
||||
}
|
||||
}, [canShow, onRequestUpdate, focusedRouteId]);
|
||||
@@ -305,12 +268,14 @@ export const NotebookSheet = () => {
|
||||
}}
|
||||
onPress={async () => {
|
||||
await deleteItems(
|
||||
selection.map((notebook) => notebook.id),
|
||||
useItemSelectionStore.getState().getSelectedItemIds(),
|
||||
"notebook"
|
||||
);
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
useItemSelectionStore.setState({
|
||||
enabled: false,
|
||||
selection: {}
|
||||
});
|
||||
return;
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
@@ -328,8 +293,10 @@ export const NotebookSheet = () => {
|
||||
}}
|
||||
onPress={() => {
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
useItemSelectionStore.setState({
|
||||
enabled: false,
|
||||
selection: {}
|
||||
});
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
tooltipText="Clear selection"
|
||||
@@ -361,7 +328,10 @@ export const NotebookSheet = () => {
|
||||
/>
|
||||
<IconButton
|
||||
name="plus"
|
||||
onPress={PLACEHOLDER_DATA.action}
|
||||
onPress={() => {
|
||||
if (!notebook) return;
|
||||
AddNotebookSheet.present(undefined, notebook, undefined);
|
||||
}}
|
||||
testID="add-notebook-button"
|
||||
color={colors.primary.icon}
|
||||
size={22}
|
||||
@@ -393,39 +363,36 @@ export const NotebookSheet = () => {
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
<SelectionContext.Provider value={selectionContext}>
|
||||
<FlashList
|
||||
data={notebooks?.placeholders}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
estimatedItemSize={50}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={false}
|
||||
onRefresh={() => {
|
||||
eSendEvent(eOnNotebookUpdated);
|
||||
}}
|
||||
colors={[colors.primary.accent]}
|
||||
progressBackgroundColor={colors.primary.background}
|
||||
/>
|
||||
}
|
||||
renderItem={renderNotebook}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.primary.icon}>No notebooks</Paragraph>
|
||||
</View>
|
||||
}
|
||||
ListFooterComponent={<View style={{ height: 50 }} />}
|
||||
/>
|
||||
</SelectionContext.Provider>
|
||||
<FlashList
|
||||
data={notebooks?.placeholders}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
estimatedItemSize={50}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={false}
|
||||
onRefresh={() => {
|
||||
eSendEvent(eOnNotebookUpdated);
|
||||
}}
|
||||
colors={[colors.primary.accent]}
|
||||
progressBackgroundColor={colors.primary.background}
|
||||
/>
|
||||
}
|
||||
renderItem={renderNotebook}
|
||||
ListEmptyComponent={
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.primary.icon}>No notebooks</Paragraph>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</ActionSheet>
|
||||
);
|
||||
@@ -453,9 +420,10 @@ const NotebookItem = ({
|
||||
} = useNotebook(id, items, true);
|
||||
const isFocused = useNavigationStore((state) => state.focusedRouteId === id);
|
||||
const { colors } = useThemeColors("sheet");
|
||||
const selection = useSelection();
|
||||
const isSelected =
|
||||
selection.selection.findIndex((selected) => selected.id === item?.id) > -1;
|
||||
const isSelected = useItemSelectionStore((state) =>
|
||||
item?.id ? state.selection[item.id] === "selected" : false
|
||||
);
|
||||
const enabled = useItemSelectionStore((state) => state.enabled);
|
||||
|
||||
const { fontScale } = useWindowDimensions();
|
||||
const expanded = useNotebookExpandedStore((state) =>
|
||||
@@ -472,15 +440,22 @@ const NotebookItem = ({
|
||||
<PressableButton
|
||||
type={isSelected || isFocused ? "selected" : "transparent"}
|
||||
onLongPress={() => {
|
||||
if (selection.enabled || !item) return;
|
||||
selection.setEnabled(true);
|
||||
selection.toggleSelection(item);
|
||||
if (enabled || !item) return;
|
||||
useItemSelectionStore.setState({
|
||||
enabled: true,
|
||||
selection: {}
|
||||
});
|
||||
useItemSelectionStore
|
||||
.getState()
|
||||
.markAs(item, isSelected ? "deselected" : "selected");
|
||||
}}
|
||||
testID={`notebook-sheet-item-${currentLevel}-${index}`}
|
||||
onPress={() => {
|
||||
if (!item) return;
|
||||
if (selection.enabled) {
|
||||
selection.toggleSelection(item);
|
||||
if (enabled) {
|
||||
useItemSelectionStore
|
||||
.getState()
|
||||
.markAs(item, isSelected ? "deselected" : "selected");
|
||||
return;
|
||||
}
|
||||
NotebookScreen.navigate(item, true);
|
||||
@@ -527,7 +502,7 @@ const NotebookItem = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{selection.enabled ? (
|
||||
{enabled ? (
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
color={isSelected ? colors.selected.icon : colors.primary.icon}
|
||||
|
||||
@@ -38,9 +38,11 @@ import NoteHistory from "../components/note-history";
|
||||
import { AddNotebookSheet } from "../components/sheets/add-notebook";
|
||||
import MoveNoteSheet from "../components/sheets/add-to";
|
||||
import ExportNotesSheet from "../components/sheets/export-notes";
|
||||
import { MoveNotes } from "../components/sheets/move-notes/movenote";
|
||||
import PublishNoteSheet from "../components/sheets/publish-note";
|
||||
import { RelationsList } from "../components/sheets/relations-list/index";
|
||||
import ReminderSheet from "../components/sheets/reminder";
|
||||
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
|
||||
import {
|
||||
ToastManager,
|
||||
eSendEvent,
|
||||
@@ -62,7 +64,6 @@ import { eOpenLoginDialog } from "../utils/events";
|
||||
import { deleteItems } from "../utils/functions";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
import { sleep } from "../utils/time";
|
||||
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
|
||||
|
||||
export const useActions = ({
|
||||
close,
|
||||
@@ -469,6 +470,15 @@ export const useActions = ({
|
||||
close();
|
||||
},
|
||||
on: defaultNotebook === item.id
|
||||
},
|
||||
{
|
||||
id: "move-notes",
|
||||
title: "Move notes",
|
||||
hidden: item.type !== "notebook",
|
||||
icon: "text",
|
||||
func: () => {
|
||||
MoveNotes.present(item);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import { TaggedNotes } from "../screens/notes/tagged";
|
||||
import Reminders from "../screens/reminders";
|
||||
import { Search } from "../screens/search";
|
||||
import Settings from "../screens/settings";
|
||||
import AppLock from "../screens/settings/app-lock";
|
||||
import Tags from "../screens/tags";
|
||||
import Trash from "../screens/trash";
|
||||
import { eSendEvent } from "../services/event-manager";
|
||||
|
||||
@@ -32,7 +32,15 @@
|
||||
"validator": "^13.5.2",
|
||||
"zustand": "^3.6.0",
|
||||
"katex": "0.16.2",
|
||||
"@readme/data-urls": "3.0.0"
|
||||
"@readme/data-urls": "3.0.0",
|
||||
"react-native-wheel-color-picker": "^1.3.1",
|
||||
"tinycolor2": "1.6.0",
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.2",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"kysely": "^0.26.3"
|
||||
},
|
||||
"sideEffects": false
|
||||
}
|
||||
|
||||
@@ -30,9 +30,14 @@ export interface SelectionStore extends State {
|
||||
canEnableMultiSelectMode: boolean;
|
||||
markAs: (item: Item, state: SelectionState | undefined) => void;
|
||||
reset: () => void;
|
||||
enabled?: boolean;
|
||||
getSelectedItemIds: () => string[];
|
||||
}
|
||||
|
||||
export function createItemSelectionStore(multiSelectMode = false) {
|
||||
export function createItemSelectionStore(
|
||||
multiSelectMode = false,
|
||||
enabled = true
|
||||
) {
|
||||
return create<SelectionStore>((set, get) => ({
|
||||
selection: {},
|
||||
setSelection: (state) => {
|
||||
@@ -45,6 +50,7 @@ export function createItemSelectionStore(multiSelectMode = false) {
|
||||
selection: { ...get().initialState }
|
||||
});
|
||||
},
|
||||
enabled: enabled,
|
||||
canEnableMultiSelectMode: multiSelectMode,
|
||||
initialState: {},
|
||||
markAs: (item, state) => {
|
||||
@@ -66,6 +72,18 @@ export function createItemSelectionStore(multiSelectMode = false) {
|
||||
set({
|
||||
multiSelect: !get().multiSelect
|
||||
});
|
||||
},
|
||||
getSelectedItemIds: () => {
|
||||
const selected: string[] = [];
|
||||
|
||||
if (!get().selection) return selected;
|
||||
|
||||
for (const item in get().selection) {
|
||||
if (get().selection[item] === "selected") {
|
||||
selected.push(item);
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"@babel/plugin-transform-named-capturing-groups-regex": "^7.16.5",
|
||||
"@babel/plugin-transform-private-methods": "^7.22.5",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@babel/runtime": "^7.20.0",
|
||||
"@react-native/eslint-config": "^0.72.2",
|
||||
|
||||
889
apps/mobile/package-lock.json
generated
889
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -30,21 +30,13 @@
|
||||
"react-refresh": "0.14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/core-asynciterator-polyfill": "^1.0.2",
|
||||
"@babel/plugin-transform-private-methods": "^7.22.5",
|
||||
"@notesnook/common": "file:../../packages/common",
|
||||
"@notesnook/core": "file:../../packages/core",
|
||||
"@notesnook/editor": "file:../../packages/editor",
|
||||
"@notesnook/editor-mobile": "file:../../packages/editor-mobile",
|
||||
"@notesnook/logger": "file:../../packages/logger",
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"kysely": "^0.26.3",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0",
|
||||
"tinycolor2": "1.6.0"
|
||||
"react-native": "0.72.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
diff --git a/node_modules/react-native-wheel-color-picker/ColorPicker.js b/node_modules/react-native-wheel-color-picker/ColorPicker.js
|
||||
index 5804b4f..fe2d3f0 100644
|
||||
--- a/node_modules/react-native-wheel-color-picker/ColorPicker.js
|
||||
+++ b/node_modules/react-native-wheel-color-picker/ColorPicker.js
|
||||
@@ -706,8 +706,11 @@ module.exports = class ColorPicker extends Component {
|
||||
</View>}
|
||||
{!swatchesOnly && !sliderHidden && (discrete
|
||||
? <View style={[ss.swatches, swatchStyle]} key={'$2'}>{this.disc}</View>
|
||||
- : <View style={[ss.slider, sliderStyle]} key={'$2'}>
|
||||
- <View style={[ss.grad, { backgroundColor: hex }]} key={'$2$1'}>
|
||||
+ : <View style={[ss.slider, sliderStyle, {
|
||||
+ marginBottom: 12,
|
||||
+ paddingHorizontal: 12,
|
||||
+ }]} key={'$2'}>
|
||||
+ <View style={[ss.grad, { backgroundColor: hex, borderRadius: 10 }]} key={'$2$1'}>
|
||||
<Image style={[ss.sliderImg, { opacity: !this.props.sliderLoadingIndicator || this.state.sliderImageLoaded ? 1 : 0 }]} source={row ? srcSliderRotated : srcSlider} onLoad={this.onSliderImageLoad} resizeMode="stretch" />
|
||||
</View>
|
||||
{(this.props.sliderLoadingIndicator ? this.state.sliderImageLoaded : true) && <Animated.View style={[ss.sliderThumb, sliderThumbStyle, Elevations[4], { pointerEvents: 'none' }]} key={'$2$2'} />}
|
||||
Reference in New Issue
Block a user