arrow_backRetour aux issues
ansvisor/ansvisor
#759
Débutant
Ouvrirarrow_forward
Débutant
Ouvrirarrow_forward
Débutant
Ouvrirarrow_forward
feat: let a brand set its own logo URL, and fall back to the site's favicon when Google has none
ecoDébutant
enhancement
good first issue
help wanted
descriptionDescription
### Problem / motivation
A brand's icon comes from exactly one place. [`web/src/lib/favicon.ts`](https://github.com/ansvisor/ansvisor/blob/main/web/src/lib/favicon.ts) builds a URL pointing at Google's `s2/favicons` service, and that URL is stored as `brands.logo_url`. When Google has no icon for the domain, the brand has no icon — and there is nothing the user can do about it.
This is not rare — two brands added on the same day both landed on it. Both of those sites publish a perfectly good icon of their own; one serves a 180×180 `apple-touch-icon.png` with the actual logo, and Google simply hasn't indexed it. DuckDuckGo's equivalent service returns `404` for the same two domains, so swapping providers is not the answer either.
The second half of the problem is that the value is **derived on every save**. In [`brands/[id]/settings/page.tsx`](https://github.com/ansvisor/ansvisor/blob/main/web/src/app/%5Blocale%5D/(dashboard)/dashboard/brands/%5Bid%5D/settings/page.tsx):
```ts
const newLogoUrl = newPrimary ? getFaviconUrl(newPrimary.domain) : null;
```
So even correcting the row by hand is temporary — changing the primary domain recomputes it and the correction is gone.
### Proposed solution
Two parts. They are one issue because they touch the same value and the same call sites, but they can land in either order.
**1. Let a brand carry its own logo URL**
Add a logo URL field to brand settings. A plain URL text input — **no file upload and no storage bucket**, deliberately. `brands.logo_url` already exists and already holds a URL, so there is no migration.
When the field has a value, it is used and it **sticks**: saving domains must not overwrite it. The derived favicon is only a default for when the field is empty.
**2. Fall back to the site's own favicon when Google has nothing**
When the Google URL fails to load, try `https:///favicon.ico` before giving up and showing the initials.
Do this **at render time**, not by storing a second URL. The fallback is presentational; keeping it out of the database means `logo_url` continues to mean "the icon this brand chose" and no backfill is ever needed.
The natural shape is one small component — something like `BrandAvatar` — that walks the chain *manual URL → Google favicon → site `/favicon.ico` → initials* and is used everywhere a brand avatar appears today:
- [`components/layout/brand-switcher.tsx`](https://github.com/ansvisor/ansvisor/blob/main/web/src/components/layout/brand-switcher.tsx) (two places)
- [`components/dashboard/brand-card.tsx`](https://github.com/ansvisor/ansvisor/blob/main/web/src/components/dashboard/brand-card.tsx)
- [`components/providers/brand-guard.tsx`](https://github.com/ansvisor/ansvisor/blob/main/web/src/components/providers/brand-guard.tsx)
- [`brands/[id]/layout.tsx`](https://github.com/ansvisor/ansvisor/blob/main/web/src/app/%5Blocale%5D/(dashboard)/dashboard/brands/%5Bid%5D/layout.tsx)
**Notes**
- Radix's `AvatarImage` already reports a failed load, which is the hook the chain needs — you should not have to fetch anything yourself.
- Google answers a miss with **`404` carrying a valid PNG body** (the grey globe). Browsers will often render that rather than firing an error, so treating "image loaded" as success is not enough on its own. Check what actually happens for a known-missing domain before assuming which event fires.
- Don't validate the pasted URL by fetching it server-side. If it doesn't load, the chain already ends at the initials.
- `team-section.tsx` also uses `AvatarImage`, for member avatars. Leave it alone — different data, different fallback.
### Alternatives considered
**Upload the logo to storage.** The most robust option and explicitly not wanted: it drags in a bucket, size and format validation, and cleanup. A URL field solves the same problem with a text input.
**Fetch and cache the icon ourselves at brand creation.** Same objection, plus it makes brand creation depend on a third-party fetch succeeding.
Issues similaires
lingdojo/kana-dojo
star3.2k
Poids du dépôt lourd
[Good First Issue] 🐡 Add new Japan Fact 251 - Beginner-Friendly Open-source Contribution
## 🟢 Good First Issue: Add Japan Fact #251 — Beginner-Friendly Open Source Contribution ![Time: <1 minute](https://img…
TypeScript
enhancement
help wanted
WasiqB/multiple-cucumber-html-reporter
star281
Poids du dépôt moyen
Error bucketing
It would be great to bucket the errors in different types i.e. Code exception, configuration exception, environmental e…
TypeScript
enhancement
help wanted
StellarLock/StellarLock
star0
Poids du dépôt léger
LP lock links omit the /token|lp/ URL segment, causing wrong-type lookups on the detail page
## Summary Lock detail links should include the lock's kind in the URL, e.g. `/app/lock/lp/:id` or `/app/lock/token/:i…
TypeScript
bug
good first issue