Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
289d1f1
refactor: retire the CCSync HTTP sync layer, unify on TaskChampion FFI
BrawlerXull Jun 13, 2026
bdab680
fix(ccsync): address audit findings on the sync-retirement change
BrawlerXull Jun 20, 2026
09040e1
refactor(rust): modularize tc_helper, add typed errors, surface task …
BrawlerXull Jun 27, 2026
98ee2d9
feat(website): project website with nightly build tracking
BrawlerXull Jul 1, 2026
f013a87
refactor(ccsync): remove residual CCSync references from the profile UI
BrawlerXull Jul 8, 2026
8aac26e
fix(deps): pin flutter_rust_bridge to 2.11.1 to match generated bindings
BrawlerXull Jul 8, 2026
0ded498
test(rust): cover dependency & annotation serialization; pin flutter_…
BrawlerXull Jul 9, 2026
0549580
build(android): rebuild arm64-v8a tc_helper from the overhauled Rust
BrawlerXull Jul 9, 2026
154318b
fix(ui): show replica tasks & surface FFI attributes; harden tour show
BrawlerXull Jul 9, 2026
494c06e
ci: compile tc_helper from source with cargo-ndk (arm64-v8a)
BrawlerXull Jul 9, 2026
78a3163
fix(home): make replica task list actually display tasks
BrawlerXull Jul 9, 2026
279de06
build(android): support armeabi-v7a via target-scoped aws-lc-sys bind…
BrawlerXull Jul 9, 2026
d8d7fb0
refactor(rust): drop unused AWS-S3/GCS sync backends (removes aws-lc)
BrawlerXull Jul 9, 2026
0534000
fix(home): make search filter the TaskChampion replica list
BrawlerXull Jul 9, 2026
a81dc6d
docs(website): add Features page, Docs guides, and content styling
BrawlerXull Jul 15, 2026
f2e0392
Merge branch 'feat/retire-ccsync' into integration/d1-d2-d5
BrawlerXull Jul 15, 2026
feb7d80
Merge branch 'feat/project-website' into integration/d1-d2-d5
BrawlerXull Jul 15, 2026
18ca717
fix(sync): show tasks immediately after saving TaskChampion credentials
BrawlerXull Jul 15, 2026
7b3116c
fix(profile): correct sync-mode snackbar spacing and config label
BrawlerXull Jul 15, 2026
d12b6b0
fix(sort): make all Taskchampion replica sort options work
BrawlerXull Jul 15, 2026
7d6bf48
fix(sort): decouple sort key from localized label
BrawlerXull Jul 16, 2026
5ba2e3d
feat(sort): implement Tags and Urgency sorting for the replica list
BrawlerXull Jul 16, 2026
6887b12
feat(reports): reporting-engine data layer (Issue #418)
BrawlerXull Jul 17, 2026
0937e01
feat(reports): reporting-engine UI + entry point (Issue #418)
BrawlerXull Jul 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/build-nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Nightly Build & Log

# Builds the signed nightly APK on a schedule and records the result in the
# website build log (website/data/nightly_builds.json). Committing that file to
# main touches website/**, which triggers deploy-website.yml to refresh the
# published site. This complements the push-triggered F-Droid deploy
# (nightlydepolyci.yml); the two can be unified later.

on:
schedule:
- cron: '0 2 * * *' # 02:00 UTC daily
workflow_dispatch:

permissions:
contents: write # commit the updated build log back to main

jobs:
nightly:
name: Build nightly APK and record build log
runs-on: ubuntu-latest
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
tool-cache: false
android: false # keep Android SDKs for Flutter
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true

- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17.x'

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.29.2'

- name: Get dependencies
run: flutter pub get

- name: Decode signing secrets
env:
NIGHTLY_KEYSTORE_B64: ${{ secrets.NIGHTLY_KEYSTORE_B64 }}
NIGHTLY_PROPERTIES_B64: ${{ secrets.NIGHTLY_PROPERTIES_B64 }}
run: |
echo "$NIGHTLY_KEYSTORE_B64" | base64 --decode > android/nightly.jks
echo "$NIGHTLY_PROPERTIES_B64" | base64 --decode > android/key_nightly.properties

- name: Build nightly APK
id: build
run: flutter build apk --flavor nightly --build-number=${{ github.run_number }} --release

- name: Record successful build
if: success()
run: |
python3 scripts/update_build_log.py success \
"https://github.com/${{ github.repository }}/raw/fdroid-repo/repo/nightly.${{ github.run_number }}.apk" \
"${{ github.run_number }}"

- name: Record failed build
if: failure()
run: python3 scripts/update_build_log.py failure "" "${{ github.run_number }}"

- name: Commit build log
if: always()
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if git diff --quiet -- website/data/nightly_builds.json; then
echo "No build-log changes to commit."
else
git add website/data/nightly_builds.json
git commit -m "chore(nightly): record build ${{ github.run_number }}"
git push
fi
72 changes: 72 additions & 0 deletions .github/workflows/build-tc-helper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Compiles the tc_helper Rust native library from source (instead of relying on
# the pre-built .so committed under android/app/src/main/jniLibs/) and then
# builds the production APK against the freshly-compiled library.
#
# This is the "proper" fix for stale committed binaries: the .so is regenerated
# from rust/ on every run, so it can never drift from the Rust source. Once this
# is green, the committed jniLibs/*.so can be git-ignored and purged from history.
#
# NOTE: This workflow has not yet been run on CI — action versions, the NDK
# version, and the cargo-ndk invocation are transcribed from a verified LOCAL
# build (macOS) and may need small adjustments on the first CI run.
#
# Both arm64-v8a and armeabi-v7a are built. taskchampion is configured with only
# the server-sync backend (see rust/Cargo.toml), so there's no aws-lc-sys — hence
# no cmake/ninja/bindgen/libclang toolchain needed; ring builds with just the
# Rust + NDK toolchain.
name: Build tc_helper (compile from source)

on:
workflow_dispatch:
pull_request:
branches: [main, reports]
paths:
- "rust/**"
- "android/**"
- ".github/workflows/build-tc-helper.yml"

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"

# Android SDK + NDK. cargo-ndk reads ANDROID_NDK_HOME to find the toolchain.
- uses: android-actions/setup-android@v3
- name: Install NDK
run: |
sdkmanager "ndk;26.1.10909125"
echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/26.1.10909125" >> "$GITHUB_ENV"

- name: Install Rust + Android targets + cargo-ndk
run: |
rustup toolchain install stable --profile minimal
rustup target add aarch64-linux-android armv7-linux-androideabi
cargo install cargo-ndk --version ^4

- name: Compile tc_helper (arm64-v8a + armeabi-v7a) from source
working-directory: rust
run: |
cargo ndk -t arm64-v8a -t armeabi-v7a \
-o ../android/app/src/main/jniLibs \
build --release

- name: Show freshly-built libs
run: ls -lhR android/app/src/main/jniLibs/

- uses: subosito/flutter-action@v2
with:
flutter-version: "3.44.5"

- run: flutter pub get
- run: flutter build apk --release --flavor production

- uses: actions/upload-artifact@v4
with:
name: production-apk-from-source
path: build/app/outputs/flutter-apk/app-production-release.apk
64 changes: 64 additions & 0 deletions .github/workflows/deploy-website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Deploy Website

# Builds the Hugo site in website/ and publishes it to GitHub Pages.
# Triggered when the site content or this workflow changes (the nightly
# build-log commit made by build-nightly.yml touches website/data/, so a new
# nightly automatically refreshes the published site).

on:
push:
branches: [main]
paths:
- 'website/**'
- '.github/workflows/deploy-website.yml'
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# Allow one concurrent deployment; don't cancel an in-progress production deploy.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.164.0
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # hugo.toml enableGitInfo needs full history

- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true

- name: Configure Pages
id: pages
uses: actions/configure-pages@v5

- name: Build site
run: hugo --source website --minify --baseURL "${{ steps.pages.outputs.base_url }}/"

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: website/public

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
7 changes: 7 additions & 0 deletions .github/workflows/nightlydepolyci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ on:
push:
branches:
- main
# Website content and the nightly build-log commit must not trigger a full
# APK rebuild + F-Droid redeploy (see build-nightly.yml / deploy-website.yml).
paths-ignore:
- 'website/**'
- 'scripts/**'
- '.github/workflows/deploy-website.yml'
- '.github/workflows/build-nightly.yml'

jobs:
build-and-deploy:
Expand Down
Binary file not shown.
Binary file modified android/app/src/main/jniLibs/arm64-v8a/libtc_helper.so
Binary file not shown.
Binary file not shown.
Binary file modified android/app/src/main/jniLibs/armeabi-v7a/libtc_helper.so
Binary file not shown.
10 changes: 5 additions & 5 deletions ios/tc_helper.xcframework/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,32 @@
<key>BinaryPath</key>
<string>tc_helper.framework/tc_helper</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>tc_helper.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>tc_helper.framework/tc_helper</string>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>tc_helper.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
Expand Down
Binary file not shown.
Binary file not shown.
88 changes: 88 additions & 0 deletions lib/app/models/report.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/// Data model for the reporting engine (Issue #418).
///
/// A *report* is a named, purpose-built bundle of a filter expression, a sort
/// order, and a set of display columns — mirroring Taskwarrior's report system
/// (`report.<name>.filter` / `.sort` / `.columns` / `.description`). Reports let
/// users invoke preset views like "next", "ready", or "overdue" instead of
/// hand-building a filter/sort every time.
library;

/// A single column in a report's display (e.g. `description`, `due`).
class ColumnSpec {
final String field;
final String? label;

const ColumnSpec(this.field, {this.label});

/// Parses a Taskwarrior `report.*.columns` value such as
/// `"id,description,due"` into a list of columns. Taskwarrior column formats
/// (e.g. `due.relative`) are reduced to their base attribute.
static List<ColumnSpec> parseList(String value) {
return value
.split(',')
.map((c) => c.trim())
.where((c) => c.isNotEmpty)
.map((c) => ColumnSpec(c.split('.').first))
.toList();
}
}

/// One sort key: an attribute plus a direction. Taskwarrior encodes these as
/// `field+` (ascending) or `field-` (descending), optionally chained with
/// commas (e.g. `"urgency-,due+"`).
class SortCriterion {
final String field;
final bool ascending;

const SortCriterion(this.field, {this.ascending = true});

/// Parses a Taskwarrior `report.*.sort` value into an ordered list of keys.
/// A trailing `+`/`-` sets direction (default ascending). A `/` break marker
/// (e.g. `urgency-/`) is ignored — only the ordering matters here.
static List<SortCriterion> parseList(String value) {
return value
.split(',')
.map((s) => s.trim().replaceAll('/', ''))
.where((s) => s.isNotEmpty)
.map((s) {
if (s.endsWith('-')) {
return SortCriterion(s.substring(0, s.length - 1), ascending: false);
}
if (s.endsWith('+')) {
return SortCriterion(s.substring(0, s.length - 1), ascending: true);
}
return SortCriterion(s); // no direction → ascending
}).toList();
}
}

/// A complete report definition.
class ReportDefinition {
/// Short identifier, e.g. `next` — also the display title.
final String name;

/// Human-readable one-line summary shown under the name.
final String description;

/// Columns to display (advisory for the UI; may be empty).
final List<ColumnSpec> columns;

/// Ordered sort keys applied after filtering.
final List<SortCriterion> sortCriteria;

/// The filter expression (e.g. `"status:pending +READY"`), or null/empty for
/// "everything".
final String? filterExpression;

/// True for reports read from a user `.taskrc` (grouped above the defaults).
final bool isCustom;

const ReportDefinition({
required this.name,
required this.description,
this.columns = const [],
this.sortCriteria = const [],
this.filterExpression,
this.isCustom = false,
});
}
Loading
Loading