Skip to content

Commit

Permalink
Merge pull request #7 from pfitzer/main
Browse files Browse the repository at this point in the history
v0.3.0
  • Loading branch information
pfitzer authored Jan 26, 2024
2 parents a9a174d + e130c08 commit 9edefc5
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 48 deletions.
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/src-tauri"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"

4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Simple desktop notes app for learning [Tauri](https://tauri.app/) and [React](https://react.dev/)

#### development

Make sure you have the latest version of [Rust](https://www.rust-lang.org/learn/get-started) installed.
```bash
git clone https://github.com/pfitzer/notes.git
cd notes
Expand All @@ -13,7 +15,7 @@ cd notes
npm install

#run
npm tauri run dev
npm run tauri dev
```

## Demo
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "notes"
version = "0.2.0"
version = "0.3.0"
description = "A Tauri App"
authors = ["Michael Pfister <michael@mp-development.de>"]
license = "MIT"
Expand All @@ -13,7 +13,7 @@ edition = "2021"
tauri-build = { version = "1.5", features = [] }

[dependencies]
tauri = { version = "1.5", features = [ "fs-write-file", "path-all", "dialog-save", "notification-all", "clipboard-write-text", "shell-open"] }
tauri = { version = "1.5", features = [ "window-close", "dialog-save", "dialog-confirm", "fs-write-file", "path-all", "notification-all", "clipboard-write-text", "shell-open"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
markdown = "0.3.0"
Expand Down
59 changes: 45 additions & 14 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,44 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]


use tauri::{CustomMenuItem, Menu, Submenu, Window};
use tauri::{CustomMenuItem, Manager, Menu, Submenu, Window};
use tauri::api::dialog::message;
use tauri_plugin_sql::{Migration, MigrationKind};

fn main() {
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
let about = CustomMenuItem::new("about".to_string(), "About");
let submenu = Submenu::new("File", Menu::new().add_item(about).add_item(quit));
let menu = Menu::new()
.add_submenu(submenu);

let migrations = vec![
// Define your migrations here
Migration {
version: 1,
description: "add new column title",
sql: "ALTER TABLE notes ADD COLUMN title TEXT;",
kind: MigrationKind::Up,
}
];

tauri::Builder::default()
.menu(menu)
.on_menu_event(|event| {
match event.menu_item_id() {
"quit" => {
std::process::exit(0);
}
"about" => {
call_about(event.window().clone());
}
_ => {}
}
})
.invoke_handler(tauri::generate_handler![convert_markdown, open_editor])
.plugin(tauri_plugin_sql::Builder::default().build())
.plugin(tauri_plugin_sql::Builder::default()
.add_migrations("sqlite:notes.sqlite", migrations)
.build())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Expand All @@ -22,19 +54,18 @@ fn convert_markdown(text: &str) -> String {
async fn open_editor(handle: tauri::AppHandle, editor_id: &str) -> Result<(), tauri::Error> {
let editor_window: Window = tauri::WindowBuilder::new(
&handle, editor_id, tauri::WindowUrl::App(("editor/".to_string() + editor_id).parse().unwrap()),
)
.menu(Menu::new().add_submenu(
Submenu::new(
"File", Menu::new()
.add_item(CustomMenuItem::new(
"export", "Export File",
)
.accelerator("cmdOrControl+E")
),
)))
.build()
.unwrap();
).build().unwrap();

editor_window.set_title("Editor").unwrap();
Ok(())
}

#[tauri::command]
fn call_about(window: tauri::Window) {
let label = window.label();
let parent_window = window.get_window(label).unwrap();
tauri::async_runtime::spawn(async move {
message(Some(&parent_window), "About", "MIT License\n\nCopyright (c) 2024 Michael Pfister
");
});
}
8 changes: 6 additions & 2 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"package": {
"productName": "notes",
"version": "0.2.0"
"version": "0.3.0"
},
"tauri": {
"allowlist": {
Expand All @@ -23,13 +23,17 @@
"all": true
},
"dialog": {
"save": true
"save": true,
"confirm" : true
},
"fs":{
"writeFile": true
},
"path": {
"all": true
},
"window": {
"close":true
}
},
"bundle": {
Expand Down
16 changes: 8 additions & 8 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ function App() {
let headline;
const noteItems = notes.map((item) =>
<div key={item.note_id}
className="p-1 flex flex-row justify-between items-center bg-green-700">
<div className="bg-green-200 cursor-pointer w-full h-full min-h-6" onClick={async () => {
className="p-1 flex flex-row justify-between items-center bg-primary rounded-md text-white mb-1">
<div className="cursor-pointer w-full h-full min-h-6" onClick={async () => {
await handleOpenWindow(item.note_id)
}}>{item.note_text.split('\n')[0]}</div>
}}>{item.title}</div>
<button className="btn btn-sm btn-square btn-ghost" onClick={() => {
handleRemoveNote(item.note_id)
}}>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="#f20707" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
className="lucide lucide-trash-2">
<path d="M3 6h18"/>
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/>
Expand Down Expand Up @@ -85,7 +85,7 @@ function App() {

async function addNote() {
const newId = crypto.randomUUID();
await addNoteDB(db, newId, "");
await addNoteDB(db, newId, {title: 'NEW NOTE', note_text: ''});
await loadNotes(db);
}

Expand All @@ -112,9 +112,9 @@ function App() {
</svg>
</button>
</div>
<input className="my-2 w-full input input-sm" onChange={(e) => {
handleSearch(e)
}}></input>
<input placeholder="search" className="my-2 w-full input input-sm" onChange={(e) => {
handleSearch(e)
}}></input>
{noteItems}
</div>
);
Expand Down
67 changes: 53 additions & 14 deletions src/Editor.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import {useEffect, useState} from "react";
import {invoke} from "@tauri-apps/api";
import {writeText} from "@tauri-apps/api/clipboard";
import {
isPermissionGranted, requestPermission, sendNotification
} from "@tauri-apps/api/notification";
import {isPermissionGranted, requestPermission, sendNotification} from "@tauri-apps/api/notification";
import {useLoaderData} from "react-router-dom";
import Database from "tauri-plugin-sql-api";
import {updateNoteDB} from "./functions/db.js";
import {listen} from "@tauri-apps/api/event";
import {save} from "@tauri-apps/api/dialog";
import {confirm, save} from "@tauri-apps/api/dialog";
import {writeTextFile} from "@tauri-apps/api/fs";
import {DBNAME} from "./functions/constants.js";
import MDEditor from '@uiw/react-md-editor';
import {appWindow} from "@tauri-apps/api/window";

export async function loader({params}) {
const noteID = params.noteID;
Expand All @@ -20,11 +19,30 @@ export async function loader({params}) {

function Editor() {
const {noteUUID} = useLoaderData();
const [note, setNote] = useState("")
const [note, setNote] = useState({})
const [isRendered, setRender] = useState(false)
const [markdownHtml, setMarkdownHtml] = useState("")
const [db, setDB] = useState("")
const [menuEventPayload, setEventPayload] = useState("");
const [isSaved, setIsSaved] = useState(true);

useEffect(() => {
if (!isSaved) {
const unlisten = async () => {
await appWindow.onCloseRequested(async (event) => {
const response = await confirm(
"The current note is unsaved, do you really wan`t to close the editor?",
{title: 'warning', type: 'warning'}
)

if (!response) {
event.preventDefault();
}
})
}
unlisten();
}
}, [isSaved]);

useEffect(() => {
loadNoteFromDB();
Expand Down Expand Up @@ -62,7 +80,7 @@ function Editor() {
filters: [{name: "Markdown", extensions: ["md"]}]
});

await writeTextFile({contents: note, path: filePath});
await writeTextFile({contents: note.note_text, path: filePath});
} catch (e) {
console.log(e);
}
Expand All @@ -71,13 +89,13 @@ function Editor() {
async function loadNoteFromDB() {
const loadedDB = await Database.load("sqlite:" + DBNAME);
const result = await loadedDB.select("SELECT * FROM notes WHERE note_id = $1;", [noteUUID]);
setNote(result[0].note_text);
setNote(result[0]);
setDB(loadedDB);
}

async function renderMarkdown() {
if (!isRendered) {
const response = await invoke("convert_markdown", {text: note});
const response = await invoke("convert_markdown", {text: note.note_text});
setMarkdownHtml({__html: response});
}
setRender(!isRendered)
Expand All @@ -88,15 +106,15 @@ function Editor() {
<div className="flex justify-between items-center pb-2">
<h1>Editor</h1>
<div className="join">
<label className="btn btn-sm join join-item swap">
<label className="btn btn-sm btn-primary join join-item swap">
<input className="join" onChange={async () => {
await renderMarkdown();
}} type="checkbox"></input>
<div className="swap-on">HTML</div>
<div className="swap-off">MD</div>
</label>
<button className="btn btn-sm join-item" onClick={async () => {
await writeText(note);
<button className="btn btn-sm btn-primary join-item" onClick={async () => {
await writeText(note.note_text);
let permissionGranted = await isPermissionGranted()
if (!permissionGranted) {
const permission = await requestPermission();
Expand All @@ -107,24 +125,45 @@ function Editor() {
}
}}>Copy
</button>
<button className="btn btn-sm join-item" onClick={async () => {
<button className="btn btn-sm btn-primary join-item" onClick={async () => {
setIsSaved(true);
await updateNoteDB(db, noteUUID, note);
}}>Save
</button>
<button className="btn btn-sm btn-primary join-item" onClick={async () => {
await saveToFile();
}}>Export
</button>
</div>
</div>
{isRendered ?
<div className="prose" dangerouslySetInnerHTML={markdownHtml}></div>
:
<div className="w-full h-full">
<div className="md:flex md:items-center mb-6">
<div className="md:w-1/6">
<label className="block text-gray-500 font-bold mb-1 md:mb-0 pr-2"
htmlFor="title">Title</label>
</div>
<div className="md:w-5/6">
<input
className="bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white focus:border-gray-800"
name="title" id="title" value={note.title} onChange={(e) => {
setNote({...note, title: e.target.value});
setIsSaved(false);
}}/>
</div>
</div>
<MDEditor
value={note}
value={note.note_text}
height={450}
preview="edit"
visibleDragbar={false}
textareaProps={{rows: 50, placeholder: "Please enter Markdown text"}}
onChange={(value, viewUpdate) => {
setNote(value);
note.note_text = value;
setNote({...note, note_text: value});
setIsSaved(false);
}}
/>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/functions/db.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { emit} from "@tauri-apps/api/event";

export async function addNoteDB(db, uuid, text) {
return await db.execute("INSERT INTO notes (note_id, note_text) VALUES ($1, $2);", [uuid, text]);
export async function addNoteDB(db, uuid, note) {
return await db.execute("INSERT INTO notes (note_id, title, note_text) VALUES ($1, $2, $3);", [uuid, note.title, note.note_text]);
}

export async function updateNoteDB(db, uuid, text) {
const result = await db.execute("UPDATE notes SET note_text = $2 WHERE note_id = $1;", [uuid, text]);
export async function updateNoteDB(db, uuid, note) {
const result = await db.execute("UPDATE notes SET note_text = $2, title = $3 WHERE note_id = $1;", [uuid, note.note_text, note.title]);
await emit("db", {message: "save"});
return result;
}
Expand All @@ -15,5 +15,5 @@ export async function removeNoteDB(db, uuid, text) {
}

export async function getSearch(db, input) {
return await db.select("SELECT * FROM notes WHERE note_text LIKE '%" + input + "%'");
return await db.select("SELECT * FROM notes WHERE note_text LIKE '%" + input + "%' OR title LIKE '%" + input + "%' ");
}

0 comments on commit 9edefc5

Please sign in to comment.