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,21 +15,35 @@ function ViewHelmet ({
const setFavicon = async () => {
try {
let url = '/appflowy.svg';
if (icon && icon.ty === ViewIconType.Emoji && icon.value) {
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';
const response = await fetch(`${baseUrl}${emojiCode}.svg`);
const svgText = await response.text();
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';
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';
const response = await fetch(`${baseUrl}${emojiCode}.svg`);
const svgText = await response.text();
const blob = new Blob([svgText], { type: 'image/svg+xml' });
url = URL.createObjectURL(blob);
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,7 +1,8 @@
import { renderColor } from '@/utils/color';
import { EmojiMartData } from '@emoji-mart/data';
import axios from 'axios';
export async function randomEmoji(skin = 0) {
export async function randomEmoji (skin = 0) {
const emojiData = await loadEmojiData();
const emojis = (emojiData as EmojiMartData).emojis;
const keys = Object.keys(emojis);
@ -10,11 +11,11 @@ export async function randomEmoji(skin = 0) {
return emojis[randomKey].skins[skin].native;
}
export async function loadEmojiData() {
export async function loadEmojiData () {
return import('@emoji-mart/data/sets/15/native.json');
}
export function isFlagEmoji(emoji: string) {
export function isFlagEmoji (emoji: string) {
return /\uD83C[\uDDE6-\uDDFF]/.test(emoji);
}
@ -45,7 +46,7 @@ let icons: Record<ICON_CATEGORY,
keywords: string[];
}[]> | undefined;
export async function loadIcons(): Promise<
export async function loadIcons (): Promise<
Record<
ICON_CATEGORY,
{
@ -66,20 +67,24 @@ 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 '';
}
}
export async function randomIcon() {
export async function randomIcon () {
const icons = await loadIcons();
const categories = Object.keys(icons);
const randomCategory = categories[Math.floor(Math.random() * categories.length)] as ICON_CATEGORY;
@ -88,7 +93,7 @@ export async function randomIcon() {
return randomIcon;
}
export async function getIcon(id: string) {
export async function getIcon (id: string) {
const icons = await loadIcons();
for (const category of Object.keys(icons)) {