Name waiting apps, align card actions, and update Angor icon
Demo images / Build & push demo images (push) Failing after 39s
Demo images / Build & push demo images (push) Failing after 39s
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Named the app in compact readiness messages and kept app-card actions aligned at the bottom.
|
||||
- Removed duplicate Mempool cards caused by frontend container aliases in restored inventory.
|
||||
|
||||
- Kept installed apps visible through restarts and hard refreshes, and delayed app launches until their web interface is ready.
|
||||
- Made Bitcoin version selection readable and usable in the ThinkPad kiosk, above the pruning settings.
|
||||
- Restored GitWorkshop build files in installation/update payloads and made slow image-pull progress clearer.
|
||||
|
||||
@@ -658,7 +658,7 @@
|
||||
],
|
||||
"category": "money",
|
||||
"tier": "optional",
|
||||
"icon": "/assets/img/app-icons/angor.svg",
|
||||
"icon": "/assets/img/app-icons/angor-green.png",
|
||||
"repoUrl": "https://github.com/block-core/angor"
|
||||
},
|
||||
{
|
||||
@@ -671,7 +671,7 @@
|
||||
"requires": [],
|
||||
"category": "nostr",
|
||||
"tier": "optional",
|
||||
"icon": "/assets/img/app-icons/angor.svg",
|
||||
"icon": "/assets/img/app-icons/angor-green.png",
|
||||
"repoUrl": "https://github.com/hoytech/strfry"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -46,7 +46,7 @@ Mempool recreation does not require editing IP addresses or restarting this app.
|
||||
No app-specific Rust installer is required.
|
||||
|
||||
Source documentation: [Angor's official deployment guide](https://github.com/block-core/angor/blob/869dd43cf38332dd7128a284a6bf4c1cac44c1a7/docker/DEPLOY-INDEXER-AND-RELAY.md).
|
||||
The unmodified icon comes from [angor.io/images/logo-text.svg](https://angor.io/images/logo-text.svg), retrieved 2026-09-30.
|
||||
The app icon is based on [Angor’s dark-mode app icon](https://angor.io/images/app-icon-dark-mode.png), retrieved 2026-09-30. At the operator’s request, the outer corners use the same green as the background. The built-in imagegen edit preserved the black mark and filled the square green; the project asset is `neode-ui/public/assets/img/app-icons/angor-green.png`.
|
||||
|
||||
Tests and release acceptance are recorded in the next-release checklist. The
|
||||
health probe establishes backend availability, not a guarantee that every
|
||||
|
||||
@@ -58,7 +58,7 @@ app:
|
||||
sync_required: true
|
||||
pruning_support: false
|
||||
metadata:
|
||||
icon: /assets/img/app-icons/angor.svg
|
||||
icon: /assets/img/app-icons/angor-green.png
|
||||
tier: optional
|
||||
repo: https://github.com/block-core/angor
|
||||
features:
|
||||
|
||||
@@ -214,7 +214,7 @@ app:
|
||||
protocol: http
|
||||
path: /
|
||||
metadata:
|
||||
icon: /assets/img/app-icons/angor.svg
|
||||
icon: /assets/img/app-icons/angor-green.png
|
||||
tier: optional
|
||||
repo: https://github.com/hoytech/strfry
|
||||
features:
|
||||
|
||||
@@ -120,3 +120,25 @@ smart-HTTP response type, current branch ref and Compose contents. Its
|
||||
slirp4netns route and all production app containers survived. The temporary
|
||||
Portainer fixture was absent. This verifies the repaired production route
|
||||
across reboot; it does not substitute for final new-runtime delivery checks.
|
||||
|
||||
## Follow-up acceptance: app cards and Angor icon
|
||||
|
||||
- Mempool duplicate traced to `archy-mempool-web` durable inventory alias being
|
||||
restored beside the real `mempool` frontend. Shared scanner canonicalization
|
||||
fixes live and absent-container paths without deleting installed markers.
|
||||
Frontend suppresses aliases only while a canonical tile exists.
|
||||
- Readiness text names the app: “Waiting for Gitea…”. It shares the status row,
|
||||
with full text available through its title; card actions use bottom alignment.
|
||||
- Angor uses the operator-supplied dark-mode icon with green outer corners.
|
||||
Built-in imagegen prompt: fill transparent/white corners with the existing
|
||||
flat green, preserve the black symbol, square opaque PNG, no added details.
|
||||
- Backend alias suite: 1608 passed, 4 ignored. Focused readiness/frame UI tests:
|
||||
30 passed. Production UI build passed and is live on dev. Browser checks at
|
||||
1440 and 1024 pixels verified named waiting text, bottom-aligned actions,
|
||||
equal row heights, no overflow and one Mempool card after hard refresh.
|
||||
The 390-pixel mobile icon layout also passed hard refresh. Final-source
|
||||
isolated Mempool alias regression passed after the scanner simplification.
|
||||
- Managed Angor adapter acceptance: five stop/start/restart cycles, missing
|
||||
prerequisite refusal, management restart and cleanup all passed. Both temporary
|
||||
fixtures and their network were removed. Actual dev API verification remains
|
||||
pending after removing an incomplete legacy-created adapter.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 948 KiB |
@@ -658,7 +658,7 @@
|
||||
],
|
||||
"category": "money",
|
||||
"tier": "optional",
|
||||
"icon": "/assets/img/app-icons/angor.svg",
|
||||
"icon": "/assets/img/app-icons/angor-green.png",
|
||||
"repoUrl": "https://github.com/block-core/angor"
|
||||
},
|
||||
{
|
||||
@@ -671,7 +671,7 @@
|
||||
"requires": [],
|
||||
"category": "nostr",
|
||||
"tier": "optional",
|
||||
"icon": "/assets/img/app-icons/angor.svg",
|
||||
"icon": "/assets/img/app-icons/angor-green.png",
|
||||
"repoUrl": "https://github.com/hoytech/strfry"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:data-controller-launch="canLaunch(pkg) ? '' : undefined"
|
||||
tabindex="0"
|
||||
role="link"
|
||||
class="glass-card p-6 transition-all hover:-translate-y-1 cursor-pointer relative min-w-0 overflow-hidden"
|
||||
class="glass-card flex flex-col h-full p-6 transition-all hover:-translate-y-1 cursor-pointer relative min-w-0 overflow-hidden"
|
||||
:class="{ 'card-stagger': showStagger }"
|
||||
:style="{ '--stagger-index': index }"
|
||||
@click="$emit('goToApp', id)"
|
||||
@@ -56,9 +56,9 @@
|
||||
{{ description }}
|
||||
</p>
|
||||
|
||||
<div v-if="!isInstalling && !isUninstalling && pkg.state !== 'installing'" class="flex items-center gap-2">
|
||||
<div v-if="!isInstalling && !isUninstalling && pkg.state !== 'installing'" class="flex items-center gap-2 min-w-0">
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium"
|
||||
class="shrink-0 inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium"
|
||||
:class="getStatusClass(pkg.state, pkg.health, pkg['exit-code'])"
|
||||
>
|
||||
<svg
|
||||
@@ -74,14 +74,14 @@
|
||||
<span v-if="pkg.state === 'running' && pkg.health === 'unhealthy'" class="w-1.5 h-1.5 rounded-full bg-orange-400 animate-pulse"></span>
|
||||
{{ getStatusLabel(pkg.state, pkg.health, pkg['exit-code']) }}
|
||||
</span>
|
||||
<p v-if="blockedReason" :title="blockedReason" class="min-w-0 truncate text-xs leading-snug text-yellow-200/80">
|
||||
{{ blockedReason }}
|
||||
</p>
|
||||
</div>
|
||||
<p v-if="blockedReason" class="mt-2 text-xs leading-snug text-yellow-200/80">
|
||||
{{ blockedReason }}
|
||||
</p>
|
||||
|
||||
<!-- Quick Actions — icon buttons in uniform dark containers -->
|
||||
<!-- Installing progress — replaces action buttons -->
|
||||
<div v-if="isInstalling || pkg.state === 'installing'" class="mt-4">
|
||||
<div v-if="isInstalling || pkg.state === 'installing'" class="mt-auto pt-4">
|
||||
<div class="flex items-center justify-between mb-1.5">
|
||||
<span class="text-xs text-white/70 flex items-center gap-1.5">
|
||||
<svg class="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
|
||||
@@ -101,7 +101,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Uninstalling progress — truthful stage-driven bar (mirrors install) -->
|
||||
<div v-else-if="isUninstalling" class="mt-4">
|
||||
<div v-else-if="isUninstalling" class="mt-auto pt-4">
|
||||
<div class="flex items-center justify-between mb-1.5">
|
||||
<span class="text-xs text-white/70 flex items-center gap-1.5">
|
||||
<svg class="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
|
||||
@@ -120,7 +120,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="mt-4 flex gap-2">
|
||||
<div v-else class="mt-auto pt-4 flex gap-2">
|
||||
<!-- Update available -->
|
||||
<button
|
||||
v-if="pkg['available-update'] && pkg.state !== 'updating'"
|
||||
|
||||
@@ -167,7 +167,7 @@ describe('appsConfig service filtering', () => {
|
||||
pkg.health = null
|
||||
expect(isAppReadyForLaunch(pkg)).toBe(false)
|
||||
expect(canLaunch(pkg)).toBe(false)
|
||||
expect(launchBlockedReason(pkg.manifest.id, pkg)).toContain('Starting up')
|
||||
expect(launchBlockedReason(pkg.manifest.id, pkg)).toBe('Waiting for GitWorkshop…')
|
||||
pkg.health = 'healthy'
|
||||
expect(canLaunch(pkg)).toBe(true)
|
||||
})
|
||||
@@ -202,7 +202,7 @@ describe('HTTP readiness independent of container health', () => {
|
||||
pkg.health = 'healthy'
|
||||
expect(canLaunch(pkg)).toBe(false)
|
||||
expect(isAppReadyForLaunch(pkg)).toBe(false)
|
||||
expect(launchBlockedReason(id, pkg)).toContain('Waiting')
|
||||
expect(launchBlockedReason(id, pkg)).toBe(`Waiting for ${resolveAppTitle(id, pkg)}…`)
|
||||
pkg['ui-ready'] = true
|
||||
expect(isAppReadyForLaunch(pkg)).toBe(true)
|
||||
}
|
||||
@@ -224,3 +224,13 @@ describe('manifest-generated service names', () => {
|
||||
expect(resolveAppTitle('unknown-service', makePkg('unknown-service', 'unknown-service', ''))).toBe('unknown-service')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not display a stale Mempool frontend alias beside its live package', () => {
|
||||
const main = makePkg('mempool', 'Mempool', 'money')
|
||||
const alias = makePkg('mempool-web', 'Mempool', 'money')
|
||||
alias.state = PackageState.Restarting
|
||||
const entries: Array<[string, PackageDataEntry]> = [['mempool', main], ['mempool-web', alias]]
|
||||
const shown = [...filterEntriesForTab(entries, 'apps', 'all'), ...filterEntriesForTab(entries, 'services', 'all')]
|
||||
expect(shown.map(([id]) => id)).toEqual(['mempool'])
|
||||
expect(filterEntriesForTab([['mempool-web', alias]], 'apps', 'all').map(([id]) => id)).toEqual(['mempool-web'])
|
||||
})
|
||||
|
||||
@@ -111,7 +111,12 @@ export function filterEntriesForTab(
|
||||
activeTab: AppsTab,
|
||||
selectedCategory: string,
|
||||
): Array<[string, PackageDataEntry]> {
|
||||
const hasMempool = entries.some(([id]) => id === 'mempool')
|
||||
return entries.filter(([id, pkg]) => {
|
||||
// Older daemons can retain the frontend manifest alias during a restart.
|
||||
// Keep one tile while the updated scanner converges; a legacy-only node
|
||||
// must still be able to see and operate its sole Mempool entry.
|
||||
if (hasMempool && ['mempool-web', 'mempool-frontend', 'archy-mempool-web'].includes(id)) return false
|
||||
if (isInternalToolingPackage(id, pkg)) return false
|
||||
const wantsWebsites = activeTab === 'websites' || activeTab === 'services'
|
||||
const isWebsite = isWebsitePackage(id, pkg)
|
||||
@@ -231,6 +236,7 @@ function serviceParentIcon(id: string): string | undefined {
|
||||
export const DEFAULT_APP_ICON = '/assets/icon/favico-black-v2.svg'
|
||||
|
||||
export function resolveAppIcon(id: string, pkg: PackageDataEntry, curatedIcon?: string): string {
|
||||
if (id === 'angor-indexer' || id === 'angor-relay') return '/assets/img/app-icons/angor-green.png'
|
||||
const rawIcon = (pkg["static-files"]?.icon || "").trim()
|
||||
const icon = rawIcon === '/assets/img/favico.png' ? '' : rawIcon
|
||||
if (
|
||||
@@ -296,9 +302,10 @@ export function isAppReadyForLaunch(pkg: PackageDataEntry): boolean {
|
||||
|
||||
export function launchBlockedReason(id: string, pkg?: PackageDataEntry | null): string {
|
||||
const appId = pkg?.manifest?.id || id
|
||||
const title = pkg ? resolveAppTitle(appId, pkg) : id
|
||||
if (pkg?.['ui-ready'] === false && !isServicePackage(appId, pkg)) {
|
||||
if (pkg.state === PackageState.Stopped || pkg.state === PackageState.Exited) return 'App is stopped. Start it to open it.'
|
||||
return 'Waiting for the app to be ready…'
|
||||
return `Waiting for ${title}…`
|
||||
}
|
||||
if (
|
||||
(appId === 'fedimint' || appId === 'fedimintd') &&
|
||||
@@ -307,7 +314,7 @@ export function launchBlockedReason(id: string, pkg?: PackageDataEntry | null):
|
||||
return 'Guardian opens a wait page until Bitcoin finishes initial sync.'
|
||||
}
|
||||
if (pkg && pkg.state === PackageState.Running && !isAppReadyForLaunch(pkg)) {
|
||||
return 'Starting up — Launch will appear when the app is ready.'
|
||||
return `Waiting for ${title}…`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user