fix: suppot display icon on helmet (#26)

This commit is contained in:
Kilu.He 2025-01-14 15:08:16 +08:00 committed by GitHub
parent ddee80e095
commit fa43205569
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 97 additions and 32 deletions

View File

@ -47,7 +47,7 @@ const fetchMetaData = async (namespace: string, publishName?: string) => {
let url = `${baseURL}/api/workspace/published/${namespace}`;
if (publishName) {
url += `/${publishName}`;
url = `${baseURL}/api/workspace/v1/published/${namespace}/${publishName}`;
}
logger.info(`Fetching meta data from ${url}`);
@ -60,7 +60,11 @@ const fetchMetaData = async (namespace: string, publishName?: string) => {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
const data = await response.json();
logger.info(`Fetched meta data from ${url}: ${JSON.stringify(data)}`);
return data;
} catch (error) {
logger.error(`Error fetching meta data ${error}`);
return null;
@ -130,7 +134,11 @@ const createServer = async (req: Request) => {
const data = await fetchMetaData(namespace, publishName);
if (publishName) {
metaData = data;
if (data.code === 0) {
metaData = data.data;
} else {
logger.error(`Error fetching meta data: ${JSON.stringify(data)}`);
}
} else {
const publishInfo = data?.data?.info;
@ -165,6 +173,7 @@ const createServer = async (req: Request) => {
if (metaData && metaData.view) {
const view = metaData.view;
const emoji = view.icon?.ty === 0 && view.icon?.value;
const icon = view.icon?.ty === 2 && view.icon?.value;
const titleList = [];
if (emoji) {
@ -172,6 +181,15 @@ const createServer = async (req: Request) => {
const baseUrl = 'https://raw.githubusercontent.com/googlefonts/noto-emoji/main/svg/emoji_u';
favicon = `${baseUrl}${emojiCode}.svg`;
} else if (icon) {
try {
const { iconContent, color } = JSON.parse(view.icon?.value);
favicon = getIconBase64(iconContent, color);
$('link[rel="icon"]').attr('type', 'image/svg+xml');
} catch (_) {
// Do nothing
}
}
if (view.name) {
@ -253,3 +271,30 @@ const start = () => {
start();
export {};
function getIconBase64 (svgText: string, color: string) {
let newSvgText = svgText.replace(/fill="[^"]*"/g, ``);
newSvgText = newSvgText.replace('<svg', `<svg fill="${argbToRgba(color)}"`);
const base64String = btoa(newSvgText);
return `data:image/svg+xml;base64,${base64String}`;
}
function argbToRgba (color: string): string {
const hex = color.replace(/^#|0x/, '');
const hasAlpha = hex.length === 8;
if (!hasAlpha) {
return color.replace('0x', '#');
}
const r = parseInt(hex.slice(2, 4), 16);
const g = parseInt(hex.slice(4, 6), 16);
const b = parseInt(hex.slice(6, 8), 16);
const a = hasAlpha ? parseInt(hex.slice(0, 2), 16) / 255 : 1;
return `rgba(${r}, ${g}, ${b}, ${a})`;
}

View File

@ -1,4 +1,5 @@
import { ViewIcon, ViewIconType } from '@/application/types';
import { getIconBase64 } from '@/utils/emoji';
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet';
@ -14,8 +15,10 @@ function ViewHelmet ({
const setFavicon = async () => {
try {
let url = '/appflowy.svg';
const link = document.querySelector('link[rel*=\'icon\']') as HTMLLinkElement || document.createElement('link');
if (icon && icon.ty === ViewIconType.Emoji && icon.value) {
if (icon && icon.value) {
if (icon.ty === ViewIconType.Emoji) {
const emojiCode = icon?.value?.codePointAt(0)?.toString(16); // Convert emoji to hex code
const baseUrl = 'https://raw.githubusercontent.com/googlefonts/noto-emoji/main/svg/emoji_u';
@ -24,11 +27,23 @@ function ViewHelmet ({
const blob = new Blob([svgText], { type: 'image/svg+xml' });
url = URL.createObjectURL(blob);
}
const link = document.querySelector('link[rel*=\'icon\']') as HTMLLinkElement || document.createElement('link');
link.type = 'image/svg+xml';
} else if (icon.ty === ViewIconType.Icon) {
const {
groupName,
iconName,
color,
} = JSON.parse(icon.value);
const id = `${groupName}/${iconName}`;
url = (await getIconBase64(id, color)) || '';
link.type = 'image/svg+xml';
}
}
link.rel = 'icon';
link.href = url;
document.getElementsByTagName('head')[0].appendChild(link);

View File

@ -3,7 +3,7 @@ import { FieldType } from '@/application/database-yjs/database.type';
import { Column, useFieldSelector } from '@/application/database-yjs/selector';
import { FieldTypeIcon } from '@/components/database/components/field';
import { ThemeModeContext } from '@/components/main/useAppThemeMode';
import { getIconSvgEncodedContent } from '@/utils/emoji';
import { getIconBase64 } from '@/utils/emoji';
import { Tooltip } from '@mui/material';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { ReactComponent as AIIndicatorSvg } from '@/assets/ai_indicator.svg';
@ -25,8 +25,8 @@ export function GridColumn ({ column, index }: { column: Column; index: number }
useEffect(() => {
if (icon) {
void getIconSvgEncodedContent(icon, isDark ? 'white' : 'black').then((res) => {
setIconEncodeContent(res);
void getIconBase64(icon, isDark ? 'white' : 'black').then((res) => {
if (res) setIconEncodeContent(res);
});
}
}, [icon, isDark]);

View File

@ -1,3 +1,4 @@
import { renderColor } from '@/utils/color';
import { EmojiMartData } from '@emoji-mart/data';
import axios from 'axios';
@ -66,16 +67,20 @@ export async function loadIcons(): Promise<
});
}
export async function getIconSvgEncodedContent(id: string, color: string) {
export async function getIconBase64 (id: string, color: string) {
try {
const { data } = await axios.get(`/af_icons/${id}.svg`);
const response = await fetch(`/af_icons/${id}.svg`);
let svgText = await response.text();
const urlEncodedContent = encodeURIComponent(data.replaceAll('black', color));
svgText = svgText.replace(/fill="[^"]*"/g, ``);
svgText = svgText.replace('<svg', `<svg fill="${renderColor(color)}"`);
return `data:image/svg+xml;utf8,${urlEncodedContent}`;
} catch (e) {
console.error(e);
return null;
const base64String = btoa(svgText);
return `data:image/svg+xml;base64,${base64String}`;
} catch (error) {
console.error('Error setting favicon:', error);
return '';
}
}