Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions InterlinedList/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public MainWindow()

// Feed/search cards raise this to open a user's profile in the People tab.
Navigator.OnOpenProfile = OpenProfile;
// Account deletion (Settings) routes back to the login screen through here.
Navigator.OnLoggedOut = () => LoggedOut?.Invoke(this, EventArgs.Empty);

StartClock();

Expand Down
5 changes: 5 additions & 0 deletions InterlinedList/Services/Navigator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ public static class Navigator
{
public static Action<string>? OnOpenProfile { get; set; }

/// <summary>Set by the shell; lets a center view (e.g. account deletion) return the app to the login screen.</summary>
public static Action? OnLoggedOut { get; set; }

public static void OpenProfile(string username)
{
if (!string.IsNullOrWhiteSpace(username))
OnOpenProfile?.Invoke(username);
}

public static void RequestLogout() => OnLoggedOut?.Invoke();
}
33 changes: 33 additions & 0 deletions InterlinedList/ViewModels/SettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty]
private string newEmail = "";

// Account deletion requires typing your exact username as a guard.
[ObservableProperty]
private string deleteConfirmUsername = "";

public SettingsViewModel(SessionService session)
{
_session = session;
Expand Down Expand Up @@ -234,6 +238,35 @@ private async Task UnmuteAsync(ModeratedUser u)
[RelayCommand]
private Task ExportFollowsAsync() => ExportCsvAsync(_session.Api.ExportFollowsCsvAsync, "follows.csv");

// Destructive. Enabled only when the typed username matches exactly. On
// success the token is cleared and the shell returns to the login screen.
private bool CanDeleteAccount() =>
_session.CurrentUser is { } u &&
string.Equals(DeleteConfirmUsername.Trim(), u.Username, StringComparison.Ordinal);

[RelayCommand(CanExecute = nameof(CanDeleteAccount))]
private async Task DeleteAccountAsync()
{
if (_session.CurrentUser is not { } user) return;
IsBusy = true;
try
{
await _session.Api.DeleteAccountAsync(user.Username, user.Email);
_session.Logout();
Navigator.RequestLogout();
}
catch (InterlinedApiException ex)
{
ErrorMessage = ex.Message;
}
finally
{
IsBusy = false;
}
}

partial void OnDeleteConfirmUsernameChanged(string value) => DeleteAccountCommand.NotifyCanExecuteChanged();

private async Task ExportCsvAsync(Func<CancellationToken, Task<string>> fetch, string defaultFileName)
{
try
Expand Down
27 changes: 27 additions & 0 deletions InterlinedList/Views/SettingsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,33 @@
</StackPanel>
</Border>

<!-- ============ Danger zone ============ -->
<Border Background="{DynamicResource SurfaceBrush}"
BorderBrush="#66E81123"
BorderThickness="1"
CornerRadius="4"
Padding="16"
Margin="0,0,0,16">
<StackPanel>
<TextBlock Text="Danger zone" Style="{StaticResource SectionTitleStyle}"/>
<TextBlock Text="Delete your account permanently. This cannot be undone. Type your username to confirm."
Style="{StaticResource SubtleTextStyle}"
Margin="0,4,0,12"/>
<TextBox Text="{Binding DeleteConfirmUsername, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource FieldStyle}" Margin="0,0,0,10"/>
<Button Content="Delete my account"
Command="{Binding DeleteAccountCommand}"
HorizontalAlignment="Left">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource DigBtnStyle}">
<Setter Property="BorderBrush" Value="#FFE81123"/>
<Setter Property="Foreground" Value="#FFE81123"/>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Border>

</StackPanel>
</ScrollViewer>
</Grid>
Expand Down
37 changes: 37 additions & 0 deletions the-gaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,43 @@ per-list schema/columns, a full standalone notifications view.

---

## Progress — Session 7 (2026-08-01) — account deletion + parity assessment

- ✅ **Account deletion** — a guarded "Danger zone" in Settings (type your exact
username to enable), calling `POST /api/user/delete {username,email}`, then
clearing the token and returning the shell to the login screen (via a new
`Navigator.OnLoggedOut` hook). Builds clean Debug + Release.

### ✅ Effective feature parity reached
Every core, verifiable, user-facing product surface is now built (feed +
media + replies + scheduling, DMs with media/polling, People + full follow
graph + moderation, Settings incl. sessions/exports/account, Lists + rows +
folders + sharing + watchers, Documents + folders + sharing + collaborators,
Organizations + members, Search, Connected Accounts, auth self-service).

**The only remaining gaps are genuinely blocked, not merely unbuilt:**
- **Materialize** ("Create from…") — the API request is a single opaque
`source` string with no documented structure; can't be built reliably without
more API detail. *API-blocked.*
- **Per-list schema/columns DSL** (`PUT /api/lists/{id}/schema`) — only partially
reverse-engineered; schema-less rows are the confirmed-working path (see
CLAUDE.md). *API-blocked.*
- **GitHub issue sync** — the test account has no GitHub linked (every
`/api/github/*` call returns "GitHub account not linked") and the wire shapes
aren't documented, so it can't be verified. *Verification-blocked* — build it
once an account with GitHub linked is available.
- **DM inbox-folder view** — `GET /api/dm` item shape can't be learned without
sending real DMs to a real person; DMs already work via the recipients list.
*Low value / verification-blocked.*
- **Billing UI** — Stripe endpoints are cookie-session-only; handled by the
"Manage account on the web" handoff. *Auth-model-blocked (by design.)*

To close the verification-blocked items, provision **(a)** a second test account
(two-sided DM/follow/moderation/sharing checks) and **(b)** GitHub linked on a
test account. Everything else is either shipped or API-limited.

---

## 1. Parity snapshot by domain

| Domain (product's name) | Web/API has | App has today | Status |
Expand Down
Loading