diff --git a/internal/app/channels/service_test.go b/internal/app/channels/service_test.go index 5e5d4b6a..03f75789 100644 --- a/internal/app/channels/service_test.go +++ b/internal/app/channels/service_test.go @@ -2854,6 +2854,61 @@ func TestChannelUsernameReservedBlocksNewClaimsButKeepsExisting(t *testing.T) { } } +func TestUpdateUsernameForcesPreHistoryVisible(t *testing.T) { + ctx := context.Background() + const ownerID int64 = 1001 + service := NewService(memory.NewChannelStore()) + created, err := service.CreateMegagroupFromCreateChat(ctx, ownerID, domain.CreateChannelRequest{ + Title: "Private First", + MemberUserIDs: []int64{1002}, + Date: 10, + }) + if err != nil { + t.Fatalf("CreateMegagroupFromCreateChat: %v", err) + } + + hidden, err := service.SetPreHistoryHidden(ctx, ownerID, created.Channel.ID, true) + if err != nil { + t.Fatalf("SetPreHistoryHidden: %v", err) + } + if !hidden.PreHistoryHidden { + t.Fatalf("hidden channel = %+v, want pre-history hidden", hidden) + } + + // Assigning a public username must force pre-history back to visible. + public, err := service.UpdateUsername(ctx, ownerID, domain.UpdateChannelUsernameRequest{ + ChannelID: created.Channel.ID, + Username: "private_first_pub", + }) + if err != nil { + t.Fatalf("UpdateUsername: %v", err) + } + if public.PreHistoryHidden { + t.Fatalf("public channel = %+v, want pre-history visible after publish", public) + } + + // Removing the username leaves the flag alone (still visible). + private, err := service.UpdateUsername(ctx, ownerID, domain.UpdateChannelUsernameRequest{ + ChannelID: created.Channel.ID, + Username: "", + }) + if err != nil { + t.Fatalf("UpdateUsername clear: %v", err) + } + if private.PreHistoryHidden { + t.Fatalf("re-privated channel = %+v, want pre-history still visible", private) + } + + // ...and the creator can hide it again once private. + rehidden, err := service.SetPreHistoryHidden(ctx, ownerID, created.Channel.ID, true) + if err != nil { + t.Fatalf("SetPreHistoryHidden after re-privating: %v", err) + } + if !rehidden.PreHistoryHidden { + t.Fatalf("re-hidden channel = %+v, want pre-history hidden again", rehidden) + } +} + func TestListStoryPostableChannelsFiltersPostStoryRights(t *testing.T) { ctx := context.Background() service := NewService(memory.NewChannelStore()) diff --git a/internal/store/memory/channel_settings.go b/internal/store/memory/channel_settings.go index 961f6bb1..15c32288 100644 --- a/internal/store/memory/channel_settings.go +++ b/internal/store/memory/channel_settings.go @@ -175,6 +175,13 @@ func (s *ChannelStore) UpdateUsername(ctx context.Context, req domain.UpdateChan } prevUsername := channel.Username channel.Username = username + // A public group cannot keep pre-history hidden: assigning a username forces + // "chat history for new members" back to visible (matches the official + // server). Removing the username leaves the flag untouched. + clearedPrehistory := username != "" && channel.PreHistoryHidden + if clearedPrehistory { + channel.PreHistoryHidden = false + } s.channels[req.ChannelID] = channel s.appendChannelAdminLogLocked(domain.ChannelAdminLogEvent{ ChannelID: req.ChannelID, @@ -184,6 +191,16 @@ func (s *ChannelStore) UpdateUsername(ctx context.Context, req domain.UpdateChan PrevString: prevUsername, NewString: username, }) + if clearedPrehistory { + s.appendChannelAdminLogLocked(domain.ChannelAdminLogEvent{ + ChannelID: req.ChannelID, + UserID: req.UserID, + Date: int(time.Now().Unix()), + Type: domain.ChannelAdminLogTogglePreHistoryHidden, + PrevBool: true, + NewBool: false, + }) + } return channel, nil } diff --git a/internal/store/postgres/channel_settings.go b/internal/store/postgres/channel_settings.go index 1cca00ac..098aecc5 100644 --- a/internal/store/postgres/channel_settings.go +++ b/internal/store/postgres/channel_settings.go @@ -241,12 +241,29 @@ func (s *ChannelStore) UpdateUsername(ctx context.Context, req domain.UpdateChan if err := replacePeerUsernameTx(ctx, tx, peerUsernameTypeChannel, req.ChannelID, username, usernameLower); err != nil { return domain.Channel{}, err } - if _, err := tx.Exec(ctx, `UPDATE channels SET username = NULLIF($2,''), updated_at = now() WHERE id = $1`, req.ChannelID, username); err != nil { + // A public group cannot keep pre-history hidden: assigning a username forces + // "chat history for new members" back to visible (matches the official + // server). Removing the username leaves the flag untouched, so the creator + // can hide history again once the group is private. + if _, err := tx.Exec(ctx, `UPDATE channels SET username = NULLIF($2,''), pre_history_hidden = (pre_history_hidden AND $2 = ''), updated_at = now() WHERE id = $1`, req.ChannelID, username); err != nil { return domain.Channel{}, fmt.Errorf("update channel username: %w", err) } if err := markUserChannelMemberIndexPublicTx(ctx, tx, req.ChannelID, username != ""); err != nil { return domain.Channel{}, err } + if username != "" && channel.PreHistoryHidden { + if err := s.insertChannelAdminLogTx(ctx, tx, domain.ChannelAdminLogEvent{ + ChannelID: req.ChannelID, + UserID: req.UserID, + Date: nowUnix(), + Type: domain.ChannelAdminLogTogglePreHistoryHidden, + PrevBool: true, + NewBool: false, + }); err != nil { + return domain.Channel{}, err + } + channel.PreHistoryHidden = false + } prevUsername := channel.Username channel.Username = username if err := s.insertChannelAdminLogTx(ctx, tx, domain.ChannelAdminLogEvent{