Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions .github/workflows/build-mobile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ jobs:
- name: Run tests
working-directory: mobile
run: flutter test

- name: Decode and configure Android signing
if: env.ANDROID_KEYSTORE_BASE64 != ''
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > mobile/android/release-key.jks
{
echo "storeFile=../release-key.jks"
echo "storePassword=$ANDROID_STORE_PASSWORD"
echo "keyAlias=$ANDROID_KEY_ALIAS"
echo "keyPassword=$ANDROID_KEY_PASSWORD"
} > mobile/android/key.properties

- name: Build APK
working-directory: mobile
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/cd-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,22 @@ jobs:
- name: Run mobile tests
working-directory: mobile
run: flutter test

- name: Decode and configure Android signing
if: env.ANDROID_KEYSTORE_BASE64 != ''
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > mobile/android/release-key.jks
{
echo "storeFile=../release-key.jks"
echo "storePassword=$ANDROID_STORE_PASSWORD"
echo "keyAlias=$ANDROID_KEY_ALIAS"
echo "keyPassword=$ANDROID_KEY_PASSWORD"
} > mobile/android/key.properties

- name: Build APK
working-directory: mobile
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ desktop/*.p12
desktop/*.spc
desktop/*.pvk

# Android release signing (never commit)
mobile/android/key.properties
mobile/android/*.jks
mobile/android/*.keystore

# Docker Compose overrides (do not ignore auto files, only manually generated)
# docker-compose.https.yml is now tracked

Expand Down
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
## [5.13.2] - 2026-08-27

### Added

- **Linear setup wizard** — Guided API-key wizard for Linear so integration setup matches the other providers; the connector registry is re-exported from the integrations package init.

- **Web manual entry cascade & inline create (#728)** — Manual time logging and the dashboard start-timer modal now follow the mobile start-timer flow: pick a client first (the project picker unlocks only then), then one of that client's projects, then a task of that project. The client field stays a real searchable dropdown even when only one client exists, so another client can be picked or created inline; a newly created client is selected automatically. Inline project creation attaches automatically to the selected client — the client dropdown is replaced by a read-only display with an editable hourly rate pre-filled from the client default — instead of asking for a client again. Inline task creation creates the task immediately from the combobox "Create …" row (no confirmation modal) for the preselected project.
- **Searchable combobox dark mode & typing (#728)** — Combobox dropdowns build their DOM in JS with utility classes Tailwind purged from the stylesheet, leaving white-on-white rows in dark mode; the dropdown now uses always-emitted component classes (`tt-searchable-*`) with proper dark hover/selected states. Focusing a combobox selects its text so typing replaces the committed label instead of appending to it. Source-controlled assets (`searchable-select.js`, `inline-create.js`, `dist/output.css`) are served with mtime-based version queries via a new `static_url()` helper so edits bust the browser cache without a release bump.
### Changed

- **Mobile start timer flow** — Client, project, and task selection in the start timer sheet now use one consistent searchable picker; picking a client filters the project list to that client and picking a project filters tasks accordingly. Projects can also be picked directly without choosing a client first, which then auto-fills the client. Typing an unknown name offers inline "Create …" for clients, projects, and tasks via the existing v1 endpoints. The projects screen is sorted by most recently used (`last_used_at`) so recent work can be restarted with one tap.
- **Client versions** — Synced Electron (`desktop/package.json`), Flutter (`mobile/pubspec.yaml`), and Chromium extension (`browser-extension/manifest.json` / `package.json`) to **5.13.2** with the webapp (`setup.py`).

### Fixed

- **Web manual entry cascade & inline create (#728)** — Manual time logging and the dashboard start-timer modal now follow the mobile start-timer flow: pick a client first (the project picker unlocks only then), then one of that client's projects, then a task of that project. The client field stays a real searchable dropdown even when only one client exists, so another client can be picked or created inline; a newly created client is selected automatically. Inline project creation attaches automatically to the selected client — the client dropdown is replaced by a read-only display with an editable hourly rate pre-filled from the client default — instead of asking for a client again. Inline task creation creates the task immediately from the combobox "Create …" row (no confirmation modal) for the preselected project.
- **Searchable combobox dark mode & typing (#728)** — Combobox dropdowns build their DOM in JS with utility classes Tailwind purged from the stylesheet, leaving white-on-white rows in dark mode; the dropdown now uses always-emitted component classes (`tt-searchable-*`) with proper dark hover/selected states. Focusing a combobox selects its text so typing replaces the committed label instead of appending to it. Source-controlled assets (`searchable-select.js`, `inline-create.js`, `dist/output.css`) are served with mtime-based version queries via a new `static_url()` helper so edits bust the browser cache without a release bump.
- **Mobile start timer crash** — Two `FloatingActionButton`s with the default Hero tag coexisted in the home screen's `IndexedStack`, turning the screen red whenever a route was pushed on top (e.g. the start timer sheet); the Entries tab FAB now has a unique `heroTag`.
- **Mobile inline creation selection** — The v1 create endpoints wrap the payload (`{"message": …, "<entity>": {…}}`); unwrapping it ensures a newly created client/project/task is selected immediately in the start timer sheet.
- **Mobile stop/pause flicker** — Background timer polling no longer toggles `isLoading`, which disabled the Stop and Pause buttons for the duration of each 5-second sync; the flag is now reserved for user-initiated actions.
- **Android release signing** — CI signs release APKs with a stable keystore so updates install over previous builds.

### Documentation

- **Version** — Bumped `setup.py` to **5.13.2** (single source of truth for the application version).

## [5.13.1] - 2026-08-26

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ TimeTracker has been continuously enhanced with powerful new features! Here's wh

**Current version** is defined in `setup.py` (single source of truth). See [CHANGELOG.md](CHANGELOG.md) for versioned release history.

### ✨ Highlights of v5.13.2

**Patch (5.13.2):** **Linear setup wizard** — guided API-key setup matching other integrations. **Web manual entry (#728)** — cascading client → project → task pickers with inline create and dark-mode comboboxes. **Mobile start timer** — unified searchable pickers, project-first with client auto-fill, crash/selection/flicker fixes. **Android signing** — stable CI keystore for release APKs. See [CHANGELOG.md](CHANGELOG.md#5132---2026-08-27).

### ✨ Highlights of v5.13.1

**Patch (5.13.1):** **Projects by last used (#738)** — pickers and API lists surface recent work first. **Idle timeout (#722)** — auto-stop records the configured duration and surfaces Still working? on web, extension, and mobile. **Picker fixes (#728)** — blur resets typed-but-unselected text, Create project requires a client, translated Create labels. **Extension release zip** — Chromium extension package attached to GitHub Releases. See [CHANGELOG.md](CHANGELOG.md#5131---2026-08-26).
Expand Down
28 changes: 25 additions & 3 deletions app/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,36 @@
Integration connectors package.
"""

from .activitywatch import ActivityWatchConnector
from .asana import AsanaConnector
from .base import BaseConnector
from .github_connector import GitHubConnector
from .google_calendar_connector import GoogleCalendarConnector
from .slack_connector import SlackConnector
from .caldav_calendar import CalDAVCalendarConnector
from .github import GitHubConnector
from .gitlab import GitLabConnector
from .google_calendar import GoogleCalendarConnector
from .jira import JiraConnector
from .linear import LinearConnector
from .microsoft_teams import MicrosoftTeamsConnector
from .outlook_calendar import OutlookCalendarConnector
from .quickbooks import QuickBooksConnector
from .slack import SlackConnector
from .trello import TrelloConnector
from .xero import XeroConnector

__all__ = [
"BaseConnector",
"ActivityWatchConnector",
"AsanaConnector",
"CalDAVCalendarConnector",
"GitHubConnector",
"GitLabConnector",
"GoogleCalendarConnector",
"JiraConnector",
"LinearConnector",
"MicrosoftTeamsConnector",
"OutlookCalendarConnector",
"QuickBooksConnector",
"SlackConnector",
"TrelloConnector",
"XeroConnector",
]
26 changes: 25 additions & 1 deletion app/routes/integrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,29 @@ def setup_wizard(provider):
flash(result["message"], "error")
return redirect(url_for("integrations.setup_wizard", provider=provider))

# Linear uses a Personal API Key stored as integration credentials
if provider == "linear" and integration:
linear_api_key = request.form.get("linear_api_key", "").strip()
if linear_api_key:
key_result = service.save_credentials(
integration_id=integration.id,
access_token=linear_api_key,
refresh_token=None,
expires_at=None,
token_type="Bearer",
scope="read",
extra_data={"auth_type": "api_key"},
)
if key_result.get("success"):
integration.is_active = True
else:
return jsonify(
{
"success": False,
"message": key_result.get("message", _("Could not save API key.")),
}
)

# Update integration config
integration.config = current_config
from sqlalchemy.orm.attributes import flag_modified
Expand Down Expand Up @@ -1588,6 +1611,7 @@ def setup_wizard(provider):
"github": [_("OAuth"), _("Repositories"), _("Sync Config"), _("Webhooks"), _("Review")],
"asana": [_("OAuth"), _("Workspace"), _("Projects"), _("Sync Config"), _("Review")],
"trello": [_("API Keys"), _("Connection Test"), _("Review")],
"linear": [_("API Key"), _("Sync Config"), _("Test & Finish")],
"outlook_calendar": [_("Tenant ID"), _("OAuth"), _("Review")],
"microsoft_teams": [_("Tenant ID"), _("OAuth"), _("Review")],
}
Expand All @@ -1597,7 +1621,7 @@ def setup_wizard(provider):

# Get test connection URL if available
test_connection_url = None
if provider in ["jira", "gitlab", "trello"]:
if provider in ["jira", "gitlab", "trello", "linear"]:
test_connection_url = url_for("integrations.test_connection_wizard", provider=provider)

wizard_title = _("%(name)s Setup Wizard", name=display_name)
Expand Down
Loading
Loading