mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
feat: impl tag create & edit
This commit is contained in:
@@ -166,7 +166,7 @@ test("edit topics individually", async () => {
|
|||||||
await page.click(Menu.new("menuitem").item("edit").build());
|
await page.click(Menu.new("menuitem").item("edit").build());
|
||||||
|
|
||||||
const editedTopicTitle = "Topic " + index + " edit 1";
|
const editedTopicTitle = "Topic " + index + " edit 1";
|
||||||
await page.fill(getTestId("dialog-edit-topic"), editedTopicTitle);
|
await page.fill(getTestId("item-dialog-title"), editedTopicTitle);
|
||||||
|
|
||||||
await page.click(getTestId("dialog-yes"));
|
await page.click(getTestId("dialog-yes"));
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { hashNavigate } from "../navigation";
|
|||||||
import ThemeProvider from "../components/theme-provider";
|
import ThemeProvider from "../components/theme-provider";
|
||||||
import { qclone } from "qclone";
|
import { qclone } from "qclone";
|
||||||
import { store as notebookStore } from "../stores/notebook-store";
|
import { store as notebookStore } from "../stores/notebook-store";
|
||||||
|
import { store as tagStore } from "../stores/tag-store";
|
||||||
import { store as appStore } from "../stores/app-store";
|
import { store as appStore } from "../stores/app-store";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { showToast } from "../utils/toast";
|
import { showToast } from "../utils/toast";
|
||||||
@@ -491,9 +492,8 @@ export function showSignUpDialog() {
|
|||||||
export function showTopicDialog() {
|
export function showTopicDialog() {
|
||||||
return showDialog((Dialogs, perform) => (
|
return showDialog((Dialogs, perform) => (
|
||||||
<Dialogs.TopicDialog
|
<Dialogs.TopicDialog
|
||||||
title={"Create a Topic"}
|
title={"Create topic"}
|
||||||
subtitle={"You can create as many topics as you want."}
|
subtitle={"You can create as many topics as you want."}
|
||||||
icon={Icon.Topic}
|
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
perform(false);
|
perform(false);
|
||||||
}}
|
}}
|
||||||
@@ -502,6 +502,7 @@ export function showTopicDialog() {
|
|||||||
const notebookId = notebookStore.get().selectedNotebookId;
|
const notebookId = notebookStore.get().selectedNotebookId;
|
||||||
await db.notebooks.notebook(notebookId).topics.add(topic);
|
await db.notebooks.notebook(notebookId).topics.add(topic);
|
||||||
notebookStore.setSelectedNotebook(notebookId);
|
notebookStore.setSelectedNotebook(notebookId);
|
||||||
|
showToast("success", "Topic created!");
|
||||||
perform(true);
|
perform(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -515,17 +516,56 @@ export function showEditTopicDialog(notebookId, topicId) {
|
|||||||
if (!topic) return;
|
if (!topic) return;
|
||||||
return showDialog((Dialogs, perform) => (
|
return showDialog((Dialogs, perform) => (
|
||||||
<Dialogs.TopicDialog
|
<Dialogs.TopicDialog
|
||||||
title={"Edit Topic"}
|
title={"Edit topic"}
|
||||||
subtitle={`You are editing "${topic.title}" topic.`}
|
subtitle={`You are editing "${topic.title}" topic.`}
|
||||||
icon={Icon.Topic}
|
icon={Icon.Topic}
|
||||||
topic={topic}
|
item={topic}
|
||||||
onClose={() => perform(false)}
|
onClose={() => perform(false)}
|
||||||
onAction={async (t) => {
|
onAction={async (t) => {
|
||||||
await db.notebooks
|
await db.notebooks
|
||||||
.notebook(topic.notebookId)
|
.notebook(topic.notebookId)
|
||||||
.topics.add({ ...topic, title: t });
|
.topics.add({ ...topic, title: t });
|
||||||
notebookStore.setSelectedNotebook(topic.notebookId);
|
notebookStore.setSelectedNotebook(topic.notebookId);
|
||||||
showToast("success", "Topic edited successfully!");
|
showToast("success", "Topic edited!");
|
||||||
|
perform(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showCreateTagDialog() {
|
||||||
|
return showDialog((Dialogs, perform) => (
|
||||||
|
<Dialogs.ItemDialog
|
||||||
|
title={"Create tag"}
|
||||||
|
subtitle={"You can create multiple tags."}
|
||||||
|
onClose={() => {
|
||||||
|
perform(false);
|
||||||
|
}}
|
||||||
|
onAction={async (title) => {
|
||||||
|
if (!title) return;
|
||||||
|
await db.tags.add(title);
|
||||||
|
showToast("success", "Tag created!");
|
||||||
|
tagStore.refresh();
|
||||||
|
perform(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showEditTagDialog(tagId) {
|
||||||
|
const tag = db.tags.tag(tagId);
|
||||||
|
if (!tag) return;
|
||||||
|
return showDialog((Dialogs, perform) => (
|
||||||
|
<Dialogs.ItemDialog
|
||||||
|
title={"Edit tag"}
|
||||||
|
subtitle={`You are editing #${tag.alias || tag.title}.`}
|
||||||
|
item={tag}
|
||||||
|
onClose={() => perform(false)}
|
||||||
|
onAction={async (title) => {
|
||||||
|
if (!title) return;
|
||||||
|
await db.tags.rename(tagId, title);
|
||||||
|
showToast("success", "Tag edited!");
|
||||||
|
tagStore.refresh();
|
||||||
perform(true);
|
perform(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -52,9 +52,13 @@ export const CREATE_BUTTON_MAP = {
|
|||||||
onClick: () => hashNavigate("/notebooks/create"),
|
onClick: () => hashNavigate("/notebooks/create"),
|
||||||
},
|
},
|
||||||
topics: {
|
topics: {
|
||||||
title: "Add a topic",
|
title: "Create a topic",
|
||||||
onClick: () => hashNavigate(`/topics/create`),
|
onClick: () => hashNavigate(`/topics/create`),
|
||||||
},
|
},
|
||||||
|
tags: {
|
||||||
|
title: "Create a tag",
|
||||||
|
onClick: () => hashNavigate(`/tags/create`),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function introduceFeatures() {
|
export async function introduceFeatures() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import MoveDialog from "./movenotedialog";
|
|||||||
import PasswordDialog from "./passworddialog";
|
import PasswordDialog from "./passworddialog";
|
||||||
import RecoveryKeyDialog from "./recoverykeydialog";
|
import RecoveryKeyDialog from "./recoverykeydialog";
|
||||||
import SignUpDialog from "./signupdialog";
|
import SignUpDialog from "./signupdialog";
|
||||||
import TopicDialog from "./topicdialog";
|
import ItemDialog from "./itemdialog";
|
||||||
import SessionExpiredDialog from "./sessionexpireddialog";
|
import SessionExpiredDialog from "./sessionexpireddialog";
|
||||||
import FeatureDialog from "./feature-dialog";
|
import FeatureDialog from "./feature-dialog";
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ const Dialogs = {
|
|||||||
PasswordDialog,
|
PasswordDialog,
|
||||||
RecoveryKeyDialog,
|
RecoveryKeyDialog,
|
||||||
SignUpDialog,
|
SignUpDialog,
|
||||||
TopicDialog,
|
ItemDialog,
|
||||||
SessionExpiredDialog,
|
SessionExpiredDialog,
|
||||||
FeatureDialog,
|
FeatureDialog,
|
||||||
};
|
};
|
||||||
|
|||||||
46
apps/web/src/components/dialogs/item-dialog.js
Normal file
46
apps/web/src/components/dialogs/item-dialog.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React, { useRef } from "react";
|
||||||
|
import { Box } from "rebass";
|
||||||
|
import { Input } from "@rebass/forms";
|
||||||
|
import Dialog from "./dialog";
|
||||||
|
import Field from "../field";
|
||||||
|
|
||||||
|
function ItemDialog(props) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
isOpen={true}
|
||||||
|
title={props.title}
|
||||||
|
description={props.subtitle}
|
||||||
|
positiveButton={{
|
||||||
|
props: {
|
||||||
|
form: "itemForm",
|
||||||
|
type: "submit",
|
||||||
|
},
|
||||||
|
text: props.title,
|
||||||
|
}}
|
||||||
|
onClose={props.onClose}
|
||||||
|
negativeButton={{ text: "Cancel", onClick: props.onClose }}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
as="form"
|
||||||
|
id="itemForm"
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const title = e.target.title.value;
|
||||||
|
props.onAction(title);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Field
|
||||||
|
required
|
||||||
|
label="Title"
|
||||||
|
id="title"
|
||||||
|
name="title"
|
||||||
|
autoFocus
|
||||||
|
data-test-id="item-dialog-title"
|
||||||
|
defaultValue={props.item?.alias || props.item?.title}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ItemDialog;
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import React, { useRef } from "react";
|
|
||||||
import { Box } from "rebass";
|
|
||||||
import { Input } from "@rebass/forms";
|
|
||||||
import Dialog from "./dialog";
|
|
||||||
|
|
||||||
function TopicDialog(props) {
|
|
||||||
const ref = useRef();
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
isOpen={true}
|
|
||||||
title={props.title}
|
|
||||||
description={props.subtitle}
|
|
||||||
icon={props.icon}
|
|
||||||
positiveButton={{
|
|
||||||
text: "Create topic",
|
|
||||||
onClick: () => {
|
|
||||||
props.onAction(ref.current.value);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onClose={props.onClose}
|
|
||||||
negativeButton={{ text: "Cancel", onClick: props.onClose }}
|
|
||||||
>
|
|
||||||
<Box my={1}>
|
|
||||||
<Input
|
|
||||||
data-test-id="dialog-edit-topic"
|
|
||||||
autoFocus
|
|
||||||
ref={ref}
|
|
||||||
placeholder="Topic title"
|
|
||||||
defaultValue={props.topic && props.topic.title}
|
|
||||||
></Input>
|
|
||||||
</Box>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TopicDialog;
|
|
||||||
@@ -148,7 +148,7 @@ function NavigationMenu(props) {
|
|||||||
{pins.map((pin) => (
|
{pins.map((pin) => (
|
||||||
<NavigationItem
|
<NavigationItem
|
||||||
key={pin.id}
|
key={pin.id}
|
||||||
title={pin.title}
|
title={pin.alias || pin.title}
|
||||||
menu={{
|
menu={{
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ListItem from "../list-item";
|
import ListItem from "../list-item";
|
||||||
import { navigate } from "../../navigation";
|
import { hashNavigate, navigate } from "../../navigation";
|
||||||
import { Text } from "rebass";
|
import { Text } from "rebass";
|
||||||
import { store as appStore } from "../../stores/app-store";
|
import { store as appStore } from "../../stores/app-store";
|
||||||
import { db } from "../../common/db";
|
import { db } from "../../common/db";
|
||||||
import * as Icon from "../icons";
|
import * as Icon from "../icons";
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
|
{
|
||||||
|
title: () => "Edit",
|
||||||
|
icon: Icon.Edit,
|
||||||
|
onClick: ({ tag }) => {
|
||||||
|
hashNavigate(`/tags/${tag.id}/edit`);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: ({ tag }) =>
|
title: ({ tag }) =>
|
||||||
db.settings.isPinned(tag.id) ? "Remove shortcut" : "Create shortcut",
|
db.settings.isPinned(tag.id) ? "Remove shortcut" : "Create shortcut",
|
||||||
@@ -16,13 +23,13 @@ const menuItems = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function Tag({ item, index }) {
|
function Tag({ item, index }) {
|
||||||
const { id, title, noteIds } = item;
|
const { id, title, alias, noteIds } = item;
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
item={item}
|
item={item}
|
||||||
selectable={false}
|
selectable={false}
|
||||||
index={index}
|
index={index}
|
||||||
title={<TagNode title={title} />}
|
title={<TagNode title={alias || title} />}
|
||||||
footer={
|
footer={
|
||||||
<Text mt={1} variant="subBody">
|
<Text mt={1} variant="subBody">
|
||||||
{noteIds.length} notes
|
{noteIds.length} notes
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import React from "react";
|
|||||||
import Vault from "../common/vault";
|
import Vault from "../common/vault";
|
||||||
import {
|
import {
|
||||||
showBuyDialog,
|
showBuyDialog,
|
||||||
|
showCreateTagDialog,
|
||||||
|
showEditTagDialog,
|
||||||
showEmailVerificationDialog,
|
showEmailVerificationDialog,
|
||||||
} from "../common/dialog-controller";
|
} from "../common/dialog-controller";
|
||||||
import {
|
import {
|
||||||
@@ -47,6 +49,12 @@ const hashroutes = {
|
|||||||
"/notebooks/:notebookId/topics/:topicId/edit": ({ notebookId, topicId }) => {
|
"/notebooks/:notebookId/topics/:topicId/edit": ({ notebookId, topicId }) => {
|
||||||
showEditTopicDialog(notebookId, topicId);
|
showEditTopicDialog(notebookId, topicId);
|
||||||
},
|
},
|
||||||
|
"/tags/create": () => {
|
||||||
|
showCreateTagDialog();
|
||||||
|
},
|
||||||
|
"/tags/:tagId/edit": ({ tagId }) => {
|
||||||
|
showEditTagDialog(tagId);
|
||||||
|
},
|
||||||
"/notes/create": () => {
|
"/notes/create": () => {
|
||||||
closeOpenedDialog();
|
closeOpenedDialog();
|
||||||
hashNavigate("/notes/create", { addNonce: true, replace: true });
|
hashNavigate("/notes/create", { addNonce: true, replace: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user