diff --git a/src/App.tsx b/src/App.tsx
index 4ba8f59..fbe132f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -41,6 +41,7 @@ const rootStackScreens: RootStackScreens = {
NoteCreate: "new-note",
NoteEdit: "notebook/:colUid/note/:itemUid",
NoteProps: "notebook/:colUid/note/:itemUid/properties",
+ NoteMove: "notebook/:colUid/note/:itemUid/move",
Invitations: "invitations",
Settings: "settings",
About: "settings/about",
diff --git a/src/RootNavigator.tsx b/src/RootNavigator.tsx
index 4625ad0..e00cdde 100644
--- a/src/RootNavigator.tsx
+++ b/src/RootNavigator.tsx
@@ -17,6 +17,7 @@ import DebugLogsScreen from "./screens/DebugLogsScreen";
import HomeScreen from "./screens/HomeScreen";
import NoteEditScreen from "./screens/NoteEditScreen";
import NotePropertiesScreen from "./screens/NotePropertiesScreen";
+import NoteMoveScreen from "./screens/NoteMoveScreen";
import CollectionEditScreen from "./screens/CollectionEditScreen";
import CollectionChangelogScreen from "./screens/CollectionChangelogScreen";
import CollectionMembersScreen from "./screens/CollectionMembersScreen";
@@ -113,6 +114,10 @@ export default React.memo(function RootNavigator() {
name="NoteProps"
component={NotePropertiesScreen}
/>
+
navigation.navigate("NoteProps", { colUid, itemUid })}
onDelete={() => setNoteDeleteDialogShow(true)}
+ onMove={() => navigation.navigate("NoteMove", { colUid, itemUid })}
onShare={onShare}
changed={changed}
/>
@@ -246,10 +247,11 @@ interface RightActionViewProps {
onEdit: () => void;
onSave: () => void;
onDelete: () => void;
+ onMove: () => void;
onShare: () => void;
}
-function RightAction({ viewMode, setViewMode, onSave, onEdit, onDelete, onShare, changed }: RightActionViewProps) {
+function RightAction({ viewMode, setViewMode, onSave, onEdit, onDelete, onMove, onShare, changed }: RightActionViewProps) {
const [showMenu, setShowMenu] = React.useState(false);
return (
@@ -276,6 +278,12 @@ function RightAction({ viewMode, setViewMode, onSave, onEdit, onDelete, onShare,
onDelete();
}}
/>
+ {
+ setShowMenu(false);
+ onMove();
+ }}
+ />
{
diff --git a/src/screens/NoteMoveScreen.tsx b/src/screens/NoteMoveScreen.tsx
new file mode 100644
index 0000000..4917847
--- /dev/null
+++ b/src/screens/NoteMoveScreen.tsx
@@ -0,0 +1,197 @@
+// SPDX-FileCopyrightText: © 2019 EteSync Authors
+// SPDX-License-Identifier: GPL-3.0-only
+
+import * as React from "react";
+import { useSelector } from "react-redux";
+import { View } from "react-native";
+import { Text, HelperText, Button, TouchableRipple } from "react-native-paper";
+import { useNavigation, RouteProp } from "@react-navigation/native";
+import { StackNavigationProp } from "@react-navigation/stack";
+
+import { useSyncGate } from "../SyncGate";
+import { useCredentials } from "../credentials";
+import { StoreState, useAsyncDispatch } from "../store";
+import { itemBatch, pushMessage } from "../store/actions";
+import { CachedItem } from "../store/reducers";
+
+import ScrollView from "../widgets/ScrollView";
+import Container from "../widgets/Container";
+import ErrorOrLoadingDialog from "../widgets/ErrorOrLoadingDialog";
+import Select from "../widgets/Select";
+import TextInputWithIcon from "../widgets/TextInputWithIcon";
+import NotFound from "../widgets/NotFound";
+
+import { useLoading } from "../helpers";
+import { RootStackParamList } from "../RootStackParamList";
+
+interface FormErrors {
+ notebook?: string;
+}
+
+type NavigationProp = StackNavigationProp;
+
+interface PropsType {
+ route: RouteProp;
+}
+
+export default function NotePropertiesScreen(props: PropsType) {
+ const colUid = props.route.params?.colUid;
+ const itemUid = props.route.params?.itemUid;
+ const [errors, setErrors] = React.useState({} as FormErrors);
+ const [selectOpen, setSelectOpen] = React.useState(false);
+ const dispatch = useAsyncDispatch();
+ const cachedCollections = useSelector((state: StoreState) => state.cache.collections);
+ const cachedItems = useSelector((state: StoreState) => state.cache.items);
+ const [cachedItem, setCachedItem] = React.useState();
+ const options = Array.from(
+ cachedCollections.map((val, uid) => ({ ...val, uid }))
+ .sort((a, b) => (a.meta!.name!.toUpperCase() >= b.meta!.name!.toUpperCase()) ? 1 : -1)
+ .values()
+ );
+ const [collection, setCollection] = React.useState((options.length > 0) ? options[0] : undefined);
+ const syncGate = useSyncGate();
+ const navigation = useNavigation();
+ const etebase = useCredentials()!;
+ const [loading, error, setPromise] = useLoading();
+
+ React.useEffect(() => {
+ if (syncGate) {
+ return;
+ }
+
+ if (colUid) {
+ const cachedCollection = cachedItems.get(colUid);
+
+ if (cachedCollection) {
+ setCollection(options.find((x) => x.uid === colUid) ?? options[0]);
+
+ if (itemUid) {
+ const cachedItem = cachedCollection.get(itemUid);
+ if (cachedItem) {
+ setCachedItem(cachedItem);
+ } else {
+ setCachedItem(undefined);
+ }
+ }
+ } else {
+ setCollection(undefined);
+ }
+ }
+
+ }, [syncGate, cachedItems, colUid, itemUid]);
+
+ React.useEffect(() => {
+ navigation.setOptions({
+ title: "Move Note",
+ });
+ }, [colUid, itemUid]);
+
+ if (syncGate) {
+ return syncGate;
+ }
+
+ if (colUid && !collection) {
+ return ;
+ }
+ if (itemUid && !cachedItem) {
+ return ;
+ }
+
+ function onMove() {
+ setPromise(async () => {
+ const saveErrors: FormErrors = {};
+ const fieldRequired = "This field is required!";
+
+ if (!collection) {
+ saveErrors.notebook = fieldRequired;
+ } else if (collection.uid === colUid) {
+ saveErrors.notebook = "You must pick a different notebook to move the note!";
+ }
+
+ if (Object.keys(saveErrors).length > 0) {
+ setErrors(saveErrors);
+ return;
+ }
+
+ const colMgr = etebase.getCollectionManager();
+
+ const oldCollection = cachedCollections.get(colUid!)!;
+ const oldCol = colMgr.cacheLoad(oldCollection.cache);
+ const oldItemMgr = colMgr.getItemManager(oldCol);
+ const oldItem = oldItemMgr.cacheLoad(cachedItem!.cache);
+
+ const newCol = colMgr.cacheLoad(collection!.cache);
+ const newItemMgr = colMgr.getItemManager(newCol);
+
+ const meta = oldItem.getMeta();
+ meta.mtime = (new Date()).getTime();
+ const content = await oldItem.getContent();
+ const newItem = await newItemMgr.create(meta, content);
+ await dispatch(itemBatch(newCol, newItemMgr, [newItem]));
+
+ oldItem.setMeta(meta);
+ oldItem.delete(true);
+ await dispatch(itemBatch(oldCol, oldItemMgr, [oldItem]));
+
+ dispatch(pushMessage({ message: "Note moved", severity: "success" }));
+ navigation.navigate("NoteEdit", { colUid: collection!.uid, itemUid: newItem.uid });
+ });
+ }
+
+ return (
+
+
+ setPromise(undefined)}
+ />
+
+
+
+
+
+
+
+ );
+}