feat(deck): build a native kanban workspace - #221
Conversation
Signed-off-by: veryCrunchy <me@verycrunchy.dev>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fabe0863f9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed8193a313
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d5dbe6cfe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
https://github.com/Obiente/nc-native/blob/d52ec5787131955b3e157b4cbccaab64472060f6/ui/src/commonMain/kotlin/dev/obiente/nextcloudnative/nativeui/runtime/GenericNativeRenderer.kt#L1787-L1788
Keep dynamic move reconciliation across refreshes
After a discovered-board drag succeeds, this callback increments the host's loadAttempt; the forced reload sets viewState to Loading, removing GenericRecordBoard from composition and discarding its remembered pendingMove. When the refreshed board returns, the verifier therefore cannot confirm that the record reached the requested lane or report that a 2xx move was ignored. Hoist the pending receipt above the loading/ready branch or keep the board composed while refreshing.
AGENTS.md reference: AGENTS.md:L192-L194
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92b814dd11
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }.getOrElse { error -> | ||
| DurableUploadEnqueueResult.Rejected( | ||
| error.message?.take(MAX_DURABLE_UPLOAD_MESSAGE_CHARACTERS) | ||
| ?: "The attachment upload failed.", | ||
| ) |
There was a problem hiding this comment.
Preserve unknown upload outcomes on desktop
On Linux desktop, which does not override this default, a transport exception or HTTP 5xx after Deck accepts the multipart body is collapsed into Rejected, so the screen releases the selected file and reports a definite failure; retrying can then create a duplicate attachment. Fresh evidence after the earlier upload-lifecycle finding is that Android now has durable outcome tracking while DesktopNextcloudServices still uses this synchronous fallback; classify ambiguous results as outcome-unknown and retain recovery state until the card is reconciled.
AGENTS.md reference: AGENTS.md:L187-L194
Useful? React with 👍 / 👎.
| onCreateStack = currentBoard?.takeIf { it.permissions.canManage }?.let { board -> | ||
| { interaction = DeckUiInteraction.StackEditor(board, null) } | ||
| }, | ||
| onCreateCard = currentBoard?.takeIf { it.permissions.canEdit }?.let { | ||
| { stack -> interaction = DeckUiInteraction.CardEditor(stack, null) } |
There was a problem hiding this comment.
Hide active-board mutations after archival
When another client archives the currently open board and the authoritative refresh updates board.archived, these creation callbacks remain exposed because they check only permissions; stack/card actions and drag use the same incomplete condition. The resulting dialogs or drag interaction accept user input only for requireActiveManage/requireEdit to reject the request as read-only, so include !board.archived when deriving all active-board mutation affordances. Fresh evidence after the archived-comment finding is that comment controls now honor archival, but these non-comment controls still do not.
AGENTS.md reference: AGENTS.md:L168-L169
Useful? React with 👍 / 👎.
| val durableUploads = runCatching { | ||
| services.durableMultipartUploadStatuses(session, attachmentUploadScope(card)) | ||
| }.getOrDefault(emptyList()) |
There was a problem hiding this comment.
Refresh background upload status while the dialog is open
On Android, this reads durable upload status only when attachments are loaded, and the post-enqueue read normally occurs before WorkManager finishes; there is no polling or worker-driven refresh afterward. A dialog left open therefore continues to show "Waiting for background upload" after completion or failure and does not reveal the new attachment until it is closed and reopened; observe or poll active jobs and refresh the authoritative attachment list when they become terminal.
AGENTS.md reference: AGENTS.md:L217-L218
Useful? React with 👍 / 👎.
| val discoveredLanes = remember(resource, records, declaredLanes) { | ||
| declaredLanes ?: nativeBoardLanes(resource, records) | ||
| } | ||
| val initialLaneOrder = remember(resource.id) { discoveredLanes.map(NativeBoardLane::key) } |
There was a problem hiding this comment.
Scope retained lane order to the active board context
When a discovered app switches between views or parent boards that reuse the same resource.id, this remember retains the first board's lane order and applies it to the new board. DynamicDiscoveredAppScreen reuses the same renderer call as selectedView and navigation context change, so two boards backed by a common resource such as cards can display the second board's lanes in the first board's order; key this state by the app, view, and active container/parent identity rather than the resource type alone.
AGENTS.md reference: AGENTS.md:L154-L161
Useful? React with 👍 / 👎.
| value.descriptionMarkdown.length > DECK_UI_CARD_DESCRIPTION_LIMIT -> | ||
| "The description is too long." |
There was a problem hiding this comment.
Validate descriptions against the encoded request limit
This validation counts UTF-16 characters, while deckJsonRequest later rejects the UTF-8 JSON body above 96 KiB. For example, a description containing 40,000 ordinary CJK characters is below the advertised 65,536-character limit but encodes to roughly 120 KiB before JSON overhead, so the enabled Save/Create action always fails during request construction; validate the encoded request size in the form or align the character and transport limits.
AGENTS.md reference: AGENTS.md:L173-L183
Useful? React with 👍 / 👎.
What changed
Why
Deck previously appeared as generic API records, so its data was visible but the workflow was not useful. This branch establishes a native kanban experience backed by explicit Deck semantics and guarded write contracts. The same interaction model also works for dynamically discovered board-shaped apps without depending on a Deck app identifier.
Verified
Remaining review gates
Advances #52