Skip to content

feat(feedback): feedback conversations with team replies - #1228

Merged
ashwkun merged 8 commits into
masterfrom
support-sending
Nov 1, 2026
Merged

ashwkun merged 8 commits into
masterfrom
support-sending

Conversation

@ashwkun

@ashwkun ashwkun commented Nov 1, 2026

Copy link
Copy Markdown
Member

Summary

  • Feedback becomes a two-way conversation. A listener picks a type, writes a message and sends it without giving an email address; the boxlore team's replies show up in Feedback, Home's Feedback button shows a dot while a reply is unread, and a notification arrives when one comes in (if notifications are on).
  • Everything the screen offers (types, limits, whether photos can be attached, a pause notice) comes from the server, so it can change without an app update.

Motivation

  • The old form only sent a one-way email. Listeners never heard back unless they left an address, and we had no way to ask a follow-up question about a bug.

What changed

  • New :core:support module: SupportRepository (send, list, open, reply, mark read, mark solved, photo fetch), local list cache and topic sync for reply notifications, and photo preparation (resize, re-encode, strip metadata) ready for when photos are switched on.
  • :core:network: SupportApi (suspend Retrofit), RequestProofSigner + RequestProofInterceptor that sign each support request with the install's device key, and AccountTokenInterceptor so a signed-in listener's conversations follow them to their other devices. DEVICE_KEY_REQUIRED and PROOF_REQUIRED join the shield codes.
  • :feature:settings Feedback: compose screen (type pills, message, optional diagnostics, privacy link), sent screen, conversation list and conversation screen with reply, load earlier, mark solved and photo viewing. On wide windows the list and the open conversation sit side by side.
  • Retry keys (RetryKeys / RetryKeyStore) so a send retried after a failure or a process restart never creates a duplicate.
  • :app: support_reply push handling, a "Feedback replies" notification channel, the feedback?thread= route (allowed for pushes; it closes open detail screens and keeps the compact player), a check for new replies every 30 minutes while the app is in use, and the account token wiring.
  • :core:designsystem: LocalFeedbackReplyWaiting drives the reply dot on the top-bar Feedback action.
  • Privacy page in the app and docs/PRIVACY.md describe feedback conversations and replies; the privacy link points to boxlore.in.
  • READMEs updated for core/support, core/network, core/designsystem, feature/settings and app.

Behavior & compatibility

  • Before: Feedback sent a one-way message by email, optionally with the listener's address. After: it starts a conversation the listener can come back to.
  • Older app versions keep using the existing feedback path unchanged.
  • New push payload type support_reply (threadId, title, body); older clients ignore it.
  • Photo attachments stay hidden until the server turns them on.
  • If support is unavailable or paused, the screen says so instead of failing silently; a draft that hasn't been sent stays on the phone.

Impact (required)

User impact — pick exactly one

Label Use when
user-impact-critical Critical fix — listeners already feel a correctness bug (missing episodes, late/wrong alerts, data loss)
user-impact-high Listeners clearly notice (player, search, downloads, onboarding, major UX)
user-impact-medium Noticeable but not headline (polish, secondary flows)
user-impact-low Minor user-facing tweak
no-user-impact CI, docs, tooling, internal-only — no listener-facing change
  • user-impact-critical
  • user-impact-high
  • user-impact-medium
  • user-impact-low
  • no-user-impact

Listener impact — required when user-impact-critical, user-impact-high, or user-impact-medium

What changes in the user’s life:

  • Sending feedback no longer needs an email address, and the boxlore team can now answer inside the app. Replies show up in Feedback, Home's Feedback button lights up, and a notification says when one arrives.

Backend — optional, pairable with any user-impact level

Label Use when
backend-change Touches server / proxy / infra (can combine with critical/high/medium/low/none)
  • backend-change

Release copy (verbatim)

CHANGELOG.md (required developer copy)

Added

  • Feedback conversations: new :core:support module (SupportRepository, local cache, reply topic sync, photo prep), SupportApi, and Feedback list, conversation and sent screens in :feature:settings, with list and conversation side by side on wide windows.
  • Signed support requests (RequestProofSigner, RequestProofInterceptor), AccountTokenInterceptor, and retry keys that survive process restarts.
  • support_reply push handling with a "Feedback replies" notification channel, the feedback?thread= push route and a reply dot on the top-bar Feedback action (LocalFeedbackReplyWaiting).

Changed

  • Feedback sends a conversation instead of a one-way email; the privacy page and docs/PRIVACY.md describe conversations and replies.

Listener notes (required for user-impact changes)

  • Listener notes already cover this change

release-notes/upcoming.md gains one line under New.

Test plan

  • Full local gate: detekt, testDebugUnitTest (all modules), koverVerifyMerged, dependency guards, lintDebug, release-script tests and architecture checks
  • JVM coverage for the repository, retry keys, request signing, push routing, list/conversation view models, draft and selection kept across window changes, and the pane layout on both sides of the 840dp split
  • Built / installed locally (./gradlew installDebug) when UI or app behavior changed
  • Manual checks: send feedback, receive a reply, notification opens the conversation, reply, mark solved
  • Required checks are green before merge, or a maintainer explicitly authorized a check bypass for this PR

Notes (optional)

  • Adaptive states: phone portrait and landscape, tablet and unfolded foldable (list and conversation side by side at 840dp and wider), and resizing across that width keep the draft, keyboard focus, open conversation and list scroll position. Covered by JVM tests; device checks so far are on a phone only.

Feedback goes to the new support API through :core:support, falling back to the old form while it is unavailable. The screen is rebuilt around type pills, a roomy message card and a keyboard bar, and a sent screen offers notifications when the reply will show up in the app.
Support requests carry X-Request-Proof, the device key's signature over the session, method and path, which the support API now requires. Each send carries a retry key that a resend of the same message reuses, and meta lists every push topic to subscribe to.
…nges

The compose screen is one layout, so the keyboard or a height change no longer replaces the message field and loses its focus. The draft is locked while sending, an older draft past the limit is kept whole until shortened, the server's message limit and categories apply as sent, the phone model goes only with diagnostics, and photos are left out (and said so) when support can't take them. The old form is used only when it can deliver, a stale pause is cleared once support is gone, support calls are cancellable and never cached, and the photo row scrolls on narrow phones.
Feedback lists the listener's conversations, opens each with the team's replies and how it ended, and takes replies; landscape tablets show the list and the conversation side by side. A reply push lights Home's Feedback button and posts a Feedback replies notification that opens the conversation, and the app joins the push topics the server lists. Feedback keeps the mini player small, as Lore does.
…oken

A send's retry key is kept until the message is saved or discarded, so a retry after the app was closed isn't saved twice, and photos already uploaded are reused. Support requests carry the signed-in account's ID token from the first request, so conversations link to the account, and a missing device-key proof is no longer retried with a token.
…hat was read

A conversation loads each time it shows, reloads on a reply push, and is marked read only once on screen, up to the newest message. Its reply draft and retry key live in saved state; a closed answer shows its ending at once; photos use a small retrying cache and scroll sideways. The list and conversation keep their places across the split gate, the shown conversation stays selected when narrowing, and a late send no longer clears a newer draft. Push topics follow every refresh, a list from before a push can't clear it, start-up checks count failed attempts, and a reply notification closes an open tablet detail.
The list, compose and sent screens each send their own page view (feedback_list, feedback, feedback_sent; the conversation already sent feedback_thread), and the host that only holds them is exempt from the page-view guard.
The in-app Privacy page and docs/PRIVACY.md say what feedback sends, where conversations are kept and who they're tied to, what Telegram receives and never receives, how long photos are kept, and how to have conversations deleted. The Feedback screen's privacy link points at the new section.
@ashwkun ashwkun added user-impact-high Listeners clearly notice this change — prioritize README and notification backend-change Touches backend/proxy/infra — pairable with any user-impact level labels Nov 1, 2026
@ashwkun
ashwkun merged commit 7201ba8 into master Nov 1, 2026
12 of 13 checks passed
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Caution

Review failed

The pull request is closed.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@ashwkun
ashwkun deleted the support-sending branch November 1, 2026 22:04
@sonarqubecloud

sonarqubecloud Bot commented Nov 1, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 New issue

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend-change Touches backend/proxy/infra — pairable with any user-impact level user-impact-high Listeners clearly notice this change — prioritize README and notification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant