@@ -48,6 +48,8 @@ const HOST_GROUP = "flare.hostGroup"; // id only; the key stays in IndexedDB
4848const DRAFT_BUDGET_MS = 6000 ; // the server answers with the template after 6 s (openapi drafts)
4949const CLOUD_WAIT_MS = 40_000 ; // DigitalOcean answers in about 30 s; the server gives up at 35 s (ADR-0022)
5050
51+ const LIVE_KEY = "flare.live" ;
52+
5153export class HostFlow {
5254 screen = $state < HostScreen > ( "camera" ) ;
5355 photoBlob : Blob | null = null ;
@@ -263,6 +265,34 @@ export class HostFlow {
263265 if ( n ) this . spot = { name : n , key : n , lat : this . spot ?. lat ?? 0 , lon : this . spot ?. lon ?? 0 } ;
264266 }
265267
268+ #saved( ) : { id : string ; link : string ; until : number } | null {
269+ try {
270+ const v = JSON . parse ( localStorage . getItem ( LIVE_KEY ) ?? "null" ) as {
271+ id : string ;
272+ link : string ;
273+ until : number ;
274+ } | null ;
275+ return v && typeof v . id === "string" && typeof v . link === "string" && v . until > Date . now ( ) ? v : null ;
276+ } catch {
277+ return null ;
278+ }
279+ }
280+
281+ /** Round 6: this phone sent a Flare that may still be live. */
282+ hasLive ( ) : boolean {
283+ return ! ! this . #saved( ) ;
284+ }
285+
286+ /** Round 6: reopening the app brings back this phone's live Flare and its link. */
287+ resume ( ) : boolean {
288+ const v = this . #saved( ) ;
289+ if ( ! v ) return false ;
290+ this . link = v . link ;
291+ this . screen = "kindling" ;
292+ this . #follow( v . id ) ;
293+ return true ;
294+ }
295+
266296 /** Automations, Again: the last crew and size, no photo; place-word chips or your own words. */
267297 startAgain ( ) : void {
268298 this . photoBlob = null ;
@@ -407,6 +437,10 @@ export class HostFlow {
407437 }
408438 await d . store . rememberGathering ( id , group . groupId ) ;
409439 this . link = link ;
440+ try {
441+ // Round 6: a reload must not lose the live Flare. Same origin as the key store; cleared at Gone.
442+ localStorage . setItem ( LIVE_KEY , JSON . stringify ( { id, link, until : Date . now ( ) + 4 * 3600_000 } ) ) ;
443+ } catch { }
410444 this . view = r . value ?? null ;
411445 if ( this . photoBlob && kind === "flare" ) void this . #uploadPhoto( id , key , this . photoBlob ) ;
412446 // ADR-0023: the real photo goes to Telegram only when the group turned photos on.
@@ -575,7 +609,12 @@ export class HostFlow {
575609 } else if ( v . state === "ember" ) feedback ( "ember" ) ;
576610 else if ( v . state === "gone" ) feedback ( "gone" ) ;
577611 }
578- if ( v . state === "gone" ) void this . #recordHot( v ) ;
612+ if ( v . state === "gone" ) {
613+ void this . #recordHot( v ) ;
614+ try {
615+ localStorage . removeItem ( LIVE_KEY ) ;
616+ } catch { }
617+ }
579618 if ( this . screen !== "sent" || v . state !== "kindling" ) this . #route( ) ;
580619 } ) ;
581620 }
0 commit comments