Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 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
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.
32 changes: 17 additions & 15 deletions lib/app/modules/home/controllers/home_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ import 'package:taskwarrior/app/utils/app_settings/app_settings.dart';
import 'package:taskwarrior/app/v3/champion/replica.dart';
import 'package:taskwarrior/app/v3/champion/models/task_for_replica.dart';
import 'package:taskwarrior/app/v3/db/task_database.dart';
import 'package:taskwarrior/app/v3/db/update.dart';
import 'package:taskwarrior/app/v3/models/task.dart';
import 'package:taskwarrior/app/v3/net/fetch.dart';
import 'package:textfield_tags/textfield_tags.dart';
import 'package:taskwarrior/app/utils/themes/theme_extension.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
Expand All @@ -51,6 +49,11 @@ class HomeController extends GetxController {
final RxSet<String> selectedTags = <String>{}.obs;
final RxList<Task> queriedTasks = <Task>[].obs;
final RxList<Task> searchedTasks = <Task>[].obs;
// Reactive mirror of the search box text. `searchedTasks` only drives the
// local-taskc list (TasksBuilder); the TaskChampion replica list reads
// `tasksFromReplica` directly, so it needs an observable query to rebuild and
// filter on each keystroke. Kept in sync by search()/toggleSearch().
final RxString searchQuery = ''.obs;
final RxList<DateTime?> selectedDates = List<DateTime?>.filled(4, null).obs;
final RxMap<String, TagMetadata> pendingTags = <String, TagMetadata>{}.obs;
final RxMap<String, ProjectNode> projects = <String, ProjectNode>{}.obs;
Expand Down Expand Up @@ -86,6 +89,12 @@ class HomeController extends GetxController {
taskdb = TaskDatabase();
taskdb.open();
getUniqueProjects();
// Initialize the pending/waiting filters from their persisted values.
// Without this the RxBool defaults to false, and the replica list view
// (which filters `status == pending` only when pendingFilter is true)
// shows completed tasks only — hiding every pending task on first load.
pendingFilter.value = Query(storage.tabs.tab()).getPendingFilter();
waitingFilter.value = Query(storage.tabs.tab()).getWaitingFilter();
_loadTaskChampion();
fetchTasksFromDB();

Expand Down Expand Up @@ -168,16 +177,6 @@ class HomeController extends GetxController {
debugPrint("Tasks from Replica: ${tasks.length}");
}

Future<void> refreshTasks(String clientId, String encryptionSecret) async {
TaskDatabase taskDatabase = TaskDatabase();
await taskDatabase.open();
List<TaskForC> tasksFromServer =
await fetchTasks(clientId, encryptionSecret);
await updateTasksInDatabase(tasksFromServer);
List<TaskForC> fetchedTasks = await taskDatabase.fetchTasksFromDatabase();
tasks.value = fetchedTasks;
}

Future<void> fetchTasksFromDB() async {
debugPrint("Fetching tasks from DB ${taskReplica.value}");
await _loadTaskChampion();
Expand Down Expand Up @@ -499,10 +498,12 @@ class HomeController extends GetxController {
if (!searchVisible.value) {
searchedTasks.assignAll(queriedTasks);
searchController.text = '';
searchQuery.value = '';
}
}

void search(String term) {
searchQuery.value = term;
searchedTasks.assignAll(
queriedTasks
.where(
Expand Down Expand Up @@ -580,9 +581,10 @@ class HomeController extends GetxController {
await refreshReplicaTasks();
}
} else if (taskchampion.value) {
if (clientId != null && encryptionSecret != null) {
await refreshTasks(clientId, encryptionSecret);
}
// CCSync HTTP sync has been retired; the local TaskChampion database is
// the source of truth for this mode, so reload it without a remote
// round-trip.
await fetchTasksFromDB();
} else {
await synchronize(context, false);
}
Expand Down
Loading
Loading