6 Commits

Author SHA1 Message Date
5085791f45 fix: task completion and sorting (#106) 2025-12-19 19:16:19 +00:00
dc3effe640 fix: task sorting
All checks were successful
actionlint check / actionlint check (pull_request) Successful in 6s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 3s
conventional commit messages check / conventional commit messages check (pull_request) Successful in 7s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 7s
GitLeaks check / GitLeaks check (pull_request) Successful in 10s
hadolint check / hadolint check (pull_request) Successful in 11s
htmlhint check / htmlhint check (pull_request) Successful in 44s
markdownlint check / markdownlint check (pull_request) Successful in 40s
Prettier check / Prettier check (pull_request) Successful in 36s
checkov check / checkov check (pull_request) Successful in 1m22s
ShellCheck check / ShellCheck check (pull_request) Successful in 29s
Stylelint check / Stylelint check (pull_request) Successful in 31s
yamllint check / yamllint check (pull_request) Successful in 29s
Rust check / Rust check (pull_request) Successful in 11m42s
2025-12-19 20:01:22 +01:00
999057314a fix: task completion 2025-12-19 19:44:17 +01:00
41d0d83478 fix: Android bundling (#105) 2025-12-19 17:45:20 +00:00
0bca3ff872 fix: Android bundling
All checks were successful
conventional commit messages check / conventional commit messages check (pull_request) Successful in 7s
dotenv-linter check / dotenv-linter check (pull_request) Successful in 7s
GitLeaks check / GitLeaks check (pull_request) Successful in 11s
Rust check / Rust check (pull_request) Successful in 12m57s
hadolint check / hadolint check (pull_request) Successful in 19s
htmlhint check / htmlhint check (pull_request) Successful in 33s
markdownlint check / markdownlint check (pull_request) Successful in 28s
Prettier check / Prettier check (pull_request) Successful in 28s
ShellCheck check / ShellCheck check (pull_request) Successful in 34s
Stylelint check / Stylelint check (pull_request) Successful in 30s
checkov check / checkov check (pull_request) Successful in 1m27s
yamllint check / yamllint check (pull_request) Successful in 25s
actionlint check / actionlint check (pull_request) Successful in 6s
conventional pull request title check / conventional pull request title check (pull_request) Successful in 3s
2025-12-19 15:26:53 +01:00
32c12186c1 chore: upgrade to Dioxus 0.7 (#104) 2025-12-18 19:20:26 +00:00
13 changed files with 74 additions and 37 deletions

View File

@@ -3,6 +3,7 @@
/.github /.github
/bundle /bundle
/node_modules /node_modules
/scripts
/target /target
/.dockerignore /.dockerignore

View File

@@ -0,0 +1,5 @@
---
services:
app:
build:
target: builder_android

View File

@@ -5,8 +5,8 @@ RUN useradd -m -u 1000 -s /bin/bash app_user \
USER app_user USER app_user
RUN cargo install --git https://github.com/diesel-rs/diesel --rev 2e85ba060d3d70ea605ea58a79b8a435749a7adc diesel_cli \ RUN cargo install --git https://github.com/diesel-rs/diesel --rev 2e85ba060d3d70ea605ea58a79b8a435749a7adc --locked diesel_cli \
&& cargo install --git https://github.com/DioxusLabs/dioxus --rev 8f8b58ea80ba0ec8057807bcd58fb609f7a5f2b1 dioxus-cli && cargo install --git https://github.com/DioxusLabs/dioxus --rev 8f8b58ea80ba0ec8057807bcd58fb609f7a5f2b1 --locked dioxus-cli
COPY --chown=app_user . /srv/app COPY --chown=app_user . /srv/app
WORKDIR /srv/app WORKDIR /srv/app

View File

@@ -1,4 +1,13 @@
FROM rust:1.92.0-bookworm@sha256:9676d0547a259997add8f5924eb6b959c589ed39055338e23b99aba7958d6d31 AS builder FROM rust:1.92.0-bookworm@sha256:9676d0547a259997add8f5924eb6b959c589ed39055338e23b99aba7958d6d31 AS builder_base
RUN cargo install --git https://github.com/diesel-rs/diesel --rev 2e85ba060d3d70ea605ea58a79b8a435749a7adc --locked diesel_cli \
&& cargo install --git https://github.com/DioxusLabs/dioxus --rev 8f8b58ea80ba0ec8057807bcd58fb609f7a5f2b1 --locked dioxus-cli --features disable-telemetry
COPY . /srv/app
WORKDIR /srv/app
FROM builder_base AS builder_android
ARG ANDROID_NDK_VERSION=26.2.11394342 ARG ANDROID_NDK_VERSION=26.2.11394342
ARG ANDROID_COMMAND_LINE_TOOLS_VERSION=13114758 ARG ANDROID_COMMAND_LINE_TOOLS_VERSION=13114758
@@ -13,11 +22,8 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
openjdk-17-jdk-headless=17.0.17+10-1~deb12u1 \ openjdk-17-jdk-headless=17.0.17+10-1~deb12u1 \
&& curl -fsSL -o /tmp/cmdline-tools.zip "https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_COMMAND_LINE_TOOLS_VERSION}_latest.zip" \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& cargo install --git https://github.com/diesel-rs/diesel --rev 2e85ba060d3d70ea605ea58a79b8a435749a7adc diesel_cli \
&& cargo install --git https://github.com/DioxusLabs/dioxus --rev 8f8b58ea80ba0ec8057807bcd58fb609f7a5f2b1 dioxus-cli --features disable-telemetry \
&& rustup target add aarch64-linux-android \ && rustup target add aarch64-linux-android \
&& mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools" \ && mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools" \
&& curl -fsSL -o /tmp/cmdline-tools.zip "https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_COMMAND_LINE_TOOLS_VERSION}_latest.zip" \ && curl -fsSL -o /tmp/cmdline-tools.zip "https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_COMMAND_LINE_TOOLS_VERSION}_latest.zip" \
@@ -25,32 +31,33 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& export PATH="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools:$PATH" \ && export PATH="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools:$PATH" \
&& mv "$ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools" "$ANDROID_SDK_ROOT/cmdline-tools/latest" \ && mv "$ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools" "$ANDROID_SDK_ROOT/cmdline-tools/latest" \
&& rm /tmp/cmdline-tools.zip \ && rm /tmp/cmdline-tools.zip \
&& yes | sdkmanager --sdk_root="$ANDROID_SDK_ROOT" --licenses > /dev/null \ # `yes` can exit with 141, which is okay, so the `test` is added to ignore it.
&& yes | sdkmanager --sdk_root="$ANDROID_SDK_ROOT" --licenses > /dev/null; test "${PIPESTATUS[1]}" -eq 0 \
&& sdkmanager --sdk_root="$ANDROID_SDK_ROOT" "platform-tools" "platforms;android-$ANDROID_VERSION" "build-tools;$ANDROID_BUILD_TOOLS_VERSION" "ndk;$ANDROID_NDK_VERSION" \ && sdkmanager --sdk_root="$ANDROID_SDK_ROOT" "platform-tools" "platforms;android-$ANDROID_VERSION" "build-tools;$ANDROID_BUILD_TOOLS_VERSION" "ndk;$ANDROID_NDK_VERSION" \
&& curl -fsSL -o /tmp/bundletool-all.jar "https://github.com/google/bundletool/releases/download/$ANDROID_BUNDLETOOL_VERSION/bundletool-all-${ANDROID_BUNDLETOOL_VERSION}.jar" \ && curl -fsSL -o /tmp/bundletool-all.jar "https://github.com/google/bundletool/releases/download/$ANDROID_BUNDLETOOL_VERSION/bundletool-all-${ANDROID_BUNDLETOOL_VERSION}.jar" \
&& echo "$ANDROID_BUNDLETOOL_SHA256 /tmp/bundletool-all.jar" | sha256sum -c - \ && echo "$ANDROID_BUNDLETOOL_SHA256 /tmp/bundletool-all.jar" | sha256sum -c - \
&& keytool -genkeypair -noprompt -keystore /tmp/android_keystore.jks -alias key -keyalg RSA -keysize 2048 -validity 3660 -dname "CN=" -storepass 123456 -keypass 123456 && keytool -genkeypair -noprompt -keystore /tmp/android_keystore.jks -alias key -keyalg RSA -keysize 2048 -validity 3660 -dname "CN=" -storepass 123456 -keypass 123456 \
&& export ANDROID_HOME="$ANDROID_SDK_ROOT" \
COPY . /srv/app
WORKDIR /srv/app
RUN export ANDROID_HOME="$ANDROID_SDK_ROOT" \
&& export ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION" \ && export ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION" \
&& dx bundle --release \
&& dx bundle --platform android --target aarch64-linux-android --release \ && dx bundle --platform android --target aarch64-linux-android --release \
&& java -jar /tmp/bundletool-all.jar build-apks --bundle=/srv/app/target/dx/todo_baggins/release/android/app/app/build/outputs/bundle/release/TodoBaggins-aarch64-linux-android.aab --output=/tmp/todo_baggins.apks --mode=universal --ks=/tmp/android_keystore.jks --ks-key-alias=key --ks-pass=pass:123456 \ && java -jar /tmp/bundletool-all.jar build-apks --bundle=/srv/app/target/dx/todo_baggins/release/android/app/app/build/outputs/bundle/release/TodoBaggins-aarch64-linux-android.aab --output=/tmp/todo_baggins.apks --mode=universal --ks=/tmp/android_keystore.jks --ks-key-alias=key --ks-pass=pass:123456 \
&& mkdir -p /srv/app/bundle/android \ && mkdir -p /srv/app/bundle \
&& unzip -qp /tmp/todo_baggins.apks universal.apk > /srv/app/bundle/android/todo_baggins.apk && unzip -qp /tmp/todo_baggins.apks universal.apk > /srv/app/bundle/todo_baggins.apk
FROM debian:bookworm@sha256:b877a1a3fdf02469440f1768cf69c9771338a875b7add5e80c45b756c92ac20a AS runner
FROM builder_base AS builder_web
RUN dx bundle --release
FROM debian:bookworm@sha256:b877a1a3fdf02469440f1768cf69c9771338a875b7add5e80c45b756c92ac20a AS runner_web
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends libpq5=15.10-0+deb12u1 \ && apt-get install -y --no-install-recommends libpq5=15.10-0+deb12u1 \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
COPY --from=builder /srv/app/target/dx/todo_baggins/release/web /srv/app/web COPY --from=builder_web /srv/app/target/dx/todo_baggins/release/web /srv/app/web
COPY --from=builder /srv/app/bundle/android /srv/app/android
RUN chown -R 1000:1000 /srv/app/web RUN chown -R 1000:1000 /srv/app/web

View File

@@ -1,4 +1,12 @@
#!/bin/sh #!/bin/sh
mkdir -p ../bundle \ echo "Warning: This script needs to stop any currently running Docker compose stacks. Stopping them now..."
&& docker compose -f docker-compose-prod.yaml cp app:/srv/app/android bundle docker compose down
mkdir -p bundle/android bundle/temp \
&& docker compose -f docker-compose-prod.yaml -f docker-compose-android-bundle.yaml up --build --no-start --no-deps app \
&& docker compose -f docker-compose-prod.yaml -f docker-compose-android-bundle.yaml cp app:/srv/app/bundle bundle/temp \
&& mv bundle/temp/bundle/* bundle/android \
&& rm -r bundle/temp
echo "Warning: If a running Docker compose stack has been stopped by this script, you may want to bring it up again now."

View File

@@ -19,7 +19,7 @@ pub(crate) fn CategoryCalendarTaskList() -> Element {
date: today_date, date: today_date,
reoccurrence: None, reoccurrence: None,
time: None, time: None,
})?(); })?;
rsx! { rsx! {
div { div {

View File

@@ -17,7 +17,7 @@ pub(crate) fn CategoryTodayTaskList() -> Element {
date: today_date, date: today_date,
reoccurrence: None, reoccurrence: None,
time: None, time: None,
})?(); })?;
let today_tasks = calendar_tasks let today_tasks = calendar_tasks
.iter() .iter()
.filter(|task| { .filter(|task| {
@@ -40,7 +40,7 @@ pub(crate) fn CategoryTodayTaskList() -> Element {
}) })
.cloned() .cloned()
.collect::<Vec<TaskWithSubtasks>>(); .collect::<Vec<TaskWithSubtasks>>();
let long_term_tasks = use_tasks_with_subtasks_in_category(Category::LongTerm)?(); let long_term_tasks = use_tasks_with_subtasks_in_category(Category::LongTerm)?;
rsx! { rsx! {
div { div {

View File

@@ -3,7 +3,7 @@ use dioxus::prelude::*;
#[component] #[component]
pub(crate) fn ProjectList() -> Element { pub(crate) fn ProjectList() -> Element {
let projects = use_projects()?(); let projects = use_projects()?;
let mut project_being_edited = use_context::<Signal<Option<Project>>>(); let mut project_being_edited = use_context::<Signal<Option<Project>>>();
rsx! { rsx! {

View File

@@ -6,7 +6,7 @@ use dioxus_i18n::t;
#[component] #[component]
pub(crate) fn ProjectSelect(initial_selected_id: Option<i32>) -> Element { pub(crate) fn ProjectSelect(initial_selected_id: Option<i32>) -> Element {
let projects = use_projects()?(); let projects = use_projects()?;
rsx! { rsx! {
select { select {
name: "project_id", name: "project_id",

View File

@@ -8,7 +8,7 @@ use dioxus::prelude::*;
#[component] #[component]
pub(crate) fn SubtasksForm(task: Task) -> Element { pub(crate) fn SubtasksForm(task: Task) -> Element {
let subtasks = use_subtasks_of_task(task.id)?(); let subtasks = use_subtasks_of_task(task.id)?;
let mut new_title = use_signal(String::new); let mut new_title = use_signal(String::new);
rsx! { rsx! {
form { form {

View File

@@ -1,6 +1,7 @@
use crate::components::task_list_item::TaskListItem; use crate::components::task_list_item::TaskListItem;
use crate::models::category::Category; use crate::models::category::Category;
use crate::models::task::{Task, TaskWithSubtasks}; use crate::models::task::{Task, TaskWithSubtasks};
use crate::server::tasks::complete_task;
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::*;
@@ -48,6 +49,9 @@ pub(crate) fn TaskList(tasks: Vec<TaskWithSubtasks>, class: Option<&'static str>
move |event: Event<MouseData>| { move |event: Event<MouseData>| {
// To prevent editing the task. // To prevent editing the task.
event.stop_propagation(); event.stop_propagation();
async move {
let _ = complete_task(task.task.id).await;
}
} }
} }
}, },

View File

@@ -13,6 +13,17 @@ use crate::{
}, },
}; };
#[allow(clippy::result_large_err)]
fn sort_loader_result<T: Ord + Clone>(
result: Result<Loader<Vec<T>>, Loading>,
) -> Result<Vec<T>, Loading> {
result.map(|loader| {
let mut items_sorted = loader();
items_sorted.sort();
items_sorted
})
}
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
fn use_loader_with_update_subscription<F, T, E>( fn use_loader_with_update_subscription<F, T, E>(
mut future: impl FnMut() -> F + 'static, mut future: impl FnMut() -> F + 'static,
@@ -40,22 +51,23 @@ where
} }
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
pub(crate) fn use_projects() -> Result<Loader<Vec<Project>>, Loading> { pub(crate) fn use_projects() -> Result<Vec<Project>, Loading> {
use_loader_with_update_subscription(get_projects).inspect(|projects| projects().sort()) let result = use_loader_with_update_subscription(get_projects);
sort_loader_result(result)
} }
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
pub(crate) fn use_tasks_with_subtasks_in_category( pub(crate) fn use_tasks_with_subtasks_in_category(
filtered_category: Category, filtered_category: Category,
) -> Result<Loader<Vec<TaskWithSubtasks>>, Loading> { ) -> Result<Vec<TaskWithSubtasks>, Loading> {
use_loader_with_update_subscription(move || { let result = use_loader_with_update_subscription(move || {
get_tasks_with_subtasks_in_category(filtered_category.clone()) get_tasks_with_subtasks_in_category(filtered_category.clone())
}) });
.inspect(|tasks| tasks().sort()) sort_loader_result(result)
} }
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
pub(crate) fn use_subtasks_of_task(task_id: i32) -> Result<Loader<Vec<Subtask>>, Loading> { pub(crate) fn use_subtasks_of_task(task_id: i32) -> Result<Vec<Subtask>, Loading> {
use_loader_with_update_subscription(move || get_subtasks_of_task(task_id)) let result = use_loader_with_update_subscription(move || get_subtasks_of_task(task_id));
.inspect(|subtasks| subtasks().sort()) sort_loader_result(result)
} }

View File

@@ -7,7 +7,7 @@ use dioxus::prelude::*;
#[component] #[component]
pub(crate) fn CategoryPage(category: Category) -> Element { pub(crate) fn CategoryPage(category: Category) -> Element {
let tasks = use_tasks_with_subtasks_in_category(category)?(); let tasks = use_tasks_with_subtasks_in_category(category)?;
rsx! { rsx! {
TaskList { TaskList {