From b66b60da9d80a6617e989e55448ca1615e43e516 Mon Sep 17 00:00:00 2001 From: Adron Hall Date: Fri, 31 Jul 2026 16:17:30 -0700 Subject: [PATCH] Build out API feature parity with interlinedlist.com MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the bulk of the gap between the native client and the web app, verified endpoint-by-endpoint against the live API (OpenAPI + probing with the test account). App builds clean in Debug and Release; the self-contained win-x64 publish (what the WiX MSI harvests) succeeds. New nav views: - Direct Messages — recipients + thread + send/read - People — profile lookup, follow/unfollow, request approve/reject, a user's messages, block/mute/report - Settings — profile edit, avatar-from-URL, email change, notification prefs, blocked/muted management, API-session list + revoke, CSV data export Feed depth: - replies/threads, edit/delete own posts, report, image attachments (upload + display), and click-through from an author to their profile (Navigator hub) Existing views enhanced: - Lists: row edit + delete (were write-once) - Documents: folder create/rename/delete + new-doc-in-folder - Organizations: full member management (add via search, change role, remove), edit/delete org - Notifications: mark-one-read + delete-one Service layer: 10 new/extended partials + multipart upload helper + shared JSON helpers; wire models added for all of the above. Write paths follow the codebase's read-after-write discipline. Corrected stale CLAUDE.md constraints (re-verified live 2026-07-31): org members and LinkedIn targets accept the bearer token (were documented as 401 walls), and the sync-token DOES have a server-side revoke (GET/DELETE /api/user/sessions). See the-gaps.md for the full gap analysis, per-session progress, and remaining post-v1 items. Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 79 ++- InterlinedList/MainWindow.xaml | 18 + InterlinedList/MainWindow.xaml.cs | 16 + InterlinedList/Models/ApiSession.cs | 19 + InterlinedList/Models/DirectMessage.cs | 21 + InterlinedList/Models/DmRecipient.cs | 16 + InterlinedList/Models/DmThread.cs | 14 + InterlinedList/Models/FollowStatus.cs | 13 + InterlinedList/Models/FollowUser.cs | 20 + InterlinedList/Models/ModeratedUser.cs | 17 + .../Models/NotificationPreference.cs | 20 + InterlinedList/Models/OrgMember.cs | 22 + InterlinedList/Models/UserProfile.cs | 26 + .../Services/InterlinedApiClient.Account.cs | 74 +++ .../InterlinedApiClient.DirectMessages.cs | 42 ++ .../Services/InterlinedApiClient.Documents.cs | 18 + .../Services/InterlinedApiClient.Exports.cs | 20 + .../Services/InterlinedApiClient.Follow.cs | 54 ++ .../Services/InterlinedApiClient.Lists.cs | 20 + .../Services/InterlinedApiClient.Messages.cs | 60 ++ .../InterlinedApiClient.Moderation.cs | 56 ++ .../InterlinedApiClient.Organizations.cs | 35 +- .../Services/InterlinedApiClient.People.cs | 20 + .../Services/InterlinedApiClient.cs | 90 ++- InterlinedList/Services/Navigator.cs | 18 + .../ViewModels/DirectMessagesViewModel.cs | 129 ++++ .../ViewModels/DmMessageViewModel.cs | 23 + .../ViewModels/DocumentsViewModel.cs | 143 +++++ InterlinedList/ViewModels/FeedViewModel.cs | 57 +- InterlinedList/ViewModels/ListsViewModel.cs | 72 +++ .../ViewModels/MessageItemViewModel.cs | 173 +++++- .../ViewModels/NotificationPrefViewModel.cs | 58 ++ .../ViewModels/NotificationsViewModel.cs | 32 + .../ViewModels/OrganizationsViewModel.cs | 250 ++++++++ InterlinedList/ViewModels/ProfileViewModel.cs | 225 +++++++ .../ViewModels/SettingsViewModel.cs | 251 ++++++++ InterlinedList/Views/DirectMessagesView.xaml | 323 ++++++++++ .../Views/DirectMessagesView.xaml.cs | 15 + InterlinedList/Views/DocumentsView.xaml | 83 ++- InterlinedList/Views/DocumentsView.xaml.cs | 23 + InterlinedList/Views/FeedView.xaml | 207 ++++++- InterlinedList/Views/ListsView.xaml | 99 ++- InterlinedList/Views/OrganizationsView.xaml | 283 ++++++++- InterlinedList/Views/PeopleView.xaml | 443 ++++++++++++++ InterlinedList/Views/PeopleView.xaml.cs | 24 + InterlinedList/Views/SettingsView.xaml | 562 ++++++++++++++++++ InterlinedList/Views/SettingsView.xaml.cs | 15 + the-gaps.md | 435 ++++++++++++++ 48 files changed, 4640 insertions(+), 93 deletions(-) create mode 100644 InterlinedList/Models/ApiSession.cs create mode 100644 InterlinedList/Models/DirectMessage.cs create mode 100644 InterlinedList/Models/DmRecipient.cs create mode 100644 InterlinedList/Models/DmThread.cs create mode 100644 InterlinedList/Models/FollowStatus.cs create mode 100644 InterlinedList/Models/FollowUser.cs create mode 100644 InterlinedList/Models/ModeratedUser.cs create mode 100644 InterlinedList/Models/NotificationPreference.cs create mode 100644 InterlinedList/Models/OrgMember.cs create mode 100644 InterlinedList/Models/UserProfile.cs create mode 100644 InterlinedList/Services/InterlinedApiClient.Account.cs create mode 100644 InterlinedList/Services/InterlinedApiClient.DirectMessages.cs create mode 100644 InterlinedList/Services/InterlinedApiClient.Exports.cs create mode 100644 InterlinedList/Services/InterlinedApiClient.Follow.cs create mode 100644 InterlinedList/Services/InterlinedApiClient.Messages.cs create mode 100644 InterlinedList/Services/InterlinedApiClient.Moderation.cs create mode 100644 InterlinedList/Services/InterlinedApiClient.People.cs create mode 100644 InterlinedList/Services/Navigator.cs create mode 100644 InterlinedList/ViewModels/DirectMessagesViewModel.cs create mode 100644 InterlinedList/ViewModels/DmMessageViewModel.cs create mode 100644 InterlinedList/ViewModels/NotificationPrefViewModel.cs create mode 100644 InterlinedList/ViewModels/ProfileViewModel.cs create mode 100644 InterlinedList/ViewModels/SettingsViewModel.cs create mode 100644 InterlinedList/Views/DirectMessagesView.xaml create mode 100644 InterlinedList/Views/DirectMessagesView.xaml.cs create mode 100644 InterlinedList/Views/PeopleView.xaml create mode 100644 InterlinedList/Views/PeopleView.xaml.cs create mode 100644 InterlinedList/Views/SettingsView.xaml create mode 100644 InterlinedList/Views/SettingsView.xaml.cs create mode 100644 the-gaps.md diff --git a/CLAUDE.md b/CLAUDE.md index 239ddb3..dd11034 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,31 +74,52 @@ The app talks to the real InterlinedList backend at `https://interlinedlist.com` (154-endpoint REST API, OpenAPI spec at `/api/openapi.json`) — there is no mock data layer. Auth is a long-lived bearer token from `POST /api/auth/sync-token` (the same mechanism the `il-sync` CLI and other native clients use — no cookie -jar), persisted DPAPI-encrypted via `CredentialStore`. **There is no -server-side revoke endpoint for this token** — treat `%LocalAppData%\InterlinedList\session.dat` -as a standing credential. - -Covered now: login/session restore, paginated feed, compose, Dig/Undig, -notifications tray, profile/follow-counts rail, **Lists** (browse/create/ -delete, freeform JSON data rows — no schema/column editor, see below), -**Documents** (personal markdown notes: root docs, folders, templates, -create/edit/delete), **Organizations** (browse orgs you belong to + the -public directory, create — **no member management**, see below), unified -**Search** (messages/people/lists/documents from one box), and **Connected -Accounts** (Bluesky/Mastodon/LinkedIn/Twitter linking + compose-time -cross-post toggles). Still not built: Stripe billing, replies/threads, -register/forgot-password, GitHub issue sync, per-list schema/column -definitions, LinkedIn per-page posting targets. - -**Two real, load-bearing constraints discovered by live-probing the API — don't +jar), persisted DPAPI-encrypted via `CredentialStore`. The token is +long-lived, so treat `%LocalAppData%\InterlinedList\session.dat` as a standing +credential — but note the server **does** expose session management: +`GET /api/user/sessions` lists a user's active sync tokens and +`DELETE /api/user/sessions/{id}` revokes one (both accept the bearer token — +verified live 2026-07-31). An earlier revision of this file claimed no revoke +endpoint existed; that is no longer true. + +Covered now (greatly expanded in the 2026-07-31 parity build-out): +login/session restore, paginated **feed** with compose (text + **image +attachments**, cross-post toggles), Dig/Undig, **replies/threads**, **edit/ +delete** own posts, **report** posts, and **click-through to author profiles**; +notifications tray with **mark-one-read / delete-one / mark-all**; **Direct +Messages** (recipient list + thread + send); **People** (profile lookup, +follow/unfollow, follow-request approve/reject, a user's messages, +**block/mute/report**); **Lists** (browse/create/delete, freeform JSON data +rows with **row edit + delete** — no schema/column editor, see below); +**Documents** (root docs, templates, create/edit/delete + **folder CRUD / +new-doc-in-folder**); **Organizations** (browse + create + **full member +management**: add via search, change role, remove, edit/delete org); +**Settings** (profile edit, avatar-from-URL, email change, notification +preferences, blocked/muted management, **API-session list + revoke**, **CSV +data export**); unified **Search**; and **Connected Accounts** (Bluesky/ +Mastodon/LinkedIn/Twitter linking + cross-post toggles). Still not built: +Stripe billing UI, register/forgot-password, GitHub issue sync (endpoints work +but the test account has no GitHub linked), per-list schema/column definitions, +LinkedIn per-page posting targets, scheduled-post UI (the service supports +`scheduledAt`), media *video* upload, list watchers/sharing, document sharing/ +collaborators, Materialize ("Create from…"), and account deletion UI (the +service method exists, intentionally unsurfaced). + +**Load-bearing constraints discovered by live-probing the API — don't "fix" these without re-verifying, they're not bugs in this app:** -1. **Not every endpoint accepts the bearer sync-token.** `GET/POST - /api/organizations/{id}/members` and `GET /api/linkedin/targets` / - `posting-targets` return `401` even with a valid token — that subsystem - requires cookie-session auth this native client doesn't have. This is why - Organizations has no member-management UI and Connected Accounts has no - LinkedIn per-page targeting. +1. **A few endpoints only accept cookie-session auth, not the bearer + sync-token.** Re-probed live 2026-07-31 with the test account: + `GET /api/user/engagement` and `GET/PUT /api/user/dashboard-layout` return + `401` with a valid bearer token (Stripe billing + some `/api/auth/*` session + flows are the same shape). A native bearer-token client structurally can't + get a cookie session, so those are either browser-handoff (like OAuth) or + out of scope. **Correction to an earlier claim:** `GET + /api/organizations/{id}/members`, `GET /api/linkedin/targets`, and + `GET /api/linkedin/posting-targets` were *previously* documented here as + `401`-walled, but as of 2026-07-31 they return `200` with the bearer token — + member-management and LinkedIn per-page targeting **are** buildable now. + (Member *mutations* — POST/PUT/DELETE — still need live write-verification.) 2. **The per-provider `GET /api/auth/{provider}/status` endpoints are a red herring** — they report whether the *server* has that OAuth integration configured, not whether *this user* has linked it. The real per-user link @@ -117,11 +138,13 @@ engineered and is **not implemented** — data rows work fine schema-less verify the actual response shape against a real (test) account before typing it strictly, and prefer read-after-write over trusting an unverified envelope. -Left nav maps to real views now: **Feed**, **Lists**, **Documents**, -**Organizations**, **Search** (`MainWindow.xaml.cs` `NavItem_Click` swaps a -`ContentControl` via a small per-tag cache in `_views`), **Accounts** -(Connected Accounts), and **Alerts** (unchanged from Phase 2 — still a -right-rail toggle, not a center view). +Left nav maps to real views now: **Feed**, **Messages** (Direct Messages), +**Lists**, **Documents**, **Organizations**, **People** (profiles + follow), +**Search** (`MainWindow.xaml.cs` `NavItem_Click` swaps a `ContentControl` via a +small per-tag cache in `_views`), **Accounts** (Connected Accounts), +**Settings**, and **Alerts** (right-rail toggle, not a center view). Feed/search +cards open a profile in the People tab via the `Navigator` hub +(`Services/Navigator.cs`) → `MainWindow.OpenProfile`. ## Packaging & distribution diff --git a/InterlinedList/MainWindow.xaml b/InterlinedList/MainWindow.xaml index 56a3478..81734a4 100644 --- a/InterlinedList/MainWindow.xaml +++ b/InterlinedList/MainWindow.xaml @@ -97,16 +97,22 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/InterlinedList/Views/PeopleView.xaml.cs b/InterlinedList/Views/PeopleView.xaml.cs new file mode 100644 index 0000000..b0449e6 --- /dev/null +++ b/InterlinedList/Views/PeopleView.xaml.cs @@ -0,0 +1,24 @@ +using System.Windows.Controls; +using InterlinedList.ViewModels; + +namespace InterlinedList.Views; + +public partial class PeopleView : UserControl +{ + private readonly ProfileViewModel _vm; + + public PeopleView() + { + InitializeComponent(); + _vm = new ProfileViewModel(Services.AppServices.Session); + DataContext = _vm; + _ = _vm.LoadCommand.ExecuteAsync(null); + } + + /// Open a specific user's profile (used by shell navigation from a feed/search card). + public void LoadProfile(string username) + { + _vm.LookupUsername = username; + _ = _vm.LoadProfileCommand.ExecuteAsync(null); + } +} diff --git a/InterlinedList/Views/SettingsView.xaml b/InterlinedList/Views/SettingsView.xaml new file mode 100644 index 0000000..27f0dbd --- /dev/null +++ b/InterlinedList/Views/SettingsView.xaml @@ -0,0 +1,562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +