fix: modified bullted icon style

This commit is contained in:
qinluhe 2024-07-03 10:16:07 +08:00
parent f96218820d
commit d5c08a3c80
21 changed files with 116 additions and 66 deletions

View File

@ -72,6 +72,12 @@ http {
access_log off; access_log off;
} }
location /covers/ {
root /usr/share/nginx/html;
expires 30d;
access_log off;
}
error_page 404 /404.html; error_page 404 /404.html;
location = /404.html { location = /404.html {
root /usr/share/nginx/html; root /usr/share/nginx/html;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -56,12 +56,13 @@ const fetchMetaData = async (url) => {
} }
}; };
const BASE_URL = process.env.AF_BASE_URL || 'https://beta.appflowy.cloud';
const createServer = async (req) => { const createServer = async (req) => {
const timer = logRequestTimer(req); const timer = logRequestTimer(req);
const reqUrl = new URL(req.url); const reqUrl = new URL(req.url);
logger.info(`Request URL: ${reqUrl.pathname}`); const hostname = req.headers.get('host');
logger.info(`Request URL: ${hostname}${reqUrl.pathname}`);
const [ const [
namespace, namespace,
@ -85,7 +86,11 @@ const createServer = async (req) => {
let metaData; let metaData;
try { try {
metaData = await fetchMetaData(`${BASE_URL}/api/workspace/published/${namespace}/${publishName}`); const isBeta = hostname.startsWith('beta');
const isTest = hostname.startsWith('test');
const defaultUrl = 'https://beta.appflowy.cloud';
const baseUrl = isBeta ? 'https://beta.appflowy.cloud' : isTest ? 'https://test.appflowy.cloud' : defaultUrl;
metaData = await fetchMetaData(`${baseUrl}/api/workspace/published/${namespace}/${publishName}`);
} catch (error) { } catch (error) {
logger.error(`Error fetching meta data: ${error}`); logger.error(`Error fetching meta data: ${error}`);
} }
@ -105,8 +110,13 @@ const createServer = async (req) => {
try { try {
const cover = metaData.view.extra ? JSON.parse(metaData.view.extra)?.cover : null; const cover = metaData.view.extra ? JSON.parse(metaData.view.extra)?.cover : null;
if (cover && ['unsplash', 'custom'].includes(cover.type)) { if (cover) {
image = cover.value; if (['unsplash', 'custom'].includes(cover.type)) {
image = cover.value;
} else if (cover.type === 'built_in') {
image = `/covers/m_cover_image_${cover.value}.png`;
}
} }
} catch (_) { } catch (_) {
// Do nothing // Do nothing
@ -152,7 +162,6 @@ const start = () => {
}, },
}); });
logger.info(`Server is running on port 3000`); logger.info(`Server is running on port 3000`);
logger.info(`Base API URL: ${process.env.AF_BASE_URL}`);
} catch (err) { } catch (err) {
logger.error(err); logger.error(err);
process.exit(1); process.exit(1);

View File

@ -61,6 +61,7 @@ export class AFClientService implements AFService {
const name = `${namespace}_${publishName}`; const name = `${namespace}_${publishName}`;
const isLoaded = this.publishViewLoaded.has(name); const isLoaded = this.publishViewLoaded.has(name);
const doc = await getPublishView( const doc = await getPublishView(
async () => { async () => {
try { try {

View File

@ -7,7 +7,7 @@ describe('convert yjs data to slate content', () => {
it('should return undefined if root block is not exist', () => { it('should return undefined if root block is not exist', () => {
const doc = new Y.Doc(); const doc = new Y.Doc();
expect(() => yDocToSlateContent(doc)).toThrowError(); expect(() => yDocToSlateContent(doc)).toBeUndefined();
const doc2 = withTestingYDoc('1'); const doc2 = withTestingYDoc('1');
const { blocks, childrenMap, textMap, pageId } = getTestingDocData(doc2); const { blocks, childrenMap, textMap, pageId } = getTestingDocData(doc2);

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="9" cy="9" r="2.75" fill="#454545"/>
</svg>

After

Width:  |  Height:  |  Size: 151 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="9" cy="9" r="2.75" stroke="#454545"/>
</svg>

After

Width:  |  Height:  |  Size: 153 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="5" height="5" transform="translate(6.5 6.5)" fill="#454545"/>
</svg>

After

Width:  |  Height:  |  Size: 178 B

View File

@ -4,23 +4,25 @@ import React, { createContext, useEffect, useState } from 'react';
import { AFService, AFServiceConfig } from '@/application/services/services.type'; import { AFService, AFServiceConfig } from '@/application/services/services.type';
import { getService } from '@/application/services'; import { getService } from '@/application/services';
const hostName = window.location.hostname;
const isProd = !hostName.includes('localhost');
const isBeta = isProd && hostName.includes('beta');
const isTest = isProd && hostName.includes('test');
const baseAPIHost = isProd
? isBeta
? 'beta.appflowy.cloud'
: isTest
? 'test.appflowy.cloud'
: 'beta.appflowy.cloud'
: 'test.appflowy.cloud';
const baseURL = `https://${baseAPIHost}`;
const gotrueURL = `${baseURL}/gotrue`;
const defaultConfig: AFServiceConfig = { const defaultConfig: AFServiceConfig = {
cloudConfig: { cloudConfig: {
baseURL: import.meta.env.AF_BASE_URL baseURL,
? import.meta.env.AF_BASE_URL gotrueURL,
: import.meta.env.DEV wsURL: `wss://${baseAPIHost}/ws/v1`,
? 'https://test.appflowy.cloud'
: 'https://beta.appflowy.cloud',
gotrueURL: import.meta.env.AF_GOTRUE_URL
? import.meta.env.AF_GOTRUE_URL
: import.meta.env.DEV
? 'https://test.appflowy.cloud/gotrue'
: 'https://beta.appflowy.cloud/gotrue',
wsURL: import.meta.env.AF_WS_URL
? import.meta.env.AF_WS_URL
: import.meta.env.DEV
? 'wss://test.appflowy.cloud/ws/v1'
: 'wss://beta.appflowy.cloud/ws/v1',
}, },
}; };

View File

@ -1,10 +1,11 @@
import { useDecorate } from '@/components/editor/components/blocks/code/useDecorate'; import { useDecorate } from '@/components/editor/components/blocks/code/useDecorate';
import { Leaf } from '@/components/editor/components/leaf'; import { Leaf } from '@/components/editor/components/leaf';
import { useEditorContext } from '@/components/editor/EditorContext'; import { useEditorContext } from '@/components/editor/EditorContext';
import React, { useCallback } from 'react'; import React, { Suspense, useCallback } from 'react';
import { NodeEntry } from 'slate'; import { NodeEntry } from 'slate';
import { Editable, ReactEditor, RenderElementProps } from 'slate-react'; import { Editable, ReactEditor, RenderElementProps } from 'slate-react';
import { Element } from './components/element'; import { Element } from './components/element';
import { Skeleton } from '@mui/material';
const EditorEditable = ({ editor }: { editor: ReactEditor }) => { const EditorEditable = ({ editor }: { editor: ReactEditor }) => {
const { readOnly } = useEditorContext(); const { readOnly } = useEditorContext();
@ -17,7 +18,14 @@ const EditorEditable = ({ editor }: { editor: ReactEditor }) => {
[codeDecorate] [codeDecorate]
); );
const renderElement = useCallback((props: RenderElementProps) => <Element {...props} />, []); const renderElement = useCallback(
(props: RenderElementProps) => (
<Suspense fallback={<Skeleton width={'100%'} height={24} />}>
<Element {...props} />
</Suspense>
),
[]
);
return ( return (
<> <>

View File

@ -28,11 +28,11 @@ export function BulletedListIcon({ block, className }: { block: BulletedListNode
const dataLetter = useMemo(() => { const dataLetter = useMemo(() => {
switch (letter) { switch (letter) {
case Letter.Disc: case Letter.Disc:
return ''; return 'disc';
case Letter.Circle: case Letter.Circle:
return ''; return 'circle';
case Letter.Square: case Letter.Square:
return ''; return 'square';
} }
}, [letter]); }, [letter]);
@ -41,9 +41,8 @@ export function BulletedListIcon({ block, className }: { block: BulletedListNode
onMouseDown={(e) => { onMouseDown={(e) => {
e.preventDefault(); e.preventDefault();
}} }}
data-letter={dataLetter}
contentEditable={false} contentEditable={false}
className={`${className} bulleted-icon flex min-w-[24px] justify-center pr-1 font-medium`} className={`${className} bulleted-icon ${dataLetter} flex min-w-[24px] justify-center pr-1 font-medium`}
/> />
); );
} }

View File

@ -9,6 +9,7 @@ export const LinkPreview = memo(
title: string; title: string;
description: string; description: string;
} | null>(null); } | null>(null);
const [notFound, setNotFound] = useState<boolean>(false);
const url = node.data.url; const url = node.data.url;
useEffect(() => { useEffect(() => {
@ -17,14 +18,19 @@ export const LinkPreview = memo(
setData(null); setData(null);
void (async () => { void (async () => {
try { try {
setNotFound(false);
const response = await axios.get(`https://api.microlink.io/?url=${url}`); const response = await axios.get(`https://api.microlink.io/?url=${url}`);
if (response.data.statusCode !== 200) return; if (response.data.statusCode !== 200) {
setNotFound(true);
return;
}
const data = response.data.data; const data = response.data.data;
setData(data); setData(data);
} catch (error) { } catch (_) {
// don't do anything setNotFound(true);
} }
})(); })();
}, [url]); }, [url]);
@ -37,17 +43,22 @@ export const LinkPreview = memo(
ref={ref} ref={ref}
className={`link-preview-block relative w-full cursor-pointer py-1`} className={`link-preview-block relative w-full cursor-pointer py-1`}
> >
<div> <div
{data ? ( className={
<div 'container-bg flex w-full cursor-pointer select-none items-center gap-4 overflow-hidden rounded border border-line-divider bg-fill-list-active p-3'
className={ }
'container-bg flex w-full cursor-pointer select-none items-center gap-4 overflow-hidden rounded border border-line-divider bg-fill-list-active p-3' >
} {notFound ? (
> <div className={'flex w-full items-center justify-center'}>
<div className={'text-text-title'}>Could not load preview</div>
<div className={'text-sm text-text-caption'}>{url}</div>
</div>
) : (
<>
<img <img
src={data.image.url} src={data?.image.url}
alt={data.title} alt={data?.title}
className={'container h-full w-[25%] rounded bg-cover bg-center'} className={'container h-full min-h-[48px] w-[25%] rounded bg-cover bg-center'}
/> />
<div className={'flex flex-col justify-center gap-2 overflow-hidden'}> <div className={'flex flex-col justify-center gap-2 overflow-hidden'}>
<div <div
@ -55,22 +66,18 @@ export const LinkPreview = memo(
'max-h-[48px] overflow-hidden whitespace-pre-wrap break-words text-base font-bold text-text-title' 'max-h-[48px] overflow-hidden whitespace-pre-wrap break-words text-base font-bold text-text-title'
} }
> >
{data.title} {data?.title}
</div> </div>
<div <div
className={ className={
'max-h-[64px] overflow-hidden truncate whitespace-pre-wrap break-words text-sm text-text-title' 'max-h-[64px] overflow-hidden truncate whitespace-pre-wrap break-words text-sm text-text-title'
} }
> >
{data.description} {data?.description}
</div> </div>
<div className={'truncate whitespace-nowrap text-xs text-text-caption'}>{url}</div> <div className={'truncate whitespace-nowrap text-xs text-text-caption'}>{url}</div>
</div> </div>
</div> </>
) : (
<a href={node.data.url} className={'text-content-blue-400 underline'} target={'blank'}>
{node.data.url}
</a>
)} )}
</div> </div>
<div ref={ref} className={'absolute left-0 top-0 h-full w-full caret-transparent'}> <div ref={ref} className={'absolute left-0 top-0 h-full w-full caret-transparent'}>

View File

@ -16,7 +16,6 @@ import { Quote } from '@/components/editor/components/blocks/quote';
import { TableBlock, TableCellBlock } from '@/components/editor/components/blocks/table'; import { TableBlock, TableCellBlock } from '@/components/editor/components/blocks/table';
import { Text } from '@/components/editor/components/blocks/text'; import { Text } from '@/components/editor/components/blocks/text';
import { ElementFallbackRender } from '@/components/error/ElementFallbackRender'; import { ElementFallbackRender } from '@/components/error/ElementFallbackRender';
import { Skeleton } from '@mui/material';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import { TodoList } from 'src/components/editor/components/blocks/todo-list'; import { TodoList } from 'src/components/editor/components/blocks/todo-list';
import { ToggleList } from 'src/components/editor/components/blocks/toggle-list'; import { ToggleList } from 'src/components/editor/components/blocks/toggle-list';
@ -25,7 +24,7 @@ import { Formula } from '@/components/editor/components/leaf/formula';
import { Mention } from '@/components/editor/components/leaf/mention'; import { Mention } from '@/components/editor/components/leaf/mention';
import { EditorElementProps, TextNode } from '@/components/editor/editor.type'; import { EditorElementProps, TextNode } from '@/components/editor/editor.type';
import { renderColor } from '@/utils/color'; import { renderColor } from '@/utils/color';
import React, { FC, memo, Suspense, useMemo } from 'react'; import React, { FC, memo, useMemo } from 'react';
import { RenderElementProps } from 'slate-react'; import { RenderElementProps } from 'slate-react';
import isEqual from 'lodash-es/isEqual'; import isEqual from 'lodash-es/isEqual';
@ -126,15 +125,13 @@ export const Element = memo(
} }
return ( return (
<Suspense fallback={<Skeleton width={'100%'} height={24} />}> <ErrorBoundary fallbackRender={ElementFallbackRender}>
<ErrorBoundary fallbackRender={ElementFallbackRender}> <div {...attributes} data-block-type={node.type} className={className}>
<div {...attributes} data-block-type={node.type} className={className}> <Component style={style} className={`flex w-full flex-col`} node={node}>
<Component style={style} className={`flex w-full flex-col`} node={node}> {children}
{children} </Component>
</Component> </div>
</div> </ErrorBoundary>
</ErrorBoundary>
</Suspense>
); );
}, },
(prevProps, nextProps) => isEqual(prevProps.element, nextProps.element) (prevProps, nextProps) => isEqual(prevProps.element, nextProps.element)

View File

@ -197,10 +197,22 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) {
} }
.bulleted-icon { .bulleted-icon {
&:after { background-repeat: no-repeat;
content: attr(data-letter); background-size: 16px 16px;
font-weight: 500; background-position: center;
&.disc {
background-image: url('../../assets/bulleted_list_icon_1.svg');
} }
&.circle {
background-image: url('../../assets/bulleted_list_icon_2.svg');
}
&.square {
background-image: url('../../assets/bulleted_list_icon_3.svg');
}
} }
.numbered-icon { .numbered-icon {

View File

@ -52,12 +52,12 @@ body {
} }
.icon { .icon {
font-family: 'Apple Color Emoji', 'Noto Color Emoji', 'Segoe UI Emoji', 'Twemoji Mozilla', sans-serif; font-family: 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Twemoji Mozilla', sans-serif;
} }
.view-icon { .view-icon {
@apply flex w-fit leading-[1.5em] cursor-pointer rounded-lg py-2 text-[1.5em]; @apply flex w-fit leading-[1.5em] cursor-pointer rounded-lg py-2 text-[1.5em];
font-family: 'Apple Color Emoji', 'Noto Color Emoji', 'Segoe UI Emoji', 'Twemoji Mozilla', sans-serif; font-family: 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Twemoji Mozilla', sans-serif;
line-height: 1em; line-height: 1em;
white-space: nowrap; white-space: nowrap;
} }