finished implementing setting category

This commit is contained in:
ide
2026-08-17 22:47:02 +02:00
parent b3dd373403
commit 495b0de7a7
8 changed files with 1712 additions and 1176 deletions
+2
View File
@@ -3,3 +3,5 @@ target/
**/*.rs.bk
*.sqlite
dabase
Generated
+1650 -1161
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -18,6 +18,8 @@ pub use fs::*;
#[cfg(target_os = "linux")]
mod fs {
use crate::runtime::spawn;
use super::Config;
use anyhow::{Context, Ok};
use serde_json;
@@ -34,7 +36,7 @@ mod fs {
.place_config_file(CONFIG_FILE_NAME)
.context("unable to create config directory")?;
tokio::spawn(async {
spawn(async {
let res = tokio::fs::write(path, conf)
.await
.context("unable to write config file");
+34 -1
View File
@@ -17,7 +17,7 @@ use std::{
use inkopslista_lib::{ButtonItem, Item, ItemData};
use reqwest::Url;
use slint::{Model, ModelRc, SharedString, ToSharedString, VecModel};
use slint::{Model, ModelRc, ToSharedString, VecModel};
use crate::{config::Config, runtime::spawn};
@@ -60,6 +60,26 @@ async fn delete_item(item: &str, conf: &Mutex<Config>) -> Result<(), anyhow::Err
Ok(())
}
async fn put_category(
itemname: &str,
category: &str,
conf: &Mutex<Config>,
) -> Result<(), anyhow::Error> {
let client = reqwest::Client::new();
let url: Url = format!(
"{address}/api/favourites/{itemname}/category",
address = conf.lock().unwrap().address
)
.parse()?;
let _response = client
.put(url)
.body(category.to_string())
.send()
.await?
.error_for_status()?;
Ok(())
}
/// Convert shopping list to slint types
fn update_slint_list(new_list: Vec<Item>, list: &VecModel<SItem>) {
list.clear();
@@ -322,6 +342,19 @@ pub fn run() -> Result<(), anyhow::Error> {
}
});
let conf = conf_shared.clone();
state.on_set_cat(move |itemname, cat| {
let conf = conf.clone();
spawn(async move {
let res = put_category(&itemname, &cat, &conf).await;
if let Err(err) = res {
eprintln!("{err:?}");
}
});
return;
});
state.on_set_server_address(move |address| {
conf_shared.lock().unwrap().address = address.to_string();
#[cfg(target_os = "linux")]
+6 -2
View File
@@ -13,8 +13,6 @@ import {
import { SettingsView } from "settings.slint";
import { State, SItem, SButtonItem } from "state.slint";
export { State, SItem }
component GoodListItem inherits Rectangle {
in-out property <SItem> item;
background: #ffffff10;
@@ -281,16 +279,22 @@ component ShopView inherits VerticalLayout {
component CatItem inherits HorizontalBox {
in-out property <SButtonItem> item;
Text {
text: item.name;
vertical-alignment: center;
font-size: 16px;
}
ComboBox {
model: ["kyl", "skafferi", "frys", "övrigt"];
current-value: item.category;
width: 60%;
selected(category) => { State.set-cat(item.name, category) }
}
}
component CatView inherits VerticalLayout {
in-out property <[SButtonItem]> cats;
+2 -3
View File
@@ -4,9 +4,7 @@ export struct SItem {
amount: int,
}
export struct SButtonItem{
name: string,
category: string}
export struct SButtonItem { name: string, category: string }
export global State {
in-out property <[SItem]> list: [
@@ -20,6 +18,7 @@ export global State {
callback delete-checked();
callback inc-amount(string);
callback dec-amount(string);
callback set-cat(string, string);
in-out property <string> server-address;
in property <[SButtonItem]> favorites: [
{ name: "mjölk", category: "kyl" },
+1
View File
@@ -9,3 +9,4 @@ serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.145"
inkopslista-lib = { path = "../lib" }
sqlite = "0.37.0"
clap = { version = "4.6.6", features = ["derive", "env"] }
+14 -8
View File
@@ -1,8 +1,7 @@
#[macro_use]
extern crate rocket;
use std::{env, path::Path};
use clap::Parser;
use inkopslista_lib::{ButtonItem, Item, ItemData};
use rocket::{
fs::{FileServer, NamedFile},
@@ -11,6 +10,7 @@ use rocket::{
State,
};
use sqlite::ConnectionThreadSafe;
use std::path::{Path, PathBuf};
/// The directory where the web files for the gui is stored.
fn www_path() -> &'static Path {
@@ -53,7 +53,7 @@ fn get_favorites(db: &State<ConnectionThreadSafe>) -> Json<Vec<ButtonItem>> {
FROM adds
WHERE (date(julianday('now')) - date(date_added, 'unixepoch')) < 184
GROUP BY itemname
HAVING day_count > 0)
HAVING day_count > 0)
SELECT favorites.itemname, category FROM favorites LEFT OUTER JOIN categories ON favorites.itemname = categories.itemname";
let list: Vec<ButtonItem> = db
.prepare(query)
@@ -73,8 +73,8 @@ fn get_favorites(db: &State<ConnectionThreadSafe>) -> Json<Vec<ButtonItem>> {
Json(list)
}
#[put("/api/favourites/<itemname>", data = "<category>")]
fn set_categories(db: &State<ConnectionThreadSafe>, itemname: &str, category: &str) {
#[put("/api/favourites/<itemname>/category", data = "<category>")]
fn put_category(db: &State<ConnectionThreadSafe>, itemname: &str, category: &str) {
let query = "INSERT INTO categories VALUES (?, ?) ON CONFLICT DO UPDATE SET category = ?";
let mut statement = db.prepare(query).unwrap();
statement.bind((1, itemname)).unwrap();
@@ -111,10 +111,16 @@ fn delete_item(item: &str, db: &State<ConnectionThreadSafe>) {
while let Ok(sqlite::State::Row) = statement.next() {}
}
#[derive(Parser)]
struct Opt {
#[clap(long, env = "DB_PATH")]
db_path: PathBuf,
}
#[launch]
fn rocket() -> _ {
let db_path = env::var("DB_PATH").unwrap();
let db = sqlite::Connection::open_thread_safe(db_path).unwrap();
let opt = Opt::parse();
let db = sqlite::Connection::open_thread_safe(opt.db_path).unwrap();
db.execute(include_str!("db/init.sql"))
.expect("unable to initialize database");
rocket::build()
@@ -132,7 +138,7 @@ fn rocket() -> _ {
put_list,
delete_item,
get_favorites,
set_categories
put_category
],
)
.mount("/", FileServer::from(www_path()))