mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-22 15:37:02 +00:00
7 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c7d6131db8
|
feat(plainspace): Collaborate-on-Plainspace discovery + smoother connect (#8649)
* feat(plainspace): add Collaborate action to project context menu Surface Plainspace sharing from the project context menu (active, non-Inbox, not-yet-shared projects) so it can be discovered at the moment of collaboration intent, reusing PlainspaceShareService. A new selectIsProjectSharedOnPlainspace selector hides the action once a project is shared to avoid provisioning a duplicate space on a repeat click. * docs(plainspace): document collaboration in wiki Add Plainspace to the issue-integration comparison (matrix + per-provider section) and a how-to for sharing a project via the project menu, including the network/privacy caveat. * feat(plainspace): place Collaborate action below the share-list item Group the two share/export actions and lift Collaborate higher for discoverability. Gated to active, non-inbox, not-already-shared projects. * fix(plainspace): smoother first-run connect and value-first dialog Pre-check connectivity and revalidate a stored token before the space picker, so a stale/foreign token routes to the connect dialog and an offline state shows a calm message instead of the raw 'check your token' picker error. Make the connect dialog value-first: lead with what you get, drop the 4-step how-to and email hint in favor of one short pointer (token-creation guidance moves to plainspace.org). * docs(plans): dedicated from-Super-Productivity flow on plainspace.org Open plan for a guided token/connect flow on plainspace.org when a user arrives from SP (Model A manual token, Model B OAuth-style handoff). * refactor(plainspace): drop redundant connect pre-check (multi-review) The space picker already detects a stale token and offers a reconnect (#8616), so the revalidate() pre-check duplicated that path, cost an extra GET /me, and forced re-auth on a valid token during a transient server blip. Keep only the one-line offline guard; drop revalidate(), the discriminated union, and the unnecessary _isOnline() seam (navigator .onLine is spyable in the runner). Fix stale doc comments and document the disabled-provider trade-off in the selector. * feat(plainspace): show brand icon in the connect dialog title * feat(plainspace): deep-link connect dialog to the from-SP onboarding flow Point the dialog's 'Open Plainspace' link at the dedicated /connect/super-productivity entrypoint (which guides token creation) instead of the bare marketing host, closing the connect-flow funnel leak. * feat(plainspace): bounce back to the app after connecting (desktop) Append a validated `?return=superproductivity://plainspace-connect` deep link to the connect URL, gated on IS_ELECTRON (only desktop registers the scheme — web/mobile would get dead buttons). Handle that action in the Electron protocol handler by surfacing the window, so the connect page's "Open Super Productivity" button re-focuses the app. Desktop-only; needs an on-device check. |
||
|
|
14a56f861e
|
feat(electron): trigger global shortcuts via superproductivity:// URLs (#8645)
* feat(electron): trigger global shortcuts via superproductivity:// URLs On Wayland the compositor owns global hotkeys, so Electron's globalShortcut often does not register. Add three actions — toggle-visibility, new-note and new-task — to the existing protocol handler so a compositor keybind can call `xdg-open superproductivity://<action>`. No extra CLI tool or runtime is needed: xdg-open (Linux), open (macOS) and start (Windows) already ship with the OS, and the running instance receives the URL via the existing single-instance / second-instance path. Extract the show/hide logic into a shared toggleWindowVisibility() used by both the globalShowHide shortcut and the new protocol action, and add a key-repeat debounce so one held key press no longer hides then immediately re-shows the window. Docs: add a Wayland keybind recipe (Niri/sway/Hyprland) to the keyboard shortcuts wiki page. Refs #7114 * fix(electron): correct toggle-visibility focus race and debounce On Linux/Windows the second-instance handler pre-focused the window before processProtocolUrl ran, so toggle-visibility always read 'visible' and hid the window the user asked to show. Skip that pre-focus for toggle-visibility only (new getProtocolAction helper); every other action keeps the bring-to-front behavior. Make the key-repeat debounce direction-agnostic with a sliding quiet-gap (1000->750ms): it now guards both show and hide, settles a held key on a single toggle, and fires on the xdg-open path where the old isHidden-only guard was always false. Stop logging the create-task title and URL path to the exportable log (CLAUDE.md rule 9 / privacy). Add coverage for the real second-instance path, held-key-from-hidden, gap expiry, the #7282 minimize fallback, the macOS hide path, unfocused-show, and the log redaction; document AppImage/Flatpak/Snap scheme registration. Refs #7114. * fix(electron): show window on cold-start toggle-visibility launch Cold start: when superproductivity://toggle-visibility launches the app (it wasn't running), the freshly-shown window was immediately hidden again because the toggle saw it focused. Flag the cold-start URL during the argv scan and SHOW — never toggle — the window once it's ready (via processPendingProtocolUrls), respecting start-minimized-to-tray. The already-running second-instance path keeps real toggle behavior. Rename the two interactive protocol actions new-task/new-note to add-task/add-note: aligns with the app's Add-Task vocabulary and the globalAddTask/globalAddNote keys, and avoids colliding with the programmatic create-task/<title>. The action names are a frozen public contract once users bind them in compositor configs, so this is the pre-merge moment to settle the naming. Refs #7114. |
||
|
|
9910d30fca
|
feat(sync): add OneDrive sync provider with PKCE auth (#7523)
* feat(sync): integrate onedrive with pkce and operation log sync * fix(sync): add oauth state validation and token refresh concurrency control * fix(sync): refactor onedrive oauth cleanup and reduce download API calls - Replace setInterval-based OAuth state pruning with on-demand _pruneExpiredOAuthStates() to avoid background timer overhead - Optimize downloadFile to read ETag from content response headers first, falling back to a metadata request only when needed (reduces API calls from 2 to 1 in the common case) - Narrow OneDrive class interface from SyncProviderServiceInterface to FileSyncProvider for stronger type guarantees - Accept optional devPath constructor parameter for non-production folder namespacing - Extract isOneDriveClientIdRequired to a helper function in sync-form.const.ts for readability - Change default OneDrive sync folder name to 'Super Productivity' Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 review feedback for OneDrive sync Critical fixes: - Detect invalid_grant in token refresh responses and clear credentials - Reset _tokenRefreshInFlightPromise in clearAuthCredentials to prevent stale refresh results from overwriting cleared state - Re-read config before persisting refreshed tokens to avoid race with concurrent clearAuthCredentials calls Important fixes: - Extract OAuth state management to shared oauth-state.util.ts to fix circular dependency between OneDrive provider and callback handler - Add 401 retry pattern (_is401Retry) matching Dropbox's approach: on 401, proactively refresh token and retry, only clear credentials if retry fails - Remove unused auth status translation keys (AUTH_SUCCESS, BTN_AUTHENTICATE, BTN_REAUTHENTICATE, REAUTH_SUCCESS, STATUS_CONFIGURED, STATUS_NOT_CONFIGURED) and form props - Remove unused requireAuth parameter from _cfgOrError Minor fixes: - Replace path-exposing log messages with structured logging - Remove unused _formatHttpErrorDetails helper - Return empty ETag fallback in getFileRev instead of throwing - Add folder cache invalidation comment for path changes - Add afterEach to restore global fetch in tests - Add PKCE defence-in-depth comment in auth code dialog Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): use eslint-disable-next-line for @microsoft.graph.conflictBehavior Replace spread+computed-property workaround with a plain property plus an explicit eslint-disable-next-line comment. Clearer intent: the naming violation is a Graph API requirement, not accidental. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): pre-save OneDrive form config in reauth() to prevent MissingCredentialsSPError Re-auth was failing silently on Linux because getAuthHelper() reads clientId from IndexedDB (privateCfg), but the form values were never written before calling configuredAuthForSyncProviderIfNecessary(). - Extract shared BTN_REAUTHENTICATE and REAUTH_SUCCESS translation keys - Add OneDrive to OAUTH_SYNC_PROVIDERS - Pre-save form config in reauth() matching the existing save() flow Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): adapt TooManyRequestsAPIError to new constructor signature after rebase * refactor(sync): migrate OneDrive provider to packages/sync-providers Move OneDrive core logic into the shared sync-providers package, matching the pattern used by Dropbox, WebDAV, and other providers. The app-side code is now a thin adapter with a createOneDriveProvider() factory function. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): re-throw MissingRefreshTokenAPIError from _requestOAuthToken catch block The catch block in _requestOAuthToken was swallowing the MissingRefreshTokenAPIError thrown after clearing credentials on invalid_grant, causing it to fall through to a generic HttpNotOkAPIError instead. Now re-throws MissingRefreshTokenAPIError explicitly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 second review critical and important items - Replace _is401Retry instance field with per-call parameter to prevent concurrent 401 races (Critical #1) - Fix _requestOAuthToken catch block to re-throw MissingRefreshTokenAPIError instead of swallowing it (Critical #2) - Fix _parseOAuthCallback defaulting unknown provider to 'unknown' instead of 'dropbox' to prevent misrouting (Critical #3 + Important #6) - Change conflictBehavior from 'replace' to 'fail' to prevent data loss if a file exists with the same name as the folder (Critical #4) - Await in-flight token refresh promise in clearAuthCredentials before clearing, to close the refresh-during-clear race (Important #5) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 review important and minor items - Extract duplicate OneDrive pre-auth save block into _persistOneDriveFormCfgBeforeAuth() method (Important #7) - Add GET probe in _ensureSyncFolderExists to skip per-segment creation when folder already exists (Important #9) - Redact sensitive params (code, code_verifier, refresh_token, access_token) from token endpoint and Graph error bodies (Important #11) - Remove targetPath from _mapAndThrow error messages to prevent logging user content (Important #12) - Add OAuth state validation in manual paste flow for OneDrive (Important #13) - Fix unknown provider routing in _parseOAuthCallback (Important #14) - Guard Electron IPC listener against post-destroy emissions (Important #15) - Remove dead authStatus Formly field from sync-form.const.ts (Minor) - Simplify Headers builder in _ensureSyncFolderExists (Minor) - Fix spec afterEach to restore original fetch instead of undefined (Minor) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): fix clearAuthCredentials race and add unit tests for OneDrive Fix a race condition in clearAuthCredentials where _tokenRefreshInFlightPromise was nulled AFTER awaiting, causing the invalid_grant handler's clearAuthCredentials to wait on itself. Now captures the promise and nulls the field before awaiting. Add 5 new unit tests covering critical code paths: - 401 token refresh and retry - 401 retry failure with credential clearing - 400 invalid_grant credential clearing - 412 precondition failed mapping - Concurrent token refresh deduplication Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 third review items - Simplify clearAuthCredentials: null _tokenRefreshInFlightPromise without awaiting to avoid deadlock when called from inside the refresh IIFE - Add superproductivity:// scheme validation in Electron OAuth listener - Fix invalid_grant test to assert MissingRefreshTokenAPIError - Fix folder cache test to match GET-probe optimization - Set setComplete default in beforeEach Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(sync): fix OAuthCallbackHandler spec to expect 'unknown' provider The _parseOAuthCallback now returns 'unknown' instead of 'dropbox' for URLs without an explicit provider path segment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 fourth review items - Handle generic 401 from token endpoint (clear creds + MissingRefreshTokenAPIError) - Remove user-supplied paths from error constructors (rule 9 compliance) - Soften clearAuthCredentials comment to reflect actual TOCTOU guarantee - Tighten Electron scheme gate to match native path prefix - Add scheme info to Electron rejection log - Fix folder-cache test to assert GET probe count - Remove redundant per-test setComplete stub Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 fifth review items - Scope invalid_grant/401 credential clearing to refresh_token grant only (bad auth code exchange no longer wipes existing credentials) - Redact OAuth code/state from Electron protocol handler logs - Add @sp/sync-providers/onedrive TS path alias to tsconfig files - Default undefined sync config fields instead of overwriting with undefined - Handle OneDrive in provider-switch branch (preserve encryptKey) - Update sync-config test expectations to match default values Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 sixth review items - Use If-None-Match: * for null-rev uploads to prevent concurrent overwrite - Guard in-flight refresh against stale credentials (match refreshToken/clientId/tenantId) - Gate OneDrive provider option behind Electron/Native (web CORS unsupported) - Don't clear credentials on transient token endpoint errors (429/5xx) - Preserve null syncProvider instead of defaulting to WebDAV - Hydrate full OneDrive privateCfg on provider switch - Remove duplicate syncInterval/isManualSyncOnly Formly controls - Revert non-English locale changes (zh.json, zh-tw.json) - Redact URL fragments (#) in Electron protocol handler logs - listFiles rethrows non-404 errors instead of swallowing all - Update 401 test expectations for new rethrow behavior Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): guard credential clearing against stale concurrent refresh Prevent a stale in-flight refresh from wiping newer credentials after a concurrent re-auth. The refresh IIFE now throws a plain Error (not MissingRefreshTokenAPIError) when it detects config drift, and all clearAuthCredentials() call sites now verify the stored config still matches before clearing. Also fix listFiles() to handle 404 from _requestJson (which throws HttpNotOkAPIError, not RemoteFileNotFoundAPIError). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 eighth review items - Replace If-None-Match:* with @microsoft.graph.conflictBehavior=fail query param for upload create-only semantics - Add identity change detection in _persistOneDriveFormCfgBeforeAuth to clear tokens when useCustomApp/clientId/tenantId change - Extract _clearIfConfigMatches helper to guard credential clearing against stale concurrent refresh - Restrict folder probe fallthrough to 404 only, propagate other errors - Restore locale files from upstream master - Centralize IS_ONEDRIVE_SUPPORTED in onedrive-auth-mode.const.ts, use in factory and form config Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): address PR #7523 ninth review items - Paginate listFiles via @odata.nextLink to avoid silent data loss - Fix logger misuse (log -> normal with proper string message) - Replace as any with OneDrivePrivateCfg in dialog-sync-cfg - Throw NoRevAPIError when uploadFile response lacks eTag - Omit undefined optional booleans from global config to prevent overwrite - Move OneDrive dynamic import inside IS_ONEDRIVE_SUPPORTED gate - Require state param for OneDrive full-URL paste (CSRF) - Add id_token to sensitive keys for body redaction Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(sync): add OneDrive type alias to electron tsconfig paths The @sp/sync-providers/onedrive path alias was missing from electron/tsconfig.electron.json, causing the Electron build to fail with "Cannot find module '@sp/sync-providers/onedrive'". The alias was already present in tsconfig.base.json but electron uses its own paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
fa8bad6292 |
fix(electron): restore hidden window on taskbar click
Fixes #6042 where app wouldn't launch from taskbar when minimize to tray was enabled. The second-instance handler now uses showOrFocus() which properly handles both hidden and minimized windows, matching the behavior of the tray icon click handler. |
||
|
|
9efa649857 | feat: add task toggle start action to protocol handler | ||
|
|
fb7f925e92 |
fix: error when using global add task shortcut
Closes #4859 |
||
|
|
cf1031c935 | feat: add protocol handling to allow adding tasks from other apps |