@@ -22,7 +22,7 @@ import {
2222} from '../data/jwxt/jwxtApi' ;
2323import { digestToMd5LikeHex } from '../data/termState/digest' ;
2424import { courseCatalogMap , courseDataset } from '../data/catalog/courseCatalog' ;
25- import { getGistFileContent , syncGist } from '../data/github/gistSync' ;
25+ import { findLatestGistId , getGistFileContent , syncGist } from '../data/github/gistSync' ;
2626import { assertNever } from '../data/termState/types' ;
2727import { deriveGroupKey } from '../data/termState/groupKey' ;
2828 import { collapseCoursesByName } from './courseDisplaySettings' ;
@@ -190,6 +190,7 @@ export function clearTermStateAlert() {
190190}
191191
192192const GIST_BUNDLE_FILENAME = 'term-state.json' ;
193+ const GIST_BUNDLE_DESCRIPTION = 'NeoSHUSchedulingHelper TermState Bundle' ;
193194const GistBundleSchema = z . object ( {
194195 updatedAt : z . number ( ) ,
195196 payloadBase64 : z . string ( ) . min ( 1 )
@@ -1241,11 +1242,21 @@ async function runEffect(effect: TermEffect) {
12411242 try {
12421243 const updatedAt = Date . now ( ) ;
12431244 const payload = JSON . stringify ( { updatedAt, payloadBase64 : effect . payloadBase64 } satisfies z . infer < typeof GistBundleSchema > ) ;
1245+
1246+ // Auto-discover the existing cloud backup gist (prevents creating many same-name gists).
1247+ let gistId : string | undefined = effect . gistId ?? undefined ;
1248+ if ( ! gistId ) {
1249+ gistId = ( await findLatestGistId ( {
1250+ token : effect . token ,
1251+ filename : GIST_BUNDLE_FILENAME ,
1252+ descriptionIncludes : GIST_BUNDLE_DESCRIPTION
1253+ } ) ) ?? undefined ;
1254+ }
12441255 const result = await syncGist ( {
12451256 token : effect . token ,
1246- gistId : effect . gistId ,
1257+ gistId : gistId ?? undefined ,
12471258 public : false ,
1248- description : 'SHU Course Scheduler TermState Bundle' ,
1259+ description : GIST_BUNDLE_DESCRIPTION ,
12491260 files : {
12501261 [ GIST_BUNDLE_FILENAME ] : payload
12511262 }
@@ -1261,9 +1272,19 @@ async function runEffect(effect: TermEffect) {
12611272 }
12621273 case 'EFF_GIST_GET' : {
12631274 try {
1275+ // Auto-discover cloud backup gist if not pinned locally yet.
1276+ let gistId : string | undefined = effect . gistId ?? undefined ;
1277+ if ( ! gistId ) {
1278+ gistId = ( await findLatestGistId ( {
1279+ token : effect . token ,
1280+ filename : GIST_BUNDLE_FILENAME ,
1281+ descriptionIncludes : GIST_BUNDLE_DESCRIPTION
1282+ } ) ) ?? undefined ;
1283+ }
1284+ if ( ! gistId ) throw new Error ( 'No cloud backup yet' ) ;
12641285 const file = await getGistFileContent ( {
12651286 token : effect . token ,
1266- gistId : effect . gistId ,
1287+ gistId,
12671288 filename : GIST_BUNDLE_FILENAME
12681289 } ) ;
12691290 const raw = JSON . parse ( file . content . trim ( ) ) as unknown ;
@@ -1278,14 +1299,15 @@ async function runEffect(effect: TermEffect) {
12781299
12791300 await dispatchTermAction ( {
12801301 type : 'SYNC_GIST_IMPORT_OK' ,
1281- gistId : effect . gistId ,
1302+ gistId,
12821303 state : bundle . termState ,
12831304 generatedAt : bundle . generatedAt
12841305 } ) ;
12851306 } catch ( error ) {
1307+ const fallbackGistId = effect . gistId ?? 'auto' ;
12861308 await dispatchTermAction ( {
12871309 type : 'SYNC_GIST_IMPORT_ERR' ,
1288- gistId : effect . gistId ,
1310+ gistId : fallbackGistId ,
12891311 error : error instanceof Error ? error . message : String ( error )
12901312 } ) ;
12911313 }
0 commit comments