@@ -17,6 +17,7 @@ import { useDisplayStore } from '~/stores/display'
1717type ManifestEntry = Database [' public' ][' Tables' ][' manifest' ][' Row' ]
1818
1919type VersionRow = Pick <Database [' public' ][' Tables' ][' app_versions' ][' Row' ], ' id' | ' name' | ' created_at' | ' manifest_count' | ' app_id' >
20+ type DeployHistoryRow = Pick <Database [' public' ][' Tables' ][' deploy_history' ][' Row' ], ' channel_id' | ' version_id' | ' created_at' | ' deployed_at' >
2021
2122const route = useRoute (' /app/[package].bundle.[bundle].manifest' )
2223const router = useRouter ()
@@ -31,6 +32,7 @@ const compareSearchLoading = ref(false)
3132const version = ref <Database [' public' ][' Tables' ][' app_versions' ][' Row' ]>()
3233const manifestEntries = ref <ManifestEntry []>([])
3334const latestCompareVersions = ref <VersionRow []>([])
35+ const preferredCompareVersions = ref <VersionRow []>([])
3436const compareSearchResults = ref <VersionRow []>([])
3537const compareSearch = ref (' ' )
3638const compareVersionId = ref <number | null >(null )
@@ -42,6 +44,7 @@ const currentPage = ref(1)
4244const MANIFEST_PAGE_SIZE = 1000
4345const compareRequestId = ref (0 )
4446const compareSearchRequestId = ref (0 )
47+ let preferredCompareRequestId = 0
4548
4649function hideHash(hash : string ) {
4750 if (! hash )
@@ -92,6 +95,7 @@ const compareVersion = computed(() => {
9295 return null
9396 return (
9497 compareSearchResults .value .find (v => v .id === compareVersionId .value )
98+ ?? preferredCompareVersions .value .find (v => v .id === compareVersionId .value )
9599 ?? latestCompareVersions .value .find (v => v .id === compareVersionId .value )
96100 ?? null
97101 )
@@ -100,7 +104,11 @@ const compareVersion = computed(() => {
100104const compareOptions = computed (() => {
101105 if (compareSearch .value .trim ())
102106 return compareSearchResults .value
103- return latestCompareVersions .value
107+ const preferredIds = new Set (preferredCompareVersions .value .map (version => version .id ))
108+ return [
109+ ... preferredCompareVersions .value ,
110+ ... latestCompareVersions .value .filter (version => ! preferredIds .has (version .id )),
111+ ]
104112})
105113
106114const diffEntries = computed (() => {
@@ -253,11 +261,126 @@ async function loadLatestCompareVersions() {
253261 latestCompareVersions .value = data ?? []
254262}
255263
264+ async function loadPreferredCompareVersions() {
265+ const requestId = ++ preferredCompareRequestId
266+ preferredCompareVersions .value = []
267+ if (! packageId .value || ! id .value )
268+ return
269+
270+ const channelIds = new Set <number >()
271+ const deployedAtByChannel = new Map <number , string | null >()
272+
273+ const { data : currentChannels, error : currentChannelsError } = await supabase
274+ .from (' channels' )
275+ .select (' id' )
276+ .eq (' app_id' , packageId .value )
277+ .eq (' version' , id .value )
278+
279+ if (requestId !== preferredCompareRequestId )
280+ return
281+
282+ if (currentChannelsError ) {
283+ console .error (' Failed to load current channels' , currentChannelsError )
284+ }
285+ else {
286+ for (const channel of currentChannels ?? [])
287+ channelIds .add (channel .id )
288+ }
289+
290+ const { data : deployHistory, error : deployHistoryError } = await supabase
291+ .from (' deploy_history' )
292+ .select (' channel_id, version_id, created_at, deployed_at' )
293+ .eq (' app_id' , packageId .value )
294+ .eq (' version_id' , id .value )
295+ .order (' created_at' , { ascending: false })
296+
297+ if (requestId !== preferredCompareRequestId )
298+ return
299+
300+ if (deployHistoryError ) {
301+ console .error (' Failed to load deploy history for bundle' , deployHistoryError )
302+ }
303+ else {
304+ for (const entry of deployHistory ?? []) {
305+ const entryTime = entry .created_at ?? entry .deployed_at ?? null
306+ if (! channelIds .has (entry .channel_id ))
307+ channelIds .add (entry .channel_id )
308+ if (! deployedAtByChannel .has (entry .channel_id ))
309+ deployedAtByChannel .set (entry .channel_id , entryTime )
310+ }
311+ }
312+
313+ if (channelIds .size === 0 )
314+ return
315+
316+ const preferredHistory: Array <{ versionId: number , deployedAt: string | null }> = []
317+ for (const channelId of channelIds ) {
318+ const cutoff = deployedAtByChannel .get (channelId )
319+ let query = supabase
320+ .from (' deploy_history' )
321+ .select (' version_id, created_at, deployed_at' )
322+ .eq (' app_id' , packageId .value )
323+ .eq (' channel_id' , channelId )
324+ .neq (' version_id' , id .value )
325+
326+ if (cutoff )
327+ query = query .lt (' created_at' , cutoff )
328+
329+ const { data, error } = await query
330+ .order (' created_at' , { ascending: false })
331+ .limit (1 )
332+
333+ if (requestId !== preferredCompareRequestId )
334+ return
335+
336+ if (error ) {
337+ console .error (' Failed to load previous deploy history' , error )
338+ continue
339+ }
340+
341+ const entry = (data ?? [])[0 ] as DeployHistoryRow | undefined
342+ if (! entry )
343+ continue
344+ preferredHistory .push ({
345+ versionId: entry .version_id ,
346+ deployedAt: entry .created_at ?? entry .deployed_at ?? null ,
347+ })
348+ }
349+
350+ if (! preferredHistory .length )
351+ return
352+
353+ const uniqueIds = [... new Set (preferredHistory .map (entry => entry .versionId ))]
354+ const { data : versions, error } = await supabase
355+ .from (' app_versions' )
356+ .select (' id, name, created_at, manifest_count, app_id' )
357+ .eq (' app_id' , packageId .value )
358+ .gt (' manifest_count' , 0 )
359+ .in (' id' , uniqueIds )
360+
361+ if (requestId !== preferredCompareRequestId )
362+ return
363+
364+ if (error ) {
365+ console .error (' Failed to load preferred compare versions' , error )
366+ return
367+ }
368+
369+ const versionMap = new Map ((versions ?? []).map (version => [version .id , version ]))
370+ const sorted = preferredHistory
371+ .filter (entry => versionMap .has (entry .versionId ))
372+ .sort ((a , b ) => (b .deployedAt ?? ' ' ).localeCompare (a .deployedAt ?? ' ' ))
373+
374+ preferredCompareVersions .value = sorted
375+ .map (entry => versionMap .get (entry .versionId ))
376+ .filter ((version ): version is VersionRow => Boolean (version ))
377+ }
378+
256379async function reloadManifest() {
257380 if (! id .value )
258381 return
259382 tableLoading .value = true
260- await Promise .all ([loadManifest (), loadLatestCompareVersions ()])
383+ await Promise .all ([loadManifest (), loadLatestCompareVersions (), loadPreferredCompareVersions () ])
261384 if (compareVersionId .value ) {
262385 const cached = compareManifestCache .value [compareVersionId .value ]
263386 compareManifestEntries .value = cached ?? await fetchManifestEntries (compareVersionId .value )
@@ -370,7 +493,7 @@ watchEffect(async () => {
370493 packageId .value = route .params .package as string
371494 id .value = Number (route .params .bundle as string )
372495 resetCompareSelection ()
373- await Promise .all ([getVersion (), loadManifest (), loadLatestCompareVersions ()])
496+ await Promise .all ([getVersion (), loadManifest (), loadLatestCompareVersions (), loadPreferredCompareVersions () ])
374497 loading .value = false
375498 if (! version .value ?.name )
376499 displayStore .NavTitle = t (' bundle' )
@@ -411,10 +534,10 @@ watchEffect(async () => {
411534 <div class =" w-full d-dropdown" >
412535 <button
413536 tabindex =" 0"
414- class =" inline-flex w-full items-center justify-between rounded-lg border border-slate-300 bg-white px-3 py-2 text-left text-sm text-slate-700 shadow-sm transition hover:border-slate-400 dark:border-slate-600 dark:bg-slate-900 dark:text-slate-200"
537+ class =" inline-flex w-full min-w-0 items-center justify-between rounded-lg border border-slate-300 bg-white px-3 py-2 text-left text-sm text-slate-700 shadow-sm transition hover:border-slate-400 dark:border-slate-600 dark:bg-slate-900 dark:text-slate-200"
415538 :disabled =" loading"
416539 >
417- <span class =" truncate" >
540+ <span class =" truncate min-w-0 " >
418541 {{ compareVersion?.name ?? t('manifest-compare-none') }}
419542 </span >
420543 <IconDown class =" w-4 h-4 shrink-0 text-slate-400" />
@@ -446,11 +569,11 @@ watchEffect(async () => {
446569 v-for =" option in compareOptions"
447570 :key =" option.id"
448571 type =" button"
449- class =" flex w-full items-center justify-between rounded-md px-3 py-2 text-sm hover:bg-slate-100 dark:hover:bg-slate-800"
572+ class =" flex w-full min-w-0 items-center justify-between rounded-md px-3 py-2 text-sm hover:bg-slate-100 dark:hover:bg-slate-800"
450573 @click =" selectCompareVersion(option)"
451574 >
452- <span class =" truncate" >{{ option.name }}</span >
453- <span class =" ml-2 text-xs text-slate-400" >{{ option.created_at ? formatLocalDate(option.created_at) : t('unknown') }}</span >
575+ <span class =" truncate min-w-0 " >{{ option.name }}</span >
576+ <span class =" ml-2 shrink-0 text-xs text-slate-400" >{{ option.created_at ? formatLocalDate(option.created_at) : t('unknown') }}</span >
454577 </button >
455578 <div v-if =" compareSearchLoading" class =" px-3 py-2 text-xs text-slate-400" >
456579 {{ t('loading') }}
0 commit comments