* [DeckShare] Browse and open public decks with loading, error and accessibility states
Add a public-decks tab that lists decks published by other users using the
server's deck visibility feature, previewing each deck's banner card, color
identity, tags and upload time without downloading the deck list until the
user opens it.
- Add a public-decks tab with a shared-settings widget and a remote model
that fetches the target user's decks and refreshes both automatically and
on user request, with a loading indicator and a server-error message
instead of a blank tab when the fetch fails or the connection drops
- Render each deck as a focusable preview tile whose banner, color identity,
tags and upload time follow the existing Preview settings, with the deck
name announced as the tile's accessible name and Space/Enter opening the
deck, mirroring the shared-deck preview tile
- Show a message box when opening a public deck fails or arrives corrupted
- Publish and unpublish decks from the server storage toolbar and context
menu, toggling the deck's own visibility bit (what the server persists)
rather than the inherited effective state, and batch the visibility
refresh until the last in-flight change is acknowledged
- Add the Show Upload Time setting so the tile's upload stamp can be hidden
like the other preview details
- Update the retranslateUi wiring for the new public-decks tab and rename
the share action tooltip from "Deck share" to "Share link"
* [DeckShare] Adapt deck upload to the server-derived banner and tag protocol
The server now derives the banner card and tags from the uploaded deck
list itself, so Command_DeckUpload only carries the client-computed color
identity. Drop the reserved banner/tag setters from the editor and storage
uploads, send the color identity on remote saves, and read tags from the
now-repeated ServerInfo_DeckStorage_TreeItem field.
* [DeckStorage] Refresh the visibility column with a guarded timer instead of a latch counter
A dropped visibility reply used to leave the pendingVisibilityChanges
counter permanently positive, so the Public/Private column never refreshed
again and nothing reset it on disconnect. A restartable single-shot timer
with a boolean guard re-reads the tree whenever publishes quiet down and is
stopped on disconnect, so a lost reply costs one stale refresh instead of
killing the column for the session.
* [DeckStorage] Summarize batch publish failures when the batch drains
Each rejected node stacked its own modal dialog, so publishing a ten-deck
selection against a rejecting server made the user dismiss ten dialogs one
at a time. Failures are now collected while the batch is in flight and shown
as a single summary when the visibility refresh timer fires; a reply that
lands outside an active batch still reports right away.
* [PublicDecks] Time out the loading state so a dropped reply cannot wedge the tab
loading only cleared in decksReceived, but the ping sweep can drop a pending
command without ever emitting finished, leaving the tab stuck on 'Loading
public decks...' and the refresh button permanently inert. A single-shot
timer started per refresh clears the latch and reports a timeout; the latch
also clears when the client disconnects.
* [PublicDecks] Escape remote-crafted text in tooltips and the tab title
Deck names and usernames come from other users' records and Qt renders
QLabel tooltips as AutoText, so a name like '<h1><table>...' parsed as
markup. Escape and bound the deck-name tooltip and escape the username
interpolated into the title label.
* [DeckStorage] Distinguish an inherited public state in the visibility column
The column reported the effective state while publishing toggles the node's
own bit, so a private deck inside a public folder already read 'Public' and
toggling appeared to do nothing (and toggling again silently unpublished
it). The cell now shows 'Public (inherited)' for that case and the tooltip
explains why.
* [PublicDecks] Run retranslateUi at construction and name the refresh button
retranslateUi was never called from the constructor, so the tooltips set
there were absent until a language change. Call it before the first refresh,
and give the icon-only refresh button an accessible name for screen readers.
* [PublicDecks] Keep the empty and status variants correct across language changes
retranslateUi unconditionally rewrote the empty label to the 'nothing
published' variant, stomping the 'no decks match your filters' choice
rebuildGrid had made, and a visible loading message stayed in the old
language. Let retranslateUi pick the same variant rebuildGrid does and
re-show the status so it retranslates.
* [VDS] Share one color-identity match rule between the two deck grids
The remote public decks model verbatim-copied updateColorMatches' switch,
down to the ExactMatch normalization and the fact that Includes/Excludes do
not normalize case. Extract colorIdentityMatches() next to the FilterMode
enum and call it from both so the subtle rule cannot drift.
* [DeckStorage] Drop the unused tree widget model accessor
The accessor handed the model out past the wrapper methods that exist to
keep it encapsulated, and nothing in the stack called it.
* [DeckShare] End the public-decks files with a trailing newline
keeps the final line's diff clean and stops clang-format CI from flagging
the files.
* [VDS] Reuse the shared quick settings widget for the public decks tab
PublicDecksQuickSettingsWidget was VisualDeckStorageQuickSettingsWidget
minus the folders, banner and tooltip controls, with identical wiring for
the shared keys and a version of the near-identical file to keep in sync by
hand. Fold the Show Upload Time checkbox into the shared widget, give it a
setPublicDecksMode() that hides the controls that do not apply, and delete
the duplicate.
* [PublicDecks] Drop stale deck-list replies after the loading timeout
A reply that lands after its own loading timeout (the reverse of the ping
sweep dropping the command) could stop the newer request's timeout timer
and repaint the grid with out-of-date data. Each refresh now captures a
monotonically increasing request id, and only the newest request's reply
updates the grid.
* [PublicDecks] Re-show a displayed failure message on language changes
The status label carries both the loading and the failure message, and
retranslateUi hid it whenever the model was not loading, so a language
change while a server-error or timeout message was on screen swapped it
for the (empty) grid. The tab now keeps the last failure text and
re-shows it when not loading, clearing it once a new refresh starts.
* [DeckStorage] Keep the visibility refresh armed until replies land
The single-shot drain was armed with the 500 ms delay at send time, so a
round trip slower than that drained before the server applied the change,
re-read the old state and never re-armed, leaving the column stale until
a manual refresh. The timer is now armed with the full network timeout at
send time (a lost reply still costs one stale refresh) and re-armed with
the short delay every time a reply lands.
* [DeckShare] Close public decks tabs when the client disconnects
TabSupervisor::stop() built tabsToDelete from the room and game tabs
only, so a public decks tab survived a disconnect, sitting with stale
contents and a refresh button that kept hitting the dead client. Its
values are now folded into the same cleanup.
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
* [DeckShare] Create temporary share links for local and server decks
* [DeckShare] Address review findings and harden the share flows
Gate every share entry point on login, de-duplicate the share-link and
color-identity logic behind DeckShareUtils and an injected querier, and
replace the silent tray/status-bar notices with always-visible dialogs.
- abstract_tab_deck_editor: explain that sharing requires a connection
instead of silently doing nothing when logged out
- tab_deck_storage: disable the share action on disconnect, reject
folder/deck mixes and the root folder with clear warnings, re-enable
Create on every entry/response so a dropped connection cannot leave
the button disabled
- tab_deck_storage_visual: same login gate for the context-menu entry,
visible success/error dialogs, and a symmetric in-flight guard
- getDeckColorIdentity now takes a CardDatabaseQuerier, dropping the
CardDatabaseManager singleton access and enabling unit tests
* [DeckShare] Fix share-link expiry build on the minimum-supported Qt
QTimeZone::UTC (the Initialization enum) only exists since Qt 6.7, so
Debian 12 and Ubuntu 24.04 (Qt 6.4) fail to compile the share-link expiry
handling in the share dialog and the two deck-storage tabs. Mirror the
existing games_model guard and fall back to Qt::UTC on older Qt.
* [DeckShare] Use the stable server client for the visual deck storage tab
* [DeckShare] Extract the share-creation response handling into DeckShareUtils
* [DeckShare] Drop includes left unused by the share-response extraction
* [DeckShare] Format share expiry with the locale-aware short format
* [DeckShare] Build share links with QUrl and QUrlQuery for percent-encoding
* [DeckShare] Replace the duplicate computeColorIdentity with the shared getDeckColorIdentity
* [DeckShare] Recover the share controls when the server never answers
* [DeckShare] Provide the full share hint in each plural form
* [DeckShare] Join the selected-count label with a non-translatable separator
* [DeckShare] Retranslate the share button tooltip with the storage widget
* [DeckShare] Forward retranslateUi to the visual deck storage widget
* [DeckShare] Let the share bar owners supply the hint text
* [DeckShare] End the share-related headers and sources with a trailing newline
* [DeckShare] Keep the settings include in the project include block
* [DeckShare] Include the network settings header used by the share timeout
* [DeckShare] Resolve the share theme icon through themePixmap
QPixmap("theme:icons/share") has no file extension, so ThemeManager::assetPath()
is bypassed and the pixmap is always null. Use themePixmap(QStringLiteral("icons/share"))
like every other toolbar action, so the .svg (and dark/light variants) resolves.
* [DeckShare] Keep the share selection consistent with the visible decks
Filtered-out previews are hidden but kept alive, so selectedFilePaths() counted
them in the share and the selection highlight. Only decks the user can see are
now shared, and a deck that stops matching the filters is deselectd as the deck
pass runs, keeping the %n count and the highlight in sync with the screen.
* [DeckShare] Abandon an in-flight tree share on cancel
Leaving share mode never stopped the timeout timer, and a late response still
ran shareFromTreeFinished, copying the link and announcing success for a share
the user backed out of. Stopping the timer and tracking the outstanding request
by sequence number means a stale reply (or a timed-out one) after cancel is
ignored, and cancelling + re-entering share mode can no longer confuse the two
requests.
* [DeckShare] Abandon an in-flight tile share on cancel
exitShareMode() left shareTimeoutTimer running and did not abandon the pending
Command_DeckShareCreate, so a timer pop or a late success still reported the
share after the user cancelled. Stop the timer and ignore stale responses via a
sequence number, mirroring the tree tab.
* [DeckShare] Wire the status-changed handler after shareBar exists
handleConnectionChanged() dereferences shareBar->isVisible(), but the connection
was set up before shareBar was constructed and shareBar had no in-class
initializer. On any status change delivered before construction the slot read an
indeterminate pointer. Seed the connection (and the initial share availability)
after shareBar exists and give shareBar a = nullptr initializer.
* [DeckShare] Explain why a blank deck cannot be shared
A blank deck exited the share flow silently. The menu only disables the entry
via setSaveStatus(), a different predicate, so the path is reachable (e.g. add a
card and remove it again). Mirror the not-logged-in branch with a short
information dialog.
* [DeckShare] Restore the banner-text doc comment
Re-add the doc block above refreshBannerCardText() that was removed as part of
the share-selection work; it documents the coupling to refreshBannerCardToolTip.
* [DeckShare] Resolve the stable server client in the deck editor gate
actShareDeck went through tabSupervisor->getClient(), which hands back a
LocalClient while an offline game is running. LocalClient never sets its status,
so a logged-in user could not share from the deck editor during a local game,
and got a misleading "You must be connected" message. Expose the supervisor's
stable remote client and use it for the gate and the dialog, matching the other
share tabs.
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
* [Cleanup] Unused #includes
Took 44 minutes
* [Cleanup] More unused #includes
Took 55 minutes
* [Cleanup] Include QSet
Took 4 minutes
* [Cleanup] Include QDebug in deck_list.cpp
Took 3 minutes
* [Cleanup] Include protocol stuff in servatrice_database_interface.h
Took 3 minutes
* [Cleanup] Include QDialogButtonBox
Took 8 minutes
* [Cleanup] Include QUrl
Took 8 minutes
* [Cleanup] Include QTextOption in header.
Took 3 minutes
* [Cleanup] Include QMap in user_list_manager.h
Took 8 minutes
* [Cleanup] Adjust qjson
Took 8 minutes
* [Cleanup] include button box.
Took 3 minutes
* [Cleanup] Redo fwd declarations.
* [Cleanup] Redo last removed fwd declarations.
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
* [DeckList] Disable copy constructor
Took 1 hour 44 minutes
Took 1 minute
# Commit time for manual adjustment:
# Took 28 seconds
Took 33 seconds
* Revert member to pointer.
Took 19 minutes
* Revert pulling up setters/getters now that getDeckList is no longer const.
Took 6 minutes
* Revert more.
Took 2 minutes
* One more fix.
Took 1 minute
* Update cockatrice/src/interface/deck_loader/deck_loader.cpp
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
Co-authored-by: RickyRister <42636155+RickyRister@users.noreply.github.com>
* A DeckLoader is not a DeckList.
Took 2 hours 39 minutes
* Explicitly initialize base class in copy constructor?
Took 3 minutes
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
* update format.sh
add shellcheck to format.sh
add statement macros to .clang-format
add no clang format to format.sh
add changed file list to format.sh diff
rename --cf-version to --print-version in format.sh
lint files
* enable --shell on ci runs
* remove useless semicolons
removes the semicolons after empty function definitions
these semicolons are optional, they don't do anything
this will have functions be consistently formatted
if we want to keep the option to have these on the same line like they
were before we should use the option AllowShortFunctionsOnASingleLine: None
* fix script
* update echo line in lint_cpp.sh which doesn't lint cpp only at all
* Move models to own library.
Took 35 minutes
Took 22 minutes
* Adjust CMakeLists
Took 20 seconds
* Reformat CMakeLists.
Took 2 minutes
* Revert "Reformat CMakeLists."
This reverts commit db5982ad1c.
Took 55 seconds
* Lint an include
Took 17 minutes
Took 9 seconds
---------
Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>