fix(help): sync omit inactive account freeze fields

This commit is contained in:
iamxvbaba 2026-07-27 20:54:04 +08:00
parent 5401c9a364
commit 5807fbad76
2 changed files with 15 additions and 29 deletions

View file

@ -135,8 +135,10 @@ func defaultAppConfigHashFor(mapboxToken string) int {
} }
// GetAppConfig returns the cached global app config plus an authenticated, // GetAppConfig returns the cached global app config plus an authenticated,
// per-account freeze overlay. The overlay owns its own deterministic hash so a // per-account freeze overlay. Only active freezes add account fields; an
// FROZEN_METHOD_INVALID-triggered refresh can never be answered notModified. // inactive account receives the field-free base config. The overlay owns its
// own deterministic hash so a FROZEN_METHOD_INVALID-triggered refresh and a
// later unfreeze can never be answered notModified against the other state.
func (s *Service) GetAppConfig(ctx context.Context, userID int64, hash int) (domain.AppConfig, bool, error) { func (s *Service) GetAppConfig(ctx context.Context, userID int64, hash int) (domain.AppConfig, bool, error) {
cfg := s.loadAppConfig(ctx) cfg := s.loadAppConfig(ctx)
var err error var err error
@ -160,15 +162,6 @@ func (s *Service) accountAppConfig(ctx context.Context, userID int64, base domai
} }
} }
if userID > 0 { if userID > 0 {
// DrKLO applies only keys present in the new JSON object and retains old
// SharedPreferences values for missing keys. Authenticated non-frozen
// accounts therefore need an explicit zero/empty triplet to converge after
// an unfreeze; merely omitting the overlay works in TDesktop but leaves
// Android frozen indefinitely. Unauthenticated config remains unscoped.
values["freeze_since_date"] = json.RawMessage("0")
values["freeze_until_date"] = json.RawMessage("0")
values["freeze_appeal_url"] = json.RawMessage(`""`)
changed = true
if s != nil && s.accountFreeze != nil { if s != nil && s.accountFreeze != nil {
freeze, found, err := s.accountFreeze.AccountFreeze(ctx, userID) freeze, found, err := s.accountFreeze.AccountFreeze(ctx, userID)
if err != nil { if err != nil {
@ -179,6 +172,7 @@ func (s *Service) accountAppConfig(ctx context.Context, userID int64, base domai
values["freeze_until_date"] = json.RawMessage(strconv.FormatInt(freeze.Until.Unix(), 10)) values["freeze_until_date"] = json.RawMessage(strconv.FormatInt(freeze.Until.Unix(), 10))
appeal, _ := json.Marshal(freeze.AppealURL) appeal, _ := json.Marshal(freeze.AppealURL)
values["freeze_appeal_url"] = appeal values["freeze_appeal_url"] = appeal
changed = true
} }
} }
} }

View file

@ -46,7 +46,7 @@ func TestAccountAppConfigFreezeOverlayIsUserScopedAndHashAware(t *testing.T) {
if err != nil || notModified || other.Hash != normal.Hash { if err != nil || notModified || other.Hash != normal.Hash {
t.Fatalf("other user = hash:%d notModified:%v err:%v", other.Hash, notModified, err) t.Fatalf("other user = hash:%d notModified:%v err:%v", other.Hash, notModified, err)
} }
assertClearedFreezeConfig(t, other.JSON) assertNoFreezeConfig(t, other.JSON)
unauthorized, _, err := svc.GetAppConfig(context.Background(), 0, 0) unauthorized, _, err := svc.GetAppConfig(context.Background(), 0, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -58,21 +58,24 @@ func TestAccountAppConfigFreezeOverlayIsUserScopedAndHashAware(t *testing.T) {
if err != nil || notModified || unfrozen.Hash != normal.Hash { if err != nil || notModified || unfrozen.Hash != normal.Hash {
t.Fatalf("unfreeze refresh = hash:%d notModified:%v err:%v", unfrozen.Hash, notModified, err) t.Fatalf("unfreeze refresh = hash:%d notModified:%v err:%v", unfrozen.Hash, notModified, err)
} }
assertClearedFreezeConfig(t, unfrozen.JSON) assertNoFreezeConfig(t, unfrozen.JSON)
} }
func TestAuthenticatedAppConfigClearsPersistedFreezeWithoutProvider(t *testing.T) { func TestAuthenticatedNonFrozenAppConfigOmitsFreezeFieldsAndReusesBaseHash(t *testing.T) {
svc := NewService(nil, nil) svc := NewService(nil, nil)
unauthorized, _, err := svc.GetAppConfig(context.Background(), 0, 0) unauthorized, _, err := svc.GetAppConfig(context.Background(), 0, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assertNoFreezeConfig(t, unauthorized.JSON) assertNoFreezeConfig(t, unauthorized.JSON)
authenticated, notModified, err := svc.GetAppConfig(context.Background(), 1001, unauthorized.Hash) authenticated, notModified, err := svc.GetAppConfig(context.Background(), 1001, 0)
if err != nil || notModified || authenticated.Hash == unauthorized.Hash { if err != nil || notModified || authenticated.Hash != unauthorized.Hash {
t.Fatalf("authenticated clear config = hash:%d base:%d notModified:%v err:%v", authenticated.Hash, unauthorized.Hash, notModified, err) t.Fatalf("authenticated config = hash:%d base:%d notModified:%v err:%v", authenticated.Hash, unauthorized.Hash, notModified, err)
}
assertNoFreezeConfig(t, authenticated.JSON)
if _, notModified, err := svc.GetAppConfig(context.Background(), 1001, authenticated.Hash); err != nil || !notModified {
t.Fatalf("authenticated hash replay = notModified:%v err:%v", notModified, err)
} }
assertClearedFreezeConfig(t, authenticated.JSON)
} }
func TestAccountAppConfigStripsGlobalFreezeFields(t *testing.T) { func TestAccountAppConfigStripsGlobalFreezeFields(t *testing.T) {
@ -112,17 +115,6 @@ func assertNoFreezeConfig(t *testing.T, body []byte) {
} }
} }
func assertClearedFreezeConfig(t *testing.T, body []byte) {
t.Helper()
var values map[string]any
if err := json.Unmarshal(body, &values); err != nil {
t.Fatal(err)
}
if values["freeze_since_date"] != float64(0) || values["freeze_until_date"] != float64(0) || values["freeze_appeal_url"] != "" {
t.Fatalf("freeze clear config = %#v", values)
}
}
type fakeAccountFreezeProvider struct { type fakeAccountFreezeProvider struct {
items map[int64]domain.AccountFreeze items map[int64]domain.AccountFreeze
} }