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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
**Highlights:** Personal channel order follows your account across browsers and devices.

- Personal sidebar channel order now follows your account across browsers and devices, with the existing local cache retained for offline use. Thanks @isaiahknight-va.
- Preserve saved sidebar channel order, including explicit clears, in SQLite and PostgreSQL JSON exports.
- Signed and notarized macOS server archives with the Foundation Developer ID, alongside the desktop installers, and verify their notarization tickets on a clean runner before publication.
- Allow macOS release automation to select its credential keychain explicitly on headless hosts.
- Updated SQLite, virtual scrolling, Wrangler, and Oxc tooling while preserving runtime minimums and the 48-hour dependency release-age gate.
Expand Down
1 change: 1 addition & 0 deletions apps/api/internal/store/postgres/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func (s *Store) ExportJSON(ctx context.Context, writer io.Writer) error {
out := map[string]any{}
tables := []string{
"users", "user_notification_settings", "channel_notification_settings", "identities", "workspaces", "workspace_members", "channels",
"user_sidebar_channel_order",
"messages", "pinned_messages", "thread_state", "reactions", "events", "event_recipients", "uploads",
"channel_reads", "direct_reads",
"message_attachments", "direct_conversations", "direct_conversation_members",
Expand Down
1 change: 1 addition & 0 deletions apps/api/internal/store/sqlite/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func (s *Store) ExportJSON(ctx context.Context, writer io.Writer) error {
out := map[string]any{}
tables := []string{
"users", "user_notification_settings", "channel_notification_settings", "identities", "workspaces", "workspace_members", "channels",
"user_sidebar_channel_order",
"messages", "pinned_messages", "thread_state", "reactions", "events", "event_recipients", "uploads",
"channel_reads", "direct_reads",
"message_attachments", "direct_conversations", "direct_conversation_members",
Expand Down
36 changes: 35 additions & 1 deletion apps/api/internal/store/storetest/sidebar.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package storetest

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"reflect"
"testing"
"time"
Expand All @@ -12,7 +15,10 @@ import (
)

// SidebarPreferences exercises the same persistence contract on both databases.
func SidebarPreferences(t *testing.T, st store.Store) {
func SidebarPreferences(t *testing.T, st interface {
store.Store
ExportJSON(context.Context, io.Writer) error
}) {
t.Helper()
ctx := context.Background()
suffix := fmt.Sprint(time.Now().UnixNano())
Expand Down Expand Up @@ -81,4 +87,32 @@ func SidebarPreferences(t *testing.T, st store.Store) {
t.Fatal("accepted oversized order")
}
check()
var output bytes.Buffer
if err := st.ExportJSON(ctx, &output); err != nil {
t.Fatal(err)
}
var snapshot struct {
Orders []struct {
UserID string `json:"user_id"`
WorkspaceID string `json:"workspace_id"`
ChannelIDs string `json:"channel_ids"`
} `json:"user_sidebar_channel_order"`
}
if err := json.Unmarshal(output.Bytes(), &snapshot); err != nil {
t.Fatal(err)
}
exported := map[string][]string{}
for _, row := range snapshot.Orders {
if row.UserID != user.ID {
continue
}
var order []string
if err := json.Unmarshal([]byte(row.ChannelIDs), &order); err != nil {
t.Fatal(err)
}
exported[row.WorkspaceID] = order
}
if !reflect.DeepEqual(exported, want) {
t.Fatalf("exported sidebar preferences: %v; want: %v", exported, want)
}
}
3 changes: 3 additions & 0 deletions docs/features/profiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,6 @@ dropped and duplicates keep their first position. Up to 100 workspaces and 500
channel IDs per workspace can be saved per request. Larger local orders retain
their tail on the browser where they were arranged. Appearance and sidebar
preferences update independently.

SQLite and PostgreSQL JSON exports include the saved per-workspace channel order,
including explicit clears, in `user_sidebar_channel_order`.
Loading