Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7694251
feat(profile): show supporter status as an animated halo and wings
Chardot Oct 8, 2026
a2f6e20
feat(profile): hide supporter halo and wings while the avatar lightbo…
Chardot Oct 8, 2026
c75407c
feat(profile): open a supporter view from other supporters' avatars
Chardot Oct 8, 2026
ed31d24
feat(profile): show the supporter halo for public supporters
Chardot Oct 13, 2026
eb7589d
fix(profile): stop the supporter halo flickering after the lightbox c…
Chardot Oct 15, 2026
581aaf3
feat(profile): replace the supporter label with the own supporter view
Chardot Oct 19, 2026
fcc163a
feat(profile): add the supporter visibility toggle to edit profile
Chardot Oct 19, 2026
f965989
feat(l10n): translate the supporter view into every locale
Chardot Oct 19, 2026
1fe762d
fix(profile): keep supporter name honorifics on the same line
dcadenas Oct 22, 2026
36c7993
docs(profile): drop the public setting from the own supporter view's doc
hm21 Oct 22, 2026
238facc
refactor(profile): share one route between the avatar lightboxes
hm21 Oct 22, 2026
923f538
test(profile): pin who the supporter view invites to join
hm21 Oct 22, 2026
ac388c4
fix(profile): keep the space after a mid-sentence supporter name
hm21 Oct 22, 2026
93e1f4b
fix(l10n): use standalone supporter titles outside the headline
dcadenas Oct 22, 2026
28818df
fix(l10n): keep the Telugu supporter title free of subtitle text
dcadenas Oct 22, 2026
841659d
fix(profile): use the light-mode accent for the supporter badge text
realmeylisdev Oct 22, 2026
9e2cc44
fix(profile): stop leaking curve listeners from the supporter view
realmeylisdev Oct 22, 2026
3aeeefc
fix(profile): skip the membership read for signed-out viewers
realmeylisdev Oct 22, 2026
1e5f0e4
fix(profile): scale the supporter view's avatar group on narrow screens
realmeylisdev Oct 22, 2026
763e242
fix(profile): hold the supporter halo level when motion is off
realmeylisdev Oct 22, 2026
f7f5a8a
test(supporter): read membership labels from l10n instead of English
realmeylisdev Oct 22, 2026
335bef2
refactor(supporter): look up optional supporter cubits by nullable type
realmeylisdev Oct 22, 2026
9cf21b3
refactor(supporter): keep the failure wording beside the other l10n maps
realmeylisdev Oct 22, 2026
420d81e
refactor(profile): share the supporter view's icon badge
realmeylisdev Oct 22, 2026
a6c0ecd
fix(profile): drop the empty gap the hidden supporter setting left
realmeylisdev Oct 22, 2026
eca651d
perf(profile): fetch supporter status alongside the profile, once
realmeylisdev Oct 22, 2026
e819101
fix(supporter): keep the profile header when a remote signer gets ready
realmeylisdev Oct 22, 2026
04e8dc5
fix(profile): trust public support after cached membership expiry
dcadenas Oct 22, 2026
d73c6dd
Revert "fix(profile): skip the membership read for signed-out viewers"
hm21 Oct 22, 2026
e03393e
Revert "fix(profile): drop the empty gap the hidden supporter setting…
hm21 Oct 22, 2026
606bab4
test(l10n): drop the test that pins one Telugu string
hm21 Oct 22, 2026
c4359ec
test(profile): cover vanished profiles with active supporter status
dcadenas Oct 23, 2026
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
Binary file added mobile/assets/supporter/halo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mobile/assets/supporter/heart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mobile/assets/supporter/sparkle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mobile/assets/supporter/wing_left.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mobile/assets/supporter/wing_right.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions mobile/lib/blocs/supporter/supporter_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class SupporterCubit extends Cubit<SupporterState> {
Future<void> setPublicRecognition(bool visible) async {
final snapshot = state.snapshot;
if (snapshot == null || state.savingRecognition) return;
_emit(state.copyWith(savingRecognition: true, clearFailure: true));
_emit(state.copyWith(requestedHaloVisible: visible, clearFailure: true));
try {
await _repository.updateRecognition(
haloVisible: visible,
Expand All @@ -97,12 +97,12 @@ class SupporterCubit extends Cubit<SupporterState> {
_emit(
state.copyWith(
snapshot: _repository.snapshot,
savingRecognition: false,
clearRequestedHaloVisible: true,
),
);
} on SupporterApiException catch (error) {
_emitApiFailure(error);
_emit(state.copyWith(savingRecognition: false));
_emit(state.copyWith(clearRequestedHaloVisible: true));
} on Object catch (error, stackTrace) {
// A failure outside the typed contract, such as a signer error, must
// still end the save, or the recognition toggle stays disabled.
Expand All @@ -114,7 +114,7 @@ class SupporterCubit extends Cubit<SupporterState> {
state.copyWith(
status: SupporterStatus.error,
failure: SupporterFailure.unknown,
savingRecognition: false,
clearRequestedHaloVisible: true,
),
);
}
Expand Down
21 changes: 16 additions & 5 deletions mobile/lib/blocs/supporter/supporter_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ enum SupporterFailure {
class SupporterState extends Equatable {
const SupporterState({
this.snapshot,
this.savingRecognition = false,
this.requestedHaloVisible,
this.tiers = const [],
this.entitlement = SupporterEntitlement.inactive,
this.status = SupporterStatus.idle,
Expand All @@ -51,7 +51,10 @@ class SupporterState extends Equatable {
});

final SupporterAccountSnapshot? snapshot;
final bool savingRecognition;

/// The public recognition being saved, shown until the save settles so the
/// setting flips as soon as it is changed. Null when nothing is saving.
final bool? requestedHaloVisible;

/// Purchasable supporter tiers loaded from the store.
final List<SupporterTier> tiers;
Expand All @@ -66,6 +69,11 @@ class SupporterState extends Equatable {
final bool awaitingPurchaseConfirmation;

bool get isSupporter => entitlement.isSupporter;
bool get savingRecognition => requestedHaloVisible != null;

/// Whether supporter status shows publicly, including a change still saving.
bool get haloVisible =>
requestedHaloVisible ?? snapshot?.haloVisible ?? false;
bool get isConfirmedInactive =>
snapshot?.status == SupporterServerStatus.expired;
bool get isBusy =>
Expand All @@ -78,7 +86,8 @@ class SupporterState extends Equatable {

SupporterState copyWith({
SupporterAccountSnapshot? snapshot,
bool? savingRecognition,
bool? requestedHaloVisible,
bool clearRequestedHaloVisible = false,
List<SupporterTier>? tiers,
SupporterEntitlement? entitlement,
SupporterStatus? status,
Expand All @@ -88,7 +97,9 @@ class SupporterState extends Equatable {
}) {
return SupporterState(
snapshot: snapshot ?? this.snapshot,
savingRecognition: savingRecognition ?? this.savingRecognition,
requestedHaloVisible: clearRequestedHaloVisible
? null
: requestedHaloVisible ?? this.requestedHaloVisible,
awaitingPurchaseConfirmation:
awaitingPurchaseConfirmation ?? this.awaitingPurchaseConfirmation,
tiers: tiers ?? this.tiers,
Expand All @@ -101,7 +112,7 @@ class SupporterState extends Equatable {
@override
List<Object?> get props => [
snapshot,
savingRecognition,
requestedHaloVisible,
tiers,
entitlement,
status,
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_am.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,27 @@
}
}
},
"supporterLightboxHeadline": "{name} {badge} ናቸው",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "የDivine ደጋፊ",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "የበለጠ ክፍት እና ፈጠራ የተሞላ ኢንተርኔት እንዲኖር ራሱን የቻለውን መተግበሪያ ይደግፋሉ።",
"supporterLightboxBenefitIndependent": "Divine ራሱን ችሎ እንዲቆይ ይረዳል",
"supporterLightboxBenefitFeatures": "አዳዲስ አስደሳች ባህሪያትን ይደግፋል",
"supporterLightboxBenefitCommunity": "የበለጠ ፈጠራ ያለው ማህበረሰብን ያጎለብታል",
"supporterLightboxMaybeLater": "ምናልባት በኋላ",
"supporterTitle": "የDivine ደጋፊዎች",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_ar.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3398,6 +3398,27 @@
}
}
},
"supporterLightboxHeadline": "{name} من {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "داعمي Divine",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "دعمٌ للتطبيق المستقل من أجل إنترنت أكثر انفتاحًا وإبداعًا.",
"supporterLightboxBenefitIndependent": "يساعد على بقاء Divine مستقلًا",
"supporterLightboxBenefitFeatures": "يدعم ميزات جديدة ومثيرة",
"supporterLightboxBenefitCommunity": "يغذّي مجتمعًا أكثر إبداعًا",
"supporterLightboxMaybeLater": "ربما لاحقًا",
"supporterTitle": "داعمو Divine",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_bg.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3515,6 +3515,27 @@
}
}
},
"supporterLightboxHeadline": "{name} е от {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "поддръжниците на Divine",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Подкрепя независимото приложение за по-отворен и креативен интернет.",
"supporterLightboxBenefitIndependent": "Помага Divine да остане независим",
"supporterLightboxBenefitFeatures": "Подкрепя нови вълнуващи функции",
"supporterLightboxBenefitCommunity": "Захранва по-креативна общност",
"supporterLightboxMaybeLater": "Може би по-късно",
"supporterTitle": "Поддръжници на Divine",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3405,6 +3405,27 @@
}
}
},
"supporterLightboxHeadline": "{name} ist {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "Divine-Supporter",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Unterstützt die unabhängige App für ein offeneres, kreativeres Internet.",
"supporterLightboxBenefitIndependent": "Hält Divine unabhängig",
"supporterLightboxBenefitFeatures": "Unterstützt spannende neue Funktionen",
"supporterLightboxBenefitCommunity": "Stärkt eine kreativere Community",
"supporterLightboxMaybeLater": "Vielleicht später",
"supporterTitle": "Divine-Supporter",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
14 changes: 14 additions & 0 deletions mobile/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -9163,6 +9163,20 @@
"supporterRecognitionDisclaimer": "Divine confirms supporter status after the store verifies your purchase. Public recognition is optional. A Supporter badge is not identity verification.",
"supporterBadgeLabel": "Supporter",
"supporterJoinLabel": "Become a supporter",
"supporterLightboxHeadline": "{name} is a {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": { "name": { "type": "String" }, "badge": { "type": "String" } }
},
"supporterLightboxBadge": "Divine Supporter",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Supporting the independent app for a more open, creative internet.",
"supporterLightboxBenefitIndependent": "Helps keep Divine independent",
"supporterLightboxBenefitFeatures": "Supports new exciting features",
"supporterLightboxBenefitCommunity": "Powers a more creative community",
"supporterLightboxMaybeLater": "Maybe later",
"supporterPublicRecognition": "Show my Supporter badge publicly",
"supporterPublicRecognitionBody": "Let others see that you support Divine. This is optional and is not a verification badge.",
"supporterTitle": "Divine Supporters",
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3409,6 +3409,27 @@
}
}
},
"supporterLightboxHeadline": "{name} es {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "Supporter de Divine",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Apoya a la app independiente por un internet más abierto y creativo.",
"supporterLightboxBenefitIndependent": "Ayuda a que Divine siga independiente",
"supporterLightboxBenefitFeatures": "Impulsa funciones nuevas y geniales",
"supporterLightboxBenefitCommunity": "Da vida a una comunidad más creativa",
"supporterLightboxMaybeLater": "Quizás más tarde",
"supporterTitle": "Supporters de Divine",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_fil.arb
Original file line number Diff line number Diff line change
Expand Up @@ -2649,6 +2649,27 @@
}
}
},
"supporterLightboxHeadline": "Isang {badge} si {name}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "Divine Supporter",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Sinusuportahan ang independent na app para sa mas bukas at malikhaing internet.",
"supporterLightboxBenefitIndependent": "Tumutulong panatilihing independent ang Divine",
"supporterLightboxBenefitFeatures": "Sumusuporta sa mga bagong exciting na feature",
"supporterLightboxBenefitCommunity": "Nagpapalakas ng mas malikhaing community",
"supporterLightboxMaybeLater": "Sa susunod na lang",
"supporterTitle": "Mga Divine Supporter",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3405,6 +3405,27 @@
}
}
},
"supporterLightboxHeadline": "{name} est {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "Supporter Divine",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Soutient l'app indépendante pour un internet plus ouvert et créatif.",
"supporterLightboxBenefitIndependent": "Aide Divine à rester indépendant",
"supporterLightboxBenefitFeatures": "Soutient de nouvelles fonctionnalités géniales",
"supporterLightboxBenefitCommunity": "Fait vivre une communauté plus créative",
"supporterLightboxMaybeLater": "Peut-être plus tard",
"supporterTitle": "Supporters Divine",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_id.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3405,6 +3405,27 @@
}
}
},
"supporterLightboxHeadline": "{name} adalah {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "Pendukung Divine",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Mendukung aplikasi independen untuk internet yang lebih terbuka dan kreatif.",
"supporterLightboxBenefitIndependent": "Membantu Divine tetap independen",
"supporterLightboxBenefitFeatures": "Mendukung fitur-fitur baru yang seru",
"supporterLightboxBenefitCommunity": "Menghidupkan komunitas yang lebih kreatif",
"supporterLightboxMaybeLater": "Nanti saja",
"supporterTitle": "Pendukung Divine",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_it.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3405,6 +3405,27 @@
}
}
},
"supporterLightboxHeadline": "{name} è tra i {badge}",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "Sostenitori Divine",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "Sostiene l'app indipendente per un internet più aperto e creativo.",
"supporterLightboxBenefitIndependent": "Aiuta Divine a restare indipendente",
"supporterLightboxBenefitFeatures": "Sostiene nuove funzioni entusiasmanti",
"supporterLightboxBenefitCommunity": "Alimenta una community più creativa",
"supporterLightboxMaybeLater": "Forse più tardi",
"supporterTitle": "Sostenitori Divine",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
21 changes: 21 additions & 0 deletions mobile/lib/l10n/app_ja.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3398,6 +3398,27 @@
}
}
},
"supporterLightboxHeadline": "{name}さんは{badge}です",
"@supporterLightboxHeadline": {
"description": "Headline of another user's supporter lightbox. {name} is their display name; {badge} is supporterLightboxBadge, shown in the accent color. Keep both placeholders; reorder freely.",
"placeholders": {
"name": {
"type": "String"
},
"badge": {
"type": "String"
}
}
},
"supporterLightboxBadge": "Divine サポーター",
"@supporterLightboxBadge": {
"description": "Highlighted status name inserted into supporterLightboxHeadline."
},
"supporterLightboxBody": "もっとオープンでクリエイティブなインターネットのために、独立したアプリを応援しています。",
"supporterLightboxBenefitIndependent": "Divine の独立を支えます",
"supporterLightboxBenefitFeatures": "ワクワクする新機能を後押しします",
"supporterLightboxBenefitCommunity": "よりクリエイティブなコミュニティを育てます",
"supporterLightboxMaybeLater": "あとで",
"supporterTitle": "Divine サポーター",
"@supporterTitle": {
"description": "Title of the supporter subscription settings screen and its settings tile."
Expand Down
Loading
Loading