@@ -55,14 +55,15 @@ class WrappedService {
5555 static const int _kWrappedYear = 2025 ;
5656 static final DateTime _kAvailabilityStart = DateTime (_kWrappedYear, 12 , 6 );
5757 static final DateTime _kAvailabilityEndExclusive =
58- DateTime (_kWrappedYear + 1 , 1 , 7 );
58+ DateTime (_kWrappedYear + 1 , 1 , 16 );
5959 static final DateTime _kFinalComputeCutoff =
6060 DateTime (_kWrappedYear + 1 , 1 , 1 );
6161
6262 final Logger _logger;
6363 final WrappedCacheService _cacheService;
6464 final Lock _computeLock;
6565 final ValueNotifier <WrappedEntryState > _state;
66+ int _sessionGeneration = 0 ;
6667 bool _initialLoadScheduled = false ;
6768
6869 ValueListenable <WrappedEntryState > get stateListenable => _state;
@@ -86,6 +87,18 @@ class WrappedService {
8687 bool get shouldShowDiscoveryEntry =>
8788 isEnabled && state.hasResult && state.isComplete;
8889
90+ void resetForLogout () {
91+ _logger.info ("Resetting Wrapped state for logout" );
92+ _sessionGeneration++ ;
93+ _initialLoadScheduled = false ;
94+ WrappedMediaPreloader .instance.reset ();
95+ _state.value = const WrappedEntryState (
96+ result: null ,
97+ resumeIndex: 0 ,
98+ isComplete: false ,
99+ );
100+ }
101+
89102 void scheduleInitialLoad () {
90103 if (! isEnabled) {
91104 return ;
@@ -95,15 +108,35 @@ class WrappedService {
95108 }
96109 _initialLoadScheduled = true ;
97110 _logger.info ("Scheduling Wrapped initial load after $_kInitialDelay " );
98- unawaited (Future <void >.delayed (_kInitialDelay, _bootstrap));
111+ final int generation = _sessionGeneration;
112+ unawaited (
113+ Future <void >.delayed (
114+ _kInitialDelay,
115+ () async {
116+ if (generation != _sessionGeneration) {
117+ return ;
118+ }
119+ await _bootstrap ();
120+ },
121+ ),
122+ );
99123 }
100124
101125 Future <void > _bootstrap () async {
126+ final int generation = _sessionGeneration;
102127 try {
103128 final WrappedResult ? cached =
104129 await _cacheService.read (year: _kWrappedYear);
130+ if (generation != _sessionGeneration) {
131+ _logger.info ("Wrapped bootstrap aborted due to session change" );
132+ return ;
133+ }
105134 if (cached != null ) {
106135 await WrappedMediaPreloader .instance.prime (cached);
136+ if (generation != _sessionGeneration) {
137+ _logger.info ("Wrapped bootstrap aborted due to session change" );
138+ return ;
139+ }
107140 _logger.info (
108141 "Loaded Wrapped cache for $_kWrappedYear with "
109142 "${cached .cards .length } cards" ,
@@ -121,6 +154,10 @@ class WrappedService {
121154 _logger.severe ("Failed to load Wrapped cache" , error, stackTrace);
122155 }
123156
157+ if (generation != _sessionGeneration) {
158+ _logger.info ("Wrapped bootstrap aborted due to session change" );
159+ return ;
160+ }
124161 if (! isEnabled) {
125162 _logger.info (
126163 "Wrapped unavailable; skipping initial compute for $_kWrappedYear " ,
@@ -156,6 +193,7 @@ class WrappedService {
156193 required String reason,
157194 required bool bypassFlag,
158195 }) async {
196+ final int generation = _sessionGeneration;
159197 if (! bypassFlag && ! isEnabled) {
160198 _logger.info (
161199 "Wrapped unavailable; skipping compute for $_kWrappedYear ($reason )" ,
@@ -167,8 +205,26 @@ class WrappedService {
167205 try {
168206 final WrappedResult result =
169207 await WrappedEngine .compute (year: _kWrappedYear);
208+ if (generation != _sessionGeneration) {
209+ _logger.info (
210+ "Discarding Wrapped compute result ($reason ) due to session change" ,
211+ );
212+ return ;
213+ }
170214 await WrappedMediaPreloader .instance.prime (result);
215+ if (generation != _sessionGeneration) {
216+ _logger.info (
217+ "Discarding Wrapped compute result ($reason ) due to session change" ,
218+ );
219+ return ;
220+ }
171221 await _cacheService.write (result: result);
222+ if (generation != _sessionGeneration) {
223+ _logger.info (
224+ "Discarding Wrapped compute result ($reason ) due to session change" ,
225+ );
226+ return ;
227+ }
172228 updateResult (result);
173229 _logger.info ("Wrapped compute completed ($reason )" );
174230 } catch (error, stackTrace) {
0 commit comments