fix(org): show org logo for favicon and OG image (#713)
When no custom favicon is set, fall back to the organization logo on public org-site pages instead of the default ClassroomIO icon. For OG images, only apply the theme-color background behind the logo when rendering initials; real logo images now render without the tint. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
co-authored by
Cursor Agent
Rotimi Best
parent
3d15bc0a01
commit
22a3e6484b
@@ -146,7 +146,10 @@ function renderOrgSiteOgTemplate(input: OrgSiteOgInput): { body: string; styles:
|
|||||||
const labelColor = rgba(themeColor, 0.72);
|
const labelColor = rgba(themeColor, 0.72);
|
||||||
const initials = orgInitials(orgName);
|
const initials = orgInitials(orgName);
|
||||||
|
|
||||||
const logoMarkup = input.logoUrl
|
const hasLogo = Boolean(input.logoUrl);
|
||||||
|
const logoWrapClass = hasLogo ? 'logo-wrap logo-wrap--image' : 'logo-wrap logo-wrap--initials';
|
||||||
|
|
||||||
|
const logoMarkup = hasLogo
|
||||||
? `<img class="logo-image" src="${escapeHtml(input.logoUrl)}" alt="" />`
|
? `<img class="logo-image" src="${escapeHtml(input.logoUrl)}" alt="" />`
|
||||||
: `<span class="logo-initials" aria-hidden="true">${escapeHtml(initials)}</span>`;
|
: `<span class="logo-initials" aria-hidden="true">${escapeHtml(initials)}</span>`;
|
||||||
|
|
||||||
@@ -159,7 +162,7 @@ function renderOrgSiteOgTemplate(input: OrgSiteOgInput): { body: string; styles:
|
|||||||
const body = `
|
const body = `
|
||||||
<div class="canvas">
|
<div class="canvas">
|
||||||
<div class="decor-circle"></div>
|
<div class="decor-circle"></div>
|
||||||
<div class="logo-wrap">${logoMarkup}</div>
|
<div class="${logoWrapClass}">${logoMarkup}</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p class="academy-label">Academy</p>
|
<p class="academy-label">Academy</p>
|
||||||
<div class="accent-bar"></div>
|
<div class="accent-bar"></div>
|
||||||
@@ -214,17 +217,26 @@ body {
|
|||||||
width: 88px;
|
width: 88px;
|
||||||
height: 88px;
|
height: 88px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background: ${themeColor};
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-wrap--initials {
|
||||||
|
background: ${themeColor};
|
||||||
box-shadow: 0 16px 40px ${rgba(themeColor, 0.28)};
|
box-shadow: 0 16px 40px ${rgba(themeColor, 0.28)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo-wrap--image {
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.logo-image {
|
.logo-image {
|
||||||
width: 56px;
|
width: 88px;
|
||||||
height: 56px;
|
height: 88px;
|
||||||
border-radius: 12px;
|
border-radius: 20px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
<meta name="theme-color" content="#333333" />
|
<meta name="theme-color" content="#333333" />
|
||||||
|
|
||||||
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||||
<link rel="icon" type="image/png" href="/favicon.ico" />
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/logo-32.png" />
|
|
||||||
|
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { AccountOrg } from '$features/app/types';
|
import type { AccountOrg } from '$features/app/types';
|
||||||
|
import { page } from '$app/state';
|
||||||
import { getOrgFaviconHref } from '$lib/utils/functions/org-branding';
|
import { getOrgFaviconHref } from '$lib/utils/functions/org-branding';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -8,11 +9,13 @@
|
|||||||
|
|
||||||
let { org }: Props = $props();
|
let { org }: Props = $props();
|
||||||
|
|
||||||
const faviconHref = $derived(getOrgFaviconHref(org));
|
const faviconHref = $derived(getOrgFaviconHref(org, page.url.origin));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
{#if faviconHref}
|
{#if faviconHref}
|
||||||
<link rel="icon" href={faviconHref} />
|
<link rel="icon" href={faviconHref} />
|
||||||
|
<link rel="shortcut icon" href={faviconHref} />
|
||||||
|
<link rel="apple-touch-icon" href={faviconHref} />
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
import type { AccountOrg } from '$features/app/types';
|
import type { AccountOrg } from '$features/app/types';
|
||||||
|
|
||||||
export function getOrgFaviconHref(org: Pick<AccountOrg, 'favicon' | 'avatarUrl'> | null | undefined): string | null {
|
export function getOrgFaviconHref(
|
||||||
|
org: Pick<AccountOrg, 'favicon' | 'avatarUrl'> | null | undefined,
|
||||||
|
pageOrigin?: string
|
||||||
|
): string | null {
|
||||||
const favicon = org?.favicon?.trim();
|
const favicon = org?.favicon?.trim();
|
||||||
if (favicon) {
|
|
||||||
return favicon;
|
|
||||||
}
|
|
||||||
|
|
||||||
const avatarUrl = org?.avatarUrl?.trim();
|
const avatarUrl = org?.avatarUrl?.trim();
|
||||||
if (avatarUrl) {
|
const rawHref = favicon || avatarUrl;
|
||||||
return avatarUrl;
|
|
||||||
|
if (!rawHref) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
try {
|
||||||
|
return new URL(rawHref, pageOrigin || undefined).href;
|
||||||
|
} catch {
|
||||||
|
return rawHref;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PageRestricted } from '$features/ui';
|
import { PageRestricted } from '$features/ui';
|
||||||
import OrgSiteFavicon from '$features/app/org-site-favicon.svelte';
|
|
||||||
import { currentOrg } from '$lib/utils/store/org';
|
import { currentOrg } from '$lib/utils/store/org';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { afterNavigate } from '$app/navigation';
|
import { afterNavigate } from '$app/navigation';
|
||||||
@@ -41,8 +40,6 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<OrgSiteFavicon org={data.org} />
|
|
||||||
|
|
||||||
{#if data.org?.isRestricted || $currentOrg.isRestricted}
|
{#if data.org?.isRestricted || $currentOrg.isRestricted}
|
||||||
<PageRestricted />
|
<PageRestricted />
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import { MetaTags } from 'svelte-meta-tags';
|
import { MetaTags } from 'svelte-meta-tags';
|
||||||
import { ModeWatcher } from '@cio/ui/base/dark-mode';
|
import { ModeWatcher } from '@cio/ui/base/dark-mode';
|
||||||
|
import OrgSiteFavicon from '$features/app/org-site-favicon.svelte';
|
||||||
|
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
|
|
||||||
@@ -62,6 +63,17 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if data.isOrgSite}
|
||||||
|
<OrgSiteFavicon org={data.org} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
{#if !data.isOrgSite}
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/logo-32.png" />
|
||||||
|
{/if}
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ModeWatcher />
|
<ModeWatcher />
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
import { Empty } from '@cio/ui/custom/empty';
|
import { Empty } from '@cio/ui/custom/empty';
|
||||||
import { SimpleLogoNav } from '@cio/ui/custom/simple-logo-nav';
|
import { SimpleLogoNav } from '@cio/ui/custom/simple-logo-nav';
|
||||||
import { buildOrgLandingPageProps, normalizeLandingPageSettings } from '$features/org/utils/landing-page';
|
import { buildOrgLandingPageProps, normalizeLandingPageSettings } from '$features/org/utils/landing-page';
|
||||||
import OrgSiteFavicon from '$features/app/org-site-favicon.svelte';
|
|
||||||
import { basePath } from '$lib/utils/store/app';
|
import { basePath } from '$lib/utils/store/app';
|
||||||
import { t } from '$lib/utils/functions/translations';
|
import { t } from '$lib/utils/functions/translations';
|
||||||
import { user } from '$lib/utils/store/user';
|
import { user } from '$lib/utils/store/user';
|
||||||
@@ -65,7 +64,6 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#if data.isOrgSite && data.org}
|
{#if data.isOrgSite && data.org}
|
||||||
<OrgSiteFavicon org={data.org} />
|
|
||||||
{#if data.ThemeComponent && landingPageProps}
|
{#if data.ThemeComponent && landingPageProps}
|
||||||
<svelte:component this={data.ThemeComponent} {...landingPageProps} />
|
<svelte:component this={data.ThemeComponent} {...landingPageProps} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user