fix(sync): restore missing force upload button in new config UI

This commit is contained in:
Michael Chang 2026-01-19 13:58:23 +01:00
parent b230216e33
commit 222b3474b8
7 changed files with 502 additions and 97 deletions

36
FIX_PLAN.md Normal file
View file

@ -0,0 +1,36 @@
# 🛠️ Fix Plan: Restore Force Upload Button
**Branch:** `fix/restore-force-upload-ui`
**Base:** `upstream/master` (Latest RC)
**Goal:** Restore the "Force Upload" button lost during the Tabbed UI migration. This is critical for resolving Sync deadlocks (e.g. WebDAV 412 Precondition Failed).
## 📋 Steps
1. [x] **Setup:**
- Stashed personal user config.
- Created clean branch `fix/restore-force-upload-ui`.
2. [ ] **Code Changes:**
- `src/app/imex/sync/sync-wrapper.service.ts`:
- Change `private _forceUpload()` to `public forceUpload()`.
- `src/app/pages/config-page/config-page.component.ts`:
- Add the "Force Upload" button to the `globalSyncConfigFormCfg` configuration array.
- Use `btnType: 'warn'` to indicate destructive action.
- Ensure it calls `this._syncWrapperService.forceUpload()`.
3. [ ] **Verification:**
- Build locally (`npm run dist` or similar) to ensure compilation passes.
- (Optional) Verify UI appearance if possible.
4. [ ] **PR Submission:**
- Commit with convention: `fix(sync): restore missing force upload button in new config UI`.
- Push to `origin` (mycochang/super-productivity).
- Open PR to `super-productivity/super-productivity`.
5. [ ] **Cleanup:**
- Switch back to user working branch.
- Pop stash to restore personal context.
## 📝 Context & Rationale
The "Force Upload" button existed in v16 but was accidentally omitted during the Config Page rewrite (RC.6+). Without it, users facing "Remote file changed" errors have no in-app way to resolve the conflict, forcing manual file deletion on the server.

509
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":";AAAA,0CAA0C;AAC1C,gEAAgE;;;AAShE,IAAY,WAYX;AAZD,WAAY,WAAW;IACrB,2CAA4B,CAAA;IAC5B,6CAA8B,CAAA;IAC9B,yCAA0B,CAAA;IAC1B,yCAA0B,CAAA;IAC1B,wDAAyC,CAAA;IACzC,uCAAwB,CAAA;IACxB,iDAAkC,CAAA;IAClC,4DAA6C,CAAA;IAC7C,gCAAiB,CAAA;IACjB,gDAAiC,CAAA;IACjC,wDAAyC,CAAA;AAC3C,CAAC,EAZW,WAAW,2BAAX,WAAW,QAYtB;AA+eD;;;GAGG;AACH,IAAY,uBAoBX;AApBD,WAAY,uBAAuB;IACjC,oBAAoB;IACpB,uDAA4B,CAAA;IAC5B,+DAAoC,CAAA;IACpC,yDAA8B,CAAA;IAE9B,cAAc;IACd,2DAAgC,CAAA;IAEhC,qBAAqB;IACrB,6EAAkD,CAAA;IAClD,mFAAwD,CAAA;IAExD,qBAAqB;IACrB,qDAA0B,CAAA;IAC1B,uEAA4C,CAAA;IAC5C,iEAAsC,CAAA;IAEtC,mBAAmB;IACnB,iDAAsB,CAAA;AACxB,CAAC,EApBW,uBAAuB,uCAAvB,uBAAuB,QAoBlC;AAED,6CAA6C;AAC7C,2EAA2E;AAC3E,mBAAmB;AACnB,uBAAuB;AACvB,4BAA4B;AAC5B,MAAM;AACN,EAAE;AACF,uDAAuD;AACvD,gCAAgC;AAChC,IAAI"}
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":";AAAA,0CAA0C;AAC1C,gEAAgE;;;AAShE,IAAY,WAYX;AAZD,WAAY,WAAW;IACrB,2CAA4B,CAAA;IAC5B,6CAA8B,CAAA;IAC9B,yCAA0B,CAAA;IAC1B,yCAA0B,CAAA;IAC1B,wDAAyC,CAAA;IACzC,uCAAwB,CAAA;IACxB,iDAAkC,CAAA;IAClC,4DAA6C,CAAA;IAC7C,gCAAiB,CAAA;IACjB,gDAAiC,CAAA;IACjC,wDAAyC,CAAA;AAC3C,CAAC,EAZW,WAAW,2BAAX,WAAW,QAYtB;AAkfD;;;GAGG;AACH,IAAY,uBAoBX;AApBD,WAAY,uBAAuB;IACjC,oBAAoB;IACpB,uDAA4B,CAAA;IAC5B,+DAAoC,CAAA;IACpC,yDAA8B,CAAA;IAE9B,cAAc;IACd,2DAAgC,CAAA;IAEhC,qBAAqB;IACrB,6EAAkD,CAAA;IAClD,mFAAwD,CAAA;IAExD,qBAAqB;IACrB,qDAA0B,CAAA;IAC1B,uEAA4C,CAAA;IAC5C,iEAAsC,CAAA;IAEtC,mBAAmB;IACnB,iDAAsB,CAAA;AACxB,CAAC,EApBW,uBAAuB,uCAAvB,uBAAuB,QAoBlC;AAED,6CAA6C;AAC7C,2EAA2E;AAC3E,mBAAmB;AACnB,uBAAuB;AACvB,4BAA4B;AAC5B,MAAM;AACN,EAAE;AACF,uDAAuD;AACvD,gCAAgC;AAChC,IAAI"}

View file

@ -105,6 +105,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz",
"integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==",
"dev": true,
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
@ -1226,6 +1227,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.33.tgz",
"integrity": "sha512-wzoocdnnpSxZ+6CjW4ADCK1jVmd1S/J3ArNWfn8FDDQtRm8dkDg7TA+mvek2wNrfCgwuZxqEOiB9B1XCJ6+dbw==",
"dev": true,
"peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@ -1519,6 +1521,7 @@
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001718",
"electron-to-chromium": "^1.5.160",
@ -2136,6 +2139,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -2275,6 +2279,7 @@
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz",
"integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
}
@ -2317,6 +2322,7 @@
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.7.tgz",
"integrity": "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==",
"dev": true,
"peer": true,
"dependencies": {
"csstype": "^3.1.0",
"seroval": "~1.3.0",
@ -2526,6 +2532,7 @@
"integrity": "sha512-C/Naxf8H0pBx1PA4BdpT+c/5wdqI9ILMdwjSMILw7tVIh3JsxzZqdeTLmmdaoh5MYUEOyBnM9K3o0DzoZ/fe+w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",

View file

@ -311,7 +311,7 @@ export class SyncWrapperService {
// TODO translate
msg: T.F.SYNC.S.ERROR_DATA_IS_CURRENTLY_WRITTEN,
type: 'ERROR',
actionFn: async () => this._forceUpload(),
actionFn: async () => this.forceUpload(),
actionStr: T.F.SYNC.S.BTN_FORCE_OVERWRITE,
});
return 'HANDLED_ERROR';
@ -357,7 +357,7 @@ export class SyncWrapperService {
}
}
private async _forceUpload(): Promise<void> {
async forceUpload(): Promise<void> {
if (!this._c(this._translateService.instant(T.F.SYNC.C.FORCE_UPLOAD))) {
return;
}
@ -462,7 +462,7 @@ export class SyncWrapperService {
firstValueFrom(dialogRef.afterClosed())
.then(async (res) => {
if (res === 'FORCE_UPDATE_REMOTE') {
await this._forceUpload();
await this.forceUpload();
} else if (res === 'FORCE_UPDATE_LOCAL') {
// Op-log architecture handles this differently
SyncLog.log(
@ -490,7 +490,7 @@ export class SyncWrapperService {
firstValueFrom(dialogRef.afterClosed())
.then(async (res) => {
if (res === 'FORCE_UPDATE_REMOTE') {
await this._forceUpload();
await this.forceUpload();
}
})
.catch((err) => {
@ -530,7 +530,7 @@ export class SyncWrapperService {
this.sync();
}
if (isForceUpload) {
this._forceUpload();
this.forceUpload();
}
});
}

View file

@ -368,6 +368,20 @@ export class ConfigPageComponent implements OnInit, OnDestroy {
},
},
},
{
hideExpression: (m: any, _v: any, field: any) =>
!m.isEnabled || !field?.form?.valid,
type: 'btn',
className: 'mt2 block',
templateOptions: {
text: T.F.SYNC.C.FORCE_UPLOAD,
btnType: 'warn',
required: false,
onClick: () => {
this._syncWrapperService.forceUpload();
},
},
},
{
hideExpression: (m: any) =>
!m.isEnabled || m.syncProvider !== LegacySyncProvider.SuperSync,

View file

@ -0,0 +1,21 @@
- [ ] <!--yaB19mxBpu-dRz8XHgirg--> Custom SP Dashboard/Waybar Integration
- [ ] <!--XSHbiVgRKJK4wAZaTz12B--> Make GitHub -> Taiga Bidirectional
- [ ] <!--mOq-ocqA0vf49KEP7RMOz--> Jira Integration
- [ ] <!--TSfixg5u7FCb-MXR0z8bo--> n8n Deployment
- [ ] <!--bFrpNPJrmLosbcqKs7PBO--> Anytype Integration
- [ ] <!--0GNMdDkLE2JsmITTfK-Om--> Taiga <-> Trello Bidirectional Sync
- [ ] <!--rsqeql3D49Bdgg7_LBnCV--> use 3070 as primary on cachy on omen
- [ ] <!--gqT0eV4SAQ3qVpNVed42Y--> Test pytaiga-mcp with Claude Desktop
- [ ] <!--bENu9iWjOkTH2uTyinjFF--> Taiga -> Discord Webhook Relay
- [ ] <!--lA05Xk_X8uclGvUH6jD9s--> #5 nadir filtering idea for efficient hi-res orthos
- [ ] <!--H4FDHqQk_61RL6TbXrcyK--> #4 write to db / polish thie manifest file
- [ ] <!--l6DDlByiAr4P1Z9uo4iS9--> #2 Track which flight types have automated processing pipelines
- [x] <!--7AcwVNz3My0m1jnogLkCP--> Vibe ADHD Coaching
- [ ] <!--2kxhONHQgatI_yHmiqHg0--> Explore WebSocket IPC transition LATER
Something for the backlog.
- [x] <!--3bXFyE7jzEY3SuHdNPWEB--> Draft technical documentation for SP-MCP SOON
Reviewing the docs for future work.
- [ ] <!--L4lWlqCOVmg_gvIE3kcYx--> Verify MCP tag assignment
Initial cleanup of the workspace.
- [ ] <!--aG51kRBGjHLkz2Sm8Htfb--> Perform final security audit URGENT
Critical system check.