fix: suppot display icon on helmet (#26)
This commit is contained in:
parent
ddee80e095
commit
fa43205569
|
|
@ -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})`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue