build: migrate to Dioxus 0.6
This commit is contained in:
parent
f56a85277a
commit
a3708198ce
2
.idea/dataSources.local.xml
generated
2
.idea/dataSources.local.xml
generated
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="dataSourceStorageLocal" created-in="RR-242.21829.114">
|
<component name="dataSourceStorageLocal" created-in="RR-243.21565.245">
|
||||||
<data-source name="todo_baggins@localhost" uuid="1658668c-c2b8-426d-a22f-16fbad9eff0b">
|
<data-source name="todo_baggins@localhost" uuid="1658668c-c2b8-426d-a22f-16fbad9eff0b">
|
||||||
<database-info product="PostgreSQL" version="16.4 (Debian 16.4-1.pgdg120+1)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.4" exact-driver-version="42.6">
|
<database-info product="PostgreSQL" version="16.4 (Debian 16.4-1.pgdg120+1)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.4" exact-driver-version="42.6">
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
3818
Cargo.lock
generated
3818
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
49
Cargo.toml
49
Cargo.toml
@ -7,30 +7,39 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dioxus = { version = "0.6.0", features = ["fullstack", "router"] }
|
||||||
|
dioxus-query = "0.6.0"
|
||||||
|
dioxus-i18n = "0.3.0"
|
||||||
|
|
||||||
|
async-std = "1.12.0"
|
||||||
chrono = { version = "0.4.38", features = ["serde", "unstable-locales"] }
|
chrono = { version = "0.4.38", features = ["serde", "unstable-locales"] }
|
||||||
diesel = { version = "2.2.2", features = ["chrono", "postgres", "postgres_backend", "serde_json"] }
|
diesel = { version = "2.2.2", features = ["chrono", "postgres", "postgres_backend", "serde_json"] }
|
||||||
|
|
||||||
dioxus = { version = "0.5", features = ["fullstack", "router"] }
|
|
||||||
|
|
||||||
# Debug
|
|
||||||
dioxus-logger = "0.5.1"
|
|
||||||
dotenvy = "0.15.7"
|
|
||||||
serde = "1.0.208"
|
|
||||||
validator = { version = "0.18.1", features = ["derive"] }
|
|
||||||
serde_json = "1.0.125"
|
|
||||||
tracing = "0.1.40"
|
|
||||||
tracing-wasm = "0.2.1"
|
|
||||||
serde_with = { version = "3.9.0", features = ["chrono_0_4"] }
|
|
||||||
async-std = "1.12.0"
|
|
||||||
dioxus-query = "0.5.1"
|
|
||||||
time = "0.3.36"
|
|
||||||
dioxus-sdk = { version = "0.5.0", features = ["i18n"] }
|
|
||||||
unic-langid-impl = "0.9.5"
|
|
||||||
voca_rs = "1.15.2"
|
|
||||||
diesel_migrations = { version = "2.2.0", features = ["postgres"] }
|
diesel_migrations = { version = "2.2.0", features = ["postgres"] }
|
||||||
|
dotenvy = "0.15.7"
|
||||||
feruca = "0.10.0"
|
feruca = "0.10.0"
|
||||||
|
serde = "1.0.208"
|
||||||
|
serde_json = "1.0.125"
|
||||||
|
serde_with = { version = "3.9.0", features = ["chrono_0_4"] }
|
||||||
|
time = "0.3.36"
|
||||||
|
tracing = "0.1.40"
|
||||||
|
unic-langid-impl = { version = "0.9.5", features = ["serde"] }
|
||||||
|
validator = { version = "0.19.0", features = ["derive"] }
|
||||||
|
voca_rs = "1.15.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["web"]
|
||||||
server = ["dioxus/axum"]
|
desktop = ["dioxus/desktop"]
|
||||||
|
mobile = ["dioxus/mobile"]
|
||||||
web = ["dioxus/web"]
|
web = ["dioxus/web"]
|
||||||
|
|
||||||
|
[profile]
|
||||||
|
|
||||||
|
[profile.wasm-dev]
|
||||||
|
inherits = "dev"
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.server-dev]
|
||||||
|
inherits = "dev"
|
||||||
|
|
||||||
|
[profile.android-dev]
|
||||||
|
inherits = "dev"
|
||||||
|
@ -30,13 +30,7 @@ watch_path = ["src", "assets"]
|
|||||||
[web.resource]
|
[web.resource]
|
||||||
|
|
||||||
# CSS style file
|
# CSS style file
|
||||||
|
style = []
|
||||||
style = [
|
|
||||||
"/styles/tailwind_output.css",
|
|
||||||
"/styles/fonts.css",
|
|
||||||
"/styles/input_number_arrows.css",
|
|
||||||
"/styles/input_range.css"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Javascript code file
|
# Javascript code file
|
||||||
script = ["https://kit.fontawesome.com/3c1b409f8f.js"]
|
script = ["https://kit.fontawesome.com/3c1b409f8f.js"]
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("/fonts/inter_variable.woff2") format("woff2");
|
src: url("/assets/fonts/inter_variable.woff2") format("woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -12,6 +12,6 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 100 900;
|
font-weight: 100 900;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("/fonts/inter_variable_italic.woff2") format("woff2");
|
src: url("/assets/fonts/inter_variable_italic.woff2") format("woff2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ services:
|
|||||||
- ./Cargo.toml:/srv/app/Cargo.toml
|
- ./Cargo.toml:/srv/app/Cargo.toml
|
||||||
- ./diesel.toml:/srv/app/diesel.toml
|
- ./diesel.toml:/srv/app/diesel.toml
|
||||||
- ./Dioxus.toml:/srv/app/Dioxus.toml
|
- ./Dioxus.toml:/srv/app/Dioxus.toml
|
||||||
- ./index.html:/srv/app/index.html
|
|
||||||
- ./package.json:/srv/app/package.json
|
- ./package.json:/srv/app/package.json
|
||||||
- ./package-lock.json:/srv/app/package-lock.json
|
- ./package-lock.json:/srv/app/package-lock.json
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM rust:1.80-bookworm
|
FROM rust:1.83-bookworm
|
||||||
|
|
||||||
RUN rustup target add wasm32-unknown-unknown && \
|
RUN rustup target add wasm32-unknown-unknown && \
|
||||||
cargo install dioxus-cli diesel_cli && \
|
cargo install dioxus-cli diesel_cli && \
|
||||||
@ -16,7 +16,7 @@ RUN chown -R 1000:1000 /srv/app && \
|
|||||||
mkdir -p /.local/share/dioxus && \
|
mkdir -p /.local/share/dioxus && \
|
||||||
chown -R 1000:1000 /.local/share/dioxus
|
chown -R 1000:1000 /.local/share/dioxus
|
||||||
|
|
||||||
HEALTHCHECK CMD curl --fail http://localhost:8000 || exit 1
|
HEALTHCHECK CMD curl --fail -H "Accept: text/html" http://localhost:8000 || exit 1
|
||||||
|
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ stdout_logfile_maxbytes=0
|
|||||||
redirect_stderr=true
|
redirect_stderr=true
|
||||||
|
|
||||||
[program:dx]
|
[program:dx]
|
||||||
command=dx serve
|
command=dx serve --addr 0.0.0.0 --port 8000
|
||||||
directory=/srv/app
|
directory=/srv/app
|
||||||
autostart=true
|
autostart=true
|
||||||
autorestart=true
|
autorestart=true
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM rust:1.80-bookworm AS builder
|
FROM rust:1.83-bookworm AS builder
|
||||||
|
|
||||||
RUN rustup target add wasm32-unknown-unknown && \
|
RUN rustup target add wasm32-unknown-unknown && \
|
||||||
cargo install dioxus-cli diesel_cli && \
|
cargo install dioxus-cli diesel_cli && \
|
||||||
@ -15,15 +15,17 @@ FROM debian:bookworm-slim AS runner
|
|||||||
|
|
||||||
RUN apt-get update && apt-get install -y libpq5=15.8-0+deb12u1
|
RUN apt-get update && apt-get install -y libpq5=15.8-0+deb12u1
|
||||||
|
|
||||||
COPY --from=builder /srv/app/dist /srv/app/dist
|
COPY --from=builder /srv/app/target/dx/todo-baggins/release/web /srv/app
|
||||||
COPY .env /srv/app/.env
|
COPY .env /srv/app/.env
|
||||||
|
|
||||||
RUN chown -R 1000:1000 /srv/app
|
RUN chown -R 1000:1000 /srv/app
|
||||||
|
|
||||||
WORKDIR /srv/app
|
WORKDIR /srv/app
|
||||||
|
|
||||||
HEALTHCHECK CMD curl --fail http://localhost:8000 || exit 1
|
HEALTHCHECK CMD curl --fail -H "Accept: text/html" http://localhost || exit 1
|
||||||
|
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|
||||||
CMD ["./dist/todo-baggins"]
|
ENV IP="0.0.0.0"
|
||||||
|
ENV PORT="80"
|
||||||
|
CMD ["./server"]
|
||||||
|
24
index.html
24
index.html
@ -1,24 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html class="min-h-screen">
|
|
||||||
<head>
|
|
||||||
<title>{app_title}</title>
|
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta charset="UTF-8"/>
|
|
||||||
{style_include}
|
|
||||||
</head>
|
|
||||||
<body class="min-h-screen">
|
|
||||||
<div id="main" class="min-h-screen"></div>
|
|
||||||
<script type="module">
|
|
||||||
import init from "/{base_path}/assets/dioxus/{app_name}.js";
|
|
||||||
|
|
||||||
init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then(wasm => {
|
|
||||||
if (wasm.__wbindgen_start == undefined) {
|
|
||||||
wasm.main();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{script_include}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,21 +1,45 @@
|
|||||||
|
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
||||||
use crate::route::Route;
|
use crate::route::Route;
|
||||||
|
use crate::server::internationalization::get_language_identifier;
|
||||||
use dioxus::core_macro::rsx;
|
use dioxus::core_macro::rsx;
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_query::prelude::{use_init_query_client};
|
use dioxus_i18n::prelude::*;
|
||||||
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
use dioxus_i18n::unic_langid::langid;
|
||||||
use dioxus_sdk::i18n::{use_init_i18n};
|
use dioxus_query::prelude::use_init_query_client;
|
||||||
use crate::internationalization::get_languages;
|
|
||||||
use crate::server::internationalization::get_language_identifier;
|
const FAVICON: Asset = asset!("/assets/favicon.ico");
|
||||||
|
const TAILWIND_CSS: Asset = asset!("/assets/styles/tailwind_output.css");
|
||||||
|
const FONTS_CSS: Asset = asset!("/assets/styles/fonts.css");
|
||||||
|
const INPUT_NUMBER_ARROWS_CSS: Asset = asset!("/assets/styles/input_number_arrows.css");
|
||||||
|
const INPUT_RANGE_CSS: Asset = asset!("/assets/styles/input_range.css");
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn App() -> Element {
|
pub(crate) fn App() -> Element {
|
||||||
use_init_query_client::<QueryValue, QueryErrors, QueryKey>();
|
use_init_query_client::<QueryValue, QueryErrors, QueryKey>();
|
||||||
|
|
||||||
let language_identifier = use_server_future(get_language_identifier)?.unwrap().unwrap();
|
let language_identifier = use_server_future(get_language_identifier)?
|
||||||
use_init_i18n(language_identifier.clone(), language_identifier, get_languages);
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
use_init_i18n(|| {
|
||||||
|
I18nConfig::new(language_identifier)
|
||||||
|
.with_locale(Locale::new_static(
|
||||||
|
langid!("cs-CZ"),
|
||||||
|
include_str!("../internationalization/cs_cz.ftl"),
|
||||||
|
))
|
||||||
|
.with_locale(Locale::new_static(
|
||||||
|
langid!("en-US"),
|
||||||
|
include_str!("../internationalization/en_us.ftl"),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
|
document::Link { rel: "icon", href: FAVICON }
|
||||||
|
document::Link { rel: "stylesheet", href: TAILWIND_CSS }
|
||||||
|
document::Link { rel: "stylesheet", href: FONTS_CSS }
|
||||||
|
document::Link { rel: "stylesheet", href: INPUT_NUMBER_ARROWS_CSS }
|
||||||
|
document::Link { rel: "stylesheet", href: INPUT_RANGE_CSS }
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: "min-h-screen text-zinc-200 bg-zinc-800 pt-4 pb-36",
|
class: "min-h-screen text-zinc-200 bg-zinc-800 pt-4 pb-36",
|
||||||
Router::<Route> {}
|
Router::<Route> {}
|
||||||
|
@ -75,7 +75,7 @@ pub(crate) fn Navigation(expanded: Signal<bool>) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { None }}
|
} else { VNode::empty() }}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ use chrono::{Datelike, Local};
|
|||||||
use dioxus::core_macro::rsx;
|
use dioxus::core_macro::rsx;
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_i18n::prelude::i18n;
|
||||||
|
use dioxus_i18n::t;
|
||||||
use dioxus_query::prelude::QueryResult;
|
use dioxus_query::prelude::QueryResult;
|
||||||
use dioxus_sdk::i18n::use_i18;
|
|
||||||
use dioxus_sdk::translate;
|
|
||||||
|
|
||||||
const CALENDAR_LENGTH_DAYS: usize = 366 * 3;
|
const CALENDAR_LENGTH_DAYS: usize = 366 * 3;
|
||||||
|
|
||||||
@ -23,14 +23,12 @@ pub(crate) fn CategoryCalendarPage() -> Element {
|
|||||||
});
|
});
|
||||||
let tasks_query_result = tasks.result();
|
let tasks_query_result = tasks.result();
|
||||||
|
|
||||||
let i18 = use_i18();
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
match tasks_query_result.value() {
|
match tasks_query_result.value() {
|
||||||
QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks))
|
QueryResult::Ok(QueryValue::TasksWithSubtasks(tasks))
|
||||||
| QueryResult::Loading(Some(QueryValue::TasksWithSubtasks(tasks))) => {
|
| QueryResult::Loading(Some(QueryValue::TasksWithSubtasks(tasks))) => {
|
||||||
let today_date = Local::now().date_naive();
|
let today_date = Local::now().date_naive();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "pt-4 flex flex-col gap-8",
|
class: "pt-4 flex flex-col gap-8",
|
||||||
@ -42,16 +40,15 @@ pub(crate) fn CategoryCalendarPage() -> Element {
|
|||||||
div {
|
div {
|
||||||
class: "pt-1",
|
class: "pt-1",
|
||||||
{
|
{
|
||||||
date_current.format_localized(translate!(
|
date_current.format_localized(t!(
|
||||||
i18,
|
|
||||||
if date_current.year() == Local::now().year() {
|
if date_current.year() == Local::now().year() {
|
||||||
"formats.date_weekday_format"
|
"date-weekday-format"
|
||||||
} else {
|
} else {
|
||||||
"formats.date_weekday_year_format"
|
"date-weekday-year-format"
|
||||||
}
|
}
|
||||||
).as_str(),
|
).as_str(),
|
||||||
LocaleFromLanguageIdentifier::from(
|
LocaleFromLanguageIdentifier::from(
|
||||||
&(i18.selected_language)()
|
&i18n().language()
|
||||||
).into()
|
).into()
|
||||||
)
|
)
|
||||||
.to_string()
|
.to_string()
|
||||||
@ -60,7 +57,7 @@ pub(crate) fn CategoryCalendarPage() -> Element {
|
|||||||
}
|
}
|
||||||
TaskList {
|
TaskList {
|
||||||
tasks: tasks.iter().filter(|task| {
|
tasks: tasks.iter().filter(|task| {
|
||||||
if let Category::Calendar { date, .. }
|
if let Category::Calendar { date, .. }
|
||||||
= task.task().category() {
|
= task.task().category() {
|
||||||
*date == date_current
|
*date == date_current
|
||||||
} else {
|
} else {
|
||||||
@ -70,7 +67,7 @@ pub(crate) fn CategoryCalendarPage() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
QueryResult::Loading(None) => rsx! {
|
QueryResult::Loading(None) => rsx! {
|
||||||
|
@ -7,9 +7,9 @@ use crate::query::tasks::use_tasks_with_subtasks_in_category_query;
|
|||||||
use crate::query::QueryValue;
|
use crate::query::QueryValue;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_i18n::t;
|
||||||
|
use dioxus_i18n::use_i18n::i18n;
|
||||||
use dioxus_query::prelude::QueryResult;
|
use dioxus_query::prelude::QueryResult;
|
||||||
use dioxus_sdk::i18n::use_i18;
|
|
||||||
use dioxus_sdk::translate;
|
|
||||||
use voca_rs::Voca;
|
use voca_rs::Voca;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -25,9 +25,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
|
|
||||||
let long_term_tasks_query = use_tasks_with_subtasks_in_category_query(Category::LongTerm);
|
let long_term_tasks_query = use_tasks_with_subtasks_in_category_query(Category::LongTerm);
|
||||||
let long_term_tasks_query_result = long_term_tasks_query.result();
|
let long_term_tasks_query_result = long_term_tasks_query.result();
|
||||||
|
|
||||||
let i18 = use_i18();
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "pt-4 flex flex-col gap-8",
|
class: "pt-4 flex flex-col gap-8",
|
||||||
@ -46,7 +44,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
class: "mt-1",
|
class: "mt-1",
|
||||||
{translate!(i18, "long_term")._upper_first()}
|
{t!("long-term")._upper_first()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
@ -97,7 +95,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
panic!("Unexpected category.");
|
panic!("Unexpected category.");
|
||||||
}
|
}
|
||||||
}).cloned().collect::<Vec<TaskWithSubtasks>>();
|
}).cloned().collect::<Vec<TaskWithSubtasks>>();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
if !overdue_tasks.is_empty() {
|
if !overdue_tasks.is_empty() {
|
||||||
div {
|
div {
|
||||||
@ -109,7 +107,7 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
class: "mt-1",
|
class: "mt-1",
|
||||||
{translate!(i18, "overdue")._upper_first()}
|
{t!("overdue")._upper_first()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TaskList {
|
TaskList {
|
||||||
@ -128,18 +126,17 @@ pub(crate) fn CategoryTodayPage() -> Element {
|
|||||||
div {
|
div {
|
||||||
class: "mt-1",
|
class: "mt-1",
|
||||||
{
|
{
|
||||||
let format = translate!(i18, "formats.date_weekday_format");
|
let format = t!("date-weekday-format");
|
||||||
let today_date = today_date.format_localized(
|
let today_date = today_date.format_localized(
|
||||||
format.as_str(),
|
format.as_str(),
|
||||||
LocaleFromLanguageIdentifier::from(
|
LocaleFromLanguageIdentifier::from(
|
||||||
&(i18.selected_language)()
|
&i18n().language()
|
||||||
).into()
|
).into()
|
||||||
).to_string();
|
).to_string();
|
||||||
format!(
|
format!(
|
||||||
"{} – {}",
|
"{} – {}",
|
||||||
translate!(i18, "today")._upper_first(),
|
t!("today")._upper_first(),
|
||||||
if translate!(i18, "formats.weekday_lowercase_first")
|
if t!("weekday-lowercase-first").parse().unwrap() {
|
||||||
.parse().unwrap() {
|
|
||||||
today_date._lower_first()
|
today_date._lower_first()
|
||||||
} else {
|
} else {
|
||||||
today_date
|
today_date
|
||||||
|
@ -116,7 +116,7 @@ pub(crate) fn SubtasksForm(task: Task) -> Element {
|
|||||||
onchange: {
|
onchange: {
|
||||||
let subtask = subtask.clone();
|
let subtask = subtask.clone();
|
||||||
let task = task.clone();
|
let task = task.clone();
|
||||||
move |event| {
|
move |event: Event<FormData>| {
|
||||||
let subtask = subtask.clone();
|
let subtask = subtask.clone();
|
||||||
let task = task.clone();
|
let task = task.clone();
|
||||||
async move {
|
async move {
|
||||||
|
@ -11,9 +11,8 @@ use chrono::Duration;
|
|||||||
use dioxus::core_macro::{component, rsx};
|
use dioxus::core_macro::{component, rsx};
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_i18n::t;
|
||||||
use dioxus_query::prelude::{use_query_client, QueryResult};
|
use dioxus_query::prelude::{use_query_client, QueryResult};
|
||||||
use dioxus_sdk::i18n::use_i18;
|
|
||||||
use dioxus_sdk::translate;
|
|
||||||
use crate::query::projects::use_projects_query;
|
use crate::query::projects::use_projects_query;
|
||||||
|
|
||||||
const REMINDER_OFFSETS: [Option<Duration>; 17] = [
|
const REMINDER_OFFSETS: [Option<Duration>; 17] = [
|
||||||
@ -81,8 +80,6 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
|||||||
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
let query_client = use_query_client::<QueryValue, QueryErrors, QueryKey>();
|
||||||
let task_for_submit = task.clone();
|
let task_for_submit = task.clone();
|
||||||
|
|
||||||
let i18 = use_i18();
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "p-4 flex flex-col gap-4",
|
class: "p-4 flex flex-col gap-4",
|
||||||
@ -176,7 +173,7 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
|||||||
id: "input_project",
|
id: "input_project",
|
||||||
option {
|
option {
|
||||||
value: 0,
|
value: 0,
|
||||||
{translate!(i18, "none")}
|
{t!("none")}
|
||||||
},
|
},
|
||||||
match projects_query.result().value() {
|
match projects_query.result().value() {
|
||||||
QueryResult::Ok(QueryValue::Projects(projects))
|
QueryResult::Ok(QueryValue::Projects(projects))
|
||||||
@ -359,12 +356,12 @@ pub(crate) fn TaskForm(task: Option<Task>, on_successful_submit: EventHandler<()
|
|||||||
} else {
|
} else {
|
||||||
format!("{} h", offset.num_hours())
|
format!("{} h", offset.num_hours())
|
||||||
}
|
}
|
||||||
).unwrap_or_else(|| translate!(i18, "none"))}
|
).unwrap_or_else(|| t!("none"))}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => None
|
_ => VNode::empty()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
if let Some(task) = task.as_ref() {
|
if let Some(task) = task.as_ref() {
|
||||||
|
@ -4,6 +4,7 @@ use dioxus::core_macro::rsx;
|
|||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_query::prelude::use_query_client;
|
use dioxus_query::prelude::use_query_client;
|
||||||
|
use tracing::info;
|
||||||
use crate::components::task_list_item::TaskListItem;
|
use crate::components::task_list_item::TaskListItem;
|
||||||
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
use crate::query::{QueryErrors, QueryKey, QueryValue};
|
||||||
use crate::server::tasks::complete_task;
|
use crate::server::tasks::complete_task;
|
||||||
@ -22,17 +23,17 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
|
|||||||
div {
|
div {
|
||||||
key: "{task.task().id()}",
|
key: "{task.task().id()}",
|
||||||
class: format!(
|
class: format!(
|
||||||
"px-8 pt-5 {} flex flex-row gap-4 select-none {}",
|
"px-8 pt-4 {} flex flex-row items-center gap-4 select-none {}",
|
||||||
if task.task().deadline().is_some() || !task.subtasks().is_empty() {
|
if task.task().deadline().is_some() || !task.subtasks().is_empty() {
|
||||||
"pb-0.5"
|
"pb-0.5"
|
||||||
} else if let Category::Calendar { time, .. } = task.task().category() {
|
} else if let Category::Calendar { time, .. } = task.task().category() {
|
||||||
if time.is_some() {
|
if time.is_some() {
|
||||||
"pb-0.5"
|
"pb-0.5"
|
||||||
} else {
|
} else {
|
||||||
"pb-5"
|
"pb-4"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
"pb-5"
|
"pb-4"
|
||||||
},
|
},
|
||||||
if task_being_edited().is_some_and(|t| t.id() == task.task().id()) {
|
if task_being_edited().is_some_and(|t| t.id() == task.task().id()) {
|
||||||
"bg-zinc-700"
|
"bg-zinc-700"
|
||||||
@ -44,7 +45,7 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
|
|||||||
},
|
},
|
||||||
i {
|
i {
|
||||||
class: format!(
|
class: format!(
|
||||||
"{} text-3xl text-zinc-500",
|
"{} text-3xl align-middle h-9 text-zinc-500",
|
||||||
if *(task.task().category()) == Category::Done {
|
if *(task.task().category()) == Category::Done {
|
||||||
"fa solid fa-square-check"
|
"fa solid fa-square-check"
|
||||||
} else {
|
} else {
|
||||||
@ -53,12 +54,13 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
|
|||||||
),
|
),
|
||||||
onclick: {
|
onclick: {
|
||||||
let task = task.clone();
|
let task = task.clone();
|
||||||
move |event| {
|
move |event: Event<MouseData>| {
|
||||||
// To prevent editing the task.
|
// To prevent editing the task.
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
let task = task.clone();
|
let task = task.clone();
|
||||||
async move {
|
async move {
|
||||||
let completed_task = complete_task(task.task().id()).await.unwrap();
|
let completed_task = complete_task(task.task().id()).await
|
||||||
|
.unwrap();
|
||||||
let mut query_keys = vec![
|
let mut query_keys = vec![
|
||||||
QueryKey::Tasks,
|
QueryKey::Tasks,
|
||||||
QueryKey::TasksInCategory(
|
QueryKey::TasksInCategory(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use dioxus_i18n::prelude::i18n;
|
||||||
use crate::internationalization::LocaleFromLanguageIdentifier;
|
use crate::internationalization::LocaleFromLanguageIdentifier;
|
||||||
use crate::models::category::Category;
|
use crate::models::category::Category;
|
||||||
use crate::models::task::TaskWithSubtasks;
|
use crate::models::task::TaskWithSubtasks;
|
||||||
@ -5,19 +6,16 @@ use chrono::{Datelike, Local};
|
|||||||
use dioxus::core_macro::rsx;
|
use dioxus::core_macro::rsx;
|
||||||
use dioxus::dioxus_core::Element;
|
use dioxus::dioxus_core::Element;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_sdk::i18n::use_i18;
|
use dioxus_i18n::t;
|
||||||
use dioxus_sdk::translate;
|
|
||||||
use voca_rs::Voca;
|
use voca_rs::Voca;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
||||||
let i18 = use_i18();
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col",
|
class: "flex flex-col",
|
||||||
div {
|
div {
|
||||||
class: "mt-1 grow font-medium",
|
class: "grow font-medium",
|
||||||
{task.task().title()}
|
{task.task().title()}
|
||||||
},
|
},
|
||||||
div {
|
div {
|
||||||
@ -33,37 +31,37 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
|||||||
format!(
|
format!(
|
||||||
" {}",
|
" {}",
|
||||||
if deadline == today_date - chrono::Days::new(1) {
|
if deadline == today_date - chrono::Days::new(1) {
|
||||||
translate!(i18, "yesterday")
|
t!("yesterday")
|
||||||
} else if deadline == today_date {
|
} else if deadline == today_date {
|
||||||
translate!(i18, "today")
|
t!("today")
|
||||||
} else if deadline == today_date + chrono::Days::new(1) {
|
} else if deadline == today_date + chrono::Days::new(1) {
|
||||||
translate!(i18, "tomorrow")
|
t!("tomorrow")
|
||||||
} else if deadline > today_date
|
} else if deadline > today_date
|
||||||
&& deadline <= today_date + chrono::Days::new(7) {
|
&& deadline <= today_date + chrono::Days::new(7) {
|
||||||
let deadline = deadline.format_localized(
|
let deadline = deadline.format_localized(
|
||||||
"%A",
|
"%A",
|
||||||
LocaleFromLanguageIdentifier::from(
|
LocaleFromLanguageIdentifier::from(
|
||||||
&(i18.selected_language)()
|
&i18n().language()
|
||||||
).into()
|
).into()
|
||||||
).to_string();
|
).to_string();
|
||||||
if translate!(i18, "formats.weekday_lowercase_first")
|
if t!("weekday-lowercase-first")
|
||||||
.parse().unwrap() {
|
.parse().unwrap() {
|
||||||
deadline._lower_first()
|
deadline._lower_first()
|
||||||
} else {
|
} else {
|
||||||
deadline
|
deadline
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let format = translate!(i18,
|
let format = t!(
|
||||||
if deadline.year() == today_date.year() {
|
if deadline.year() == today_date.year() {
|
||||||
"formats.date_format"
|
"date-format"
|
||||||
} else {
|
} else {
|
||||||
"formats.date_year_format"
|
"date-year-format"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
deadline.format_localized(
|
deadline.format_localized(
|
||||||
format.as_str(),
|
format.as_str(),
|
||||||
LocaleFromLanguageIdentifier::from(
|
LocaleFromLanguageIdentifier::from(
|
||||||
&(i18.selected_language)()
|
&i18n().language()
|
||||||
).into()
|
).into()
|
||||||
).to_string()
|
).to_string()
|
||||||
}
|
}
|
||||||
@ -79,8 +77,8 @@ pub(crate) fn TaskListItem(task: TaskWithSubtasks) -> Element {
|
|||||||
class: "fa-solid fa-clock"
|
class: "fa-solid fa-clock"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
let format = translate!(i18, "formats.time_format");
|
let format = t!("time-format");
|
||||||
format!(" {}",calendar_time.time().format(format.as_str()))
|
format!(" {}", calendar_time.time().format(format.as_str()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/internationalization/cs_cz.ftl
Normal file
14
src/internationalization/cs_cz.ftl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
none = žádný
|
||||||
|
long-term = dlouhodobé
|
||||||
|
yesterday = včera
|
||||||
|
today = dnes
|
||||||
|
tomorrow = zítra
|
||||||
|
overdue = zpožděné
|
||||||
|
|
||||||
|
## Date and time formats
|
||||||
|
date-format = %-d. %B
|
||||||
|
date-year-format = %-d. %B %Y
|
||||||
|
date-weekday-format = %A %-d. %B
|
||||||
|
date-weekday-year-format = %A %-d. %B %Y
|
||||||
|
weekday-lowercase-first = true
|
||||||
|
time-format = %-H:%M
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "cs-CZ",
|
|
||||||
"texts": {
|
|
||||||
"none": "žádný",
|
|
||||||
"long_term": "dlouhodobé",
|
|
||||||
"yesterday": "včera",
|
|
||||||
"today": "dnes",
|
|
||||||
"tomorrow": "zítra",
|
|
||||||
"overdue": "zpožděné",
|
|
||||||
"formats": {
|
|
||||||
"date_format": "%-d. %B",
|
|
||||||
"date_year_format": "%-d. %B %Y",
|
|
||||||
"date_weekday_format": "%A %-d. %B",
|
|
||||||
"date_weekday_year_format": "%A %-d. %B %Y",
|
|
||||||
"weekday_lowercase_first": "true",
|
|
||||||
"time_format": "%-H:%M"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
14
src/internationalization/en_us.ftl
Normal file
14
src/internationalization/en_us.ftl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
none = none
|
||||||
|
long-term = long-term
|
||||||
|
yesterday = yesterday
|
||||||
|
today = today
|
||||||
|
tomorrow = tomorrow
|
||||||
|
overdue = overdue
|
||||||
|
|
||||||
|
## Date and time formats
|
||||||
|
date-format = %B %-d
|
||||||
|
date-year-format = %B %-d, %Y
|
||||||
|
date-weekday-format = %A, %B %-d
|
||||||
|
date-weekday-year-format = %A, %B %-d, %Y
|
||||||
|
weekday-lowercase-first = false
|
||||||
|
time-format = %-I:%M %P
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "en-US",
|
|
||||||
"texts": {
|
|
||||||
"none": "none",
|
|
||||||
"long_term": "long-term",
|
|
||||||
"yesterday": "yesterday",
|
|
||||||
"today": "today",
|
|
||||||
"tomorrow": "tomorrow",
|
|
||||||
"overdue": "overdue",
|
|
||||||
"formats": {
|
|
||||||
"date_format": "%B %-d",
|
|
||||||
"date_year_format": "%B %-d, %Y",
|
|
||||||
"date_weekday_format": "%A, %B %-d",
|
|
||||||
"date_weekday_year_format": "%A, %B %-d, %Y",
|
|
||||||
"weekday_lowercase_first": "false",
|
|
||||||
"time_format": "%-I:%M %P"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +1,12 @@
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use chrono::Locale;
|
use chrono::Locale;
|
||||||
use dioxus::fullstack::once_cell::sync::Lazy;
|
use dioxus::fullstack::once_cell::sync::Lazy;
|
||||||
use dioxus_sdk::i18n::Language;
|
|
||||||
use feruca::Collator;
|
use feruca::Collator;
|
||||||
use unic_langid_impl::LanguageIdentifier;
|
use unic_langid_impl::LanguageIdentifier;
|
||||||
|
|
||||||
const EN_US: &str = include_str!("en_us.json");
|
|
||||||
const CS_CZ: &str = include_str!("cs_cz.json");
|
|
||||||
|
|
||||||
pub(crate) static COLLATOR: Lazy<Mutex<Collator>> = Lazy::new(|| Mutex::new(Collator::default()));
|
pub(crate) static COLLATOR: Lazy<Mutex<Collator>> = Lazy::new(|| Mutex::new(Collator::default()));
|
||||||
|
|
||||||
pub(crate) fn get_languages() -> Vec<Language> {
|
|
||||||
Vec::from([EN_US, CS_CZ]).into_iter().map(|texts| Language::from_str(texts).unwrap()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct LocaleFromLanguageIdentifier<'a>(&'a LanguageIdentifier);
|
pub(crate) struct LocaleFromLanguageIdentifier<'a>(&'a LanguageIdentifier);
|
||||||
|
|
||||||
impl<'a> Deref for LocaleFromLanguageIdentifier<'a> {
|
impl<'a> Deref for LocaleFromLanguageIdentifier<'a> {
|
||||||
|
@ -11,19 +11,14 @@ mod migrations;
|
|||||||
|
|
||||||
use components::app::App;
|
use components::app::App;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_logger::tracing::{info, Level};
|
use tracing::info;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus_logger::init(Level::INFO).expect("Failed to initialize the logger.");
|
|
||||||
|
|
||||||
info!("Running migrations.");
|
info!("Running migrations.");
|
||||||
server_only!(
|
server_only!(
|
||||||
migrations::run_migrations().expect("Failed to run migrations.");
|
migrations::run_migrations().expect("Failed to run migrations.");
|
||||||
);
|
);
|
||||||
|
|
||||||
info!("Starting app.");
|
info!("Starting app.");
|
||||||
let cfg = server_only!(
|
launch(App);
|
||||||
dioxus::fullstack::Config::new().addr(std::net::SocketAddr::from(([0, 0, 0, 0], 8000)))
|
|
||||||
);
|
|
||||||
LaunchBuilder::fullstack().with_cfg(cfg).launch(App);
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use dotenvy::dotenv;
|
|||||||
#[server]
|
#[server]
|
||||||
pub(crate) async fn get_language_identifier() -> Result<LanguageIdentifier, ServerFnError> {
|
pub(crate) async fn get_language_identifier() -> Result<LanguageIdentifier, ServerFnError> {
|
||||||
dotenv().expect("Could not load environment variables from the .env file.");
|
dotenv().expect("Could not load environment variables from the .env file.");
|
||||||
|
|
||||||
Ok(env::var("LANGUAGE_CODE")
|
Ok(env::var("LANGUAGE_CODE")
|
||||||
.expect("The environment variable LANGUAGE_CODE must be set.")
|
.expect("The environment variable LANGUAGE_CODE must be set.")
|
||||||
.parse::<LanguageIdentifier>()?)
|
.parse::<LanguageIdentifier>()?)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user