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;
}
location /covers/ {
root /usr/share/nginx/html;
expires 30d;
access_log off;
}
error_page 404 /404.html;
location = /404.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,13 +56,14 @@ const fetchMetaData = async (url) => {
}
};
const BASE_URL = process.env.AF_BASE_URL || 'https://beta.appflowy.cloud';
const createServer = async (req) => {
const timer = logRequestTimer(req);
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 [
namespace,
publishName,
@ -85,7 +86,11 @@ const createServer = async (req) => {
let metaData;
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) {
logger.error(`Error fetching meta data: ${error}`);
}
@ -105,8 +110,13 @@ const createServer = async (req) => {
try {
const cover = metaData.view.extra ? JSON.parse(metaData.view.extra)?.cover : null;
if (cover && ['unsplash', 'custom'].includes(cover.type)) {
image = cover.value;
if (cover) {
if (['unsplash', 'custom'].includes(cover.type)) {
image = cover.value;
} else if (cover.type === 'built_in') {
image = `/covers/m_cover_image_${cover.value}.png`;
}
}
} catch (_) {
// Do nothing
@ -152,7 +162,6 @@ const start = () => {
},
});
logger.info(`Server is running on port 3000`);
logger.info(`Base API URL: ${process.env.AF_BASE_URL}`);
} catch (err) {
logger.error(err);
process.exit(1);

View File

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

View File

@ -7,7 +7,7 @@ describe('convert yjs data to slate content', () => {
it('should return undefined if root block is not exist', () => {
const doc = new Y.Doc();
expect(() => yDocToSlateContent(doc)).toThrowError();
expect(() => yDocToSlateContent(doc)).toBeUndefined();
const doc2 = withTestingYDoc('1');
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 { 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 = {
cloudConfig: {
baseURL: import.meta.env.AF_BASE_URL
? import.meta.env.AF_BASE_URL
: import.meta.env.DEV
? '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',
baseURL,
gotrueURL,
wsURL: `wss://${baseAPIHost}/ws/v1`,
},
};

View File

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

View File

@ -28,11 +28,11 @@ export function BulletedListIcon({ block, className }: { block: BulletedListNode
const dataLetter = useMemo(() => {
switch (letter) {
case Letter.Disc:
return '';
return 'disc';
case Letter.Circle:
return '';
return 'circle';
case Letter.Square:
return '';
return 'square';
}
}, [letter]);
@ -41,9 +41,8 @@ export function BulletedListIcon({ block, className }: { block: BulletedListNode
onMouseDown={(e) => {
e.preventDefault();
}}
data-letter={dataLetter}
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;
description: string;
} | null>(null);
const [notFound, setNotFound] = useState<boolean>(false);
const url = node.data.url;
useEffect(() => {
@ -17,14 +18,19 @@ export const LinkPreview = memo(
setData(null);
void (async () => {
try {
setNotFound(false);
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;
setData(data);
} catch (error) {
// don't do anything
} catch (_) {
setNotFound(true);
}
})();
}, [url]);
@ -37,17 +43,22 @@ export const LinkPreview = memo(
ref={ref}
className={`link-preview-block relative w-full cursor-pointer py-1`}
>
<div>
{data ? (
<div
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'
}
>
<div
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
src={data.image.url}
alt={data.title}
className={'container h-full w-[25%] rounded bg-cover bg-center'}
src={data?.image.url}
alt={data?.title}
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
@ -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'
}
>
{data.title}
{data?.title}
</div>
<div
className={
'max-h-[64px] overflow-hidden truncate whitespace-pre-wrap break-words text-sm text-text-title'
}
>
{data.description}
{data?.description}
</div>
<div className={'truncate whitespace-nowrap text-xs text-text-caption'}>{url}</div>
</div>
</div>
) : (
<a href={node.data.url} className={'text-content-blue-400 underline'} target={'blank'}>
{node.data.url}
</a>
</>
)}
</div>
<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 { Text } from '@/components/editor/components/blocks/text';
import { ElementFallbackRender } from '@/components/error/ElementFallbackRender';
import { Skeleton } from '@mui/material';
import { ErrorBoundary } from 'react-error-boundary';
import { TodoList } from 'src/components/editor/components/blocks/todo-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 { EditorElementProps, TextNode } from '@/components/editor/editor.type';
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 isEqual from 'lodash-es/isEqual';
@ -126,15 +125,13 @@ export const Element = memo(
}
return (
<Suspense fallback={<Skeleton width={'100%'} height={24} />}>
<ErrorBoundary fallbackRender={ElementFallbackRender}>
<div {...attributes} data-block-type={node.type} className={className}>
<Component style={style} className={`flex w-full flex-col`} node={node}>
{children}
</Component>
</div>
</ErrorBoundary>
</Suspense>
<ErrorBoundary fallbackRender={ElementFallbackRender}>
<div {...attributes} data-block-type={node.type} className={className}>
<Component style={style} className={`flex w-full flex-col`} node={node}>
{children}
</Component>
</div>
</ErrorBoundary>
);
},
(prevProps, nextProps) => isEqual(prevProps.element, nextProps.element)

View File

@ -197,10 +197,22 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) {
}
.bulleted-icon {
&:after {
content: attr(data-letter);
font-weight: 500;
background-repeat: no-repeat;
background-size: 16px 16px;
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 {

View File

@ -52,12 +52,12 @@ body {
}
.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 {
@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;
white-space: nowrap;
}