Merge pull request #4473 from toeverything/release-0.8.4

0.8.4 Release proposal
This commit is contained in:
LongYinan
2023-09-26 02:58:00 -05:00
committed by GitHub
36 changed files with 281 additions and 213 deletions
+1
View File
@@ -1,6 +1,7 @@
name: Build Canary Desktop App on Staging Branch
on:
workflow_dispatch:
push:
branches:
# 0.6.x-staging
+1 -1
View File
@@ -60,7 +60,7 @@
"react-resizable-panels": "^0.0.55",
"react-router-dom": "^6.15.0",
"rxjs": "^7.8.1",
"ses": "^0.18.7",
"ses": "^0.18.8",
"swr": "2.2.1",
"y-protocols": "^1.0.5",
"yjs": "^13.6.7",
+1 -2
View File
@@ -1,4 +1,3 @@
import { isDesktop } from '@affine/env/constant';
import { atom, useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
@@ -50,7 +49,7 @@ export const fontStyleOptions = [
}[];
const appSettingBaseAtom = atomWithStorage<AppSetting>('affine-settings', {
clientBorder: isDesktop,
clientBorder: environment.isDesktop && globalThis.platform !== 'win32',
fullWidthLayout: false,
windowFrameStyle: 'frameless',
fontStyle: 'Sans',
@@ -281,11 +281,7 @@ export const CreateWorkspaceModal = ({
console.error(err);
});
} else if (mode === 'new') {
setStep(
environment.isDesktop && runtimeConfig.enableSQLiteProvider
? 'set-db-location'
: 'name-workspace'
);
setStep('name-workspace');
} else {
setStep(undefined);
}
@@ -24,11 +24,8 @@ export const shortcutKey = style({
alignItems: 'center',
justifyContent: 'center',
padding: '0 6px',
border: '1px solid var(--affine-border-color)',
borderRadius: '4px',
background: 'var(--affine-background-tertiary-color)',
boxShadow:
'0px 6px 4px 0px rgba(255, 255, 255, 0.24) inset, 0px 0px 0px 0.5px rgba(0, 0, 0, 0.10) inset',
fontSize: 'var(--affine-font-xs)',
selectors: {
'&:not(:last-of-type)': {
@@ -25,6 +25,15 @@ export const titleInput = style({
margin: 'auto',
width: '100%',
height: '100%',
selectors: {
'&:focus': {
border: '1px solid var(--affine-black-10)',
borderRadius: '8px',
height: '32px',
padding: '6px 8px',
},
},
});
export const shadowTitle = style({
visibility: 'hidden',
@@ -51,7 +51,7 @@ export const AddCollectionButton = ({
onConfirm={setting.saveCollection}
open={show}
onClose={() => showUpdateCollection(false)}
title={t['Save As New Collection']()}
title={t['Save as New Collection']()}
init={defaultCollection}
/>
</>
+10 -4
View File
@@ -8,7 +8,13 @@ import {
} from '@toeverything/infra/atom';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import type { ReactElement } from 'react';
import { lazy, Suspense, useCallback, useTransition } from 'react';
import {
lazy,
startTransition,
Suspense,
useCallback,
useTransition,
} from 'react';
import type { SettingAtom } from '../atoms';
import {
@@ -119,13 +125,14 @@ export const AllWorkspaceModals = (): ReactElement => {
);
const { jumpToSubPath } = useNavigateHelper();
const workspaces = useAtomValue(rootWorkspacesMetadataAtom);
const workspaces = useAtomValue(rootWorkspacesMetadataAtom, {
delay: 0,
});
const setWorkspaces = useSetAtom(rootWorkspacesMetadataAtom);
const [currentWorkspaceId, setCurrentWorkspaceId] = useAtom(
currentWorkspaceIdAtom
);
const setCurrentPageId = useSetAtom(currentPageIdAtom);
const [isPending, startTransition] = useTransition();
const [, startCloseTransition] = useTransition();
const [, setOpenSettingModalAtom] = useAtom(openSettingModalAtom);
@@ -146,7 +153,6 @@ export const AllWorkspaceModals = (): ReactElement => {
<>
<Suspense>
<WorkspaceListModal
disabled={isPending}
workspaces={workspaces}
currentWorkspaceId={currentWorkspaceId}
open={
-3
View File
@@ -154,9 +154,6 @@ test('windows only check', async ({ page }) => {
test('delete workspace', async ({ page }) => {
await page.getByTestId('current-workspace').click();
await page.getByTestId('new-workspace').click();
await page.getByTestId('create-workspace-default-location-button').click({
delay: 100,
});
await page.getByTestId('create-workspace-input').type('Delete Me', {
delay: 100,
});
+3 -1
View File
@@ -12,6 +12,8 @@ import fs from 'fs-extra';
import type { ElectronApplication, Page } from 'playwright';
import { _electron as electron } from 'playwright';
import { removeWithRetry } from '../tests/utils';
function generateUUID() {
return crypto.randomUUID();
}
@@ -102,7 +104,7 @@ export const test = base.extend<{
});
await use(electronApp);
try {
await fs.rm(clonedDist, { recursive: true, force: true });
await removeWithRetry(clonedDist);
} catch (error) {
console.log(error);
}
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"baseUrl": ".",
"target": "ESNext"
},
"references": [{ "path": "../../../tests/kit" }],
"include": ["**.spec.ts", "**.test.ts", "fixture.ts"],
"exclude": ["lib"]
}
-1
View File
@@ -113,7 +113,6 @@ module.exports = {
: undefined,
// We need the following line for updater
extraResource: ['./resources/app-update.yml'],
ignore: ['e2e', 'tests'],
},
makers,
hooks: {
@@ -70,10 +70,13 @@ export const registerUpdater = async () => {
autoUpdater.on('checking-for-update', () => {
logger.info('Checking for update');
});
let downloading = false;
autoUpdater.on('update-available', info => {
logger.info('Update available', info);
if (allowAutoUpdate) {
if (allowAutoUpdate && !downloading) {
downloading = true;
autoUpdater?.downloadUpdate().catch(e => {
downloading = false;
logger.error('Failed to download update', e);
});
logger.info('Update available, downloading...', info);
@@ -91,6 +94,7 @@ export const registerUpdater = async () => {
updaterSubjects.downloadProgress.next(e.percent);
});
autoUpdater.on('update-downloaded', e => {
downloading = false;
updaterSubjects.updateReady.next({
version: e.version,
allowAutoUpdate,
-3
View File
@@ -33,9 +33,6 @@
{
"path": "./tsconfig.node.json"
},
{
"path": "./e2e/tsconfig.json"
},
{ "path": "../../tests/kit" }
],
"ts-node": {
+1 -1
View File
@@ -3,7 +3,7 @@
"compilerOptions": {
"composite": true
},
"include": ["**/__tests__/**/*", "./tests"],
"include": ["**/__tests__/**/*", "./tests", "./e2e"],
"references": [
{
"path": "./tsconfig.json"
+1 -1
View File
@@ -25,7 +25,7 @@
"concurrently": "^8.2.0",
"jest-mock": "^29.6.2",
"serve": "^14.2.0",
"ses": "^0.18.7",
"ses": "^0.18.8",
"storybook": "^7.3.1",
"storybook-dark-mode": "^3.0.1",
"wait-on": "^7.0.1"
+1 -2
View File
@@ -32,8 +32,7 @@
"{projectRoot}/dist",
"{projectRoot}/build",
"{projectRoot}/out",
"{projectRoot}/storybook-static",
"{workspaceRoot}/packages/i18n/src/i18n-generated.ts"
"{projectRoot}/storybook-static"
],
"inputs": [
"{workspaceRoot}/infra/**/*",
+1
View File
@@ -21,6 +21,7 @@
"@affine/jotai": "workspace:*",
"@affine/workspace": "workspace:*",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
@@ -35,7 +35,7 @@ export const navWrapperStyle = style({
'&[data-enable-animation="true"]': {
transition: 'margin-left .3s, width .3s',
},
'&.has-background': {
'&[data-is-floating="false"].has-background': {
backgroundColor: 'var(--affine-white-60)',
borderRight: '1px solid var(--affine-border-color)',
},
@@ -2,6 +2,7 @@ import { Skeleton } from '@mui/material';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import { useAtom, useAtomValue } from 'jotai';
import debounce from 'lodash/debounce';
import type { PropsWithChildren, ReactElement } from 'react';
import { useEffect, useRef, useState } from 'react';
@@ -77,10 +78,10 @@ export function AppSidebar(props: AppSidebarProps): ReactElement {
setAppSidebarFloating(isFloating && !!open);
}
onResize();
window.addEventListener('resize', onResize);
const dOnResize = debounce(onResize, 50);
window.addEventListener('resize', dOnResize);
return () => {
window.removeEventListener('resize', onResize);
window.removeEventListener('resize', dOnResize);
};
}, [appSidebarWidth, open, setAppSidebarFloating, setOpen]);
@@ -47,7 +47,6 @@ export const StyledCard = styled('div')<{
borderRadius: '12px',
border: `1px solid ${borderColor}`,
...displayFlex('flex-start', 'flex-start'),
marginBottom: '12px',
transition: 'background .2s',
alignItems: 'center',
position: 'relative',
@@ -62,6 +61,12 @@ export const StyledCard = styled('div')<{
'.setting-entry': {
opacity: 1,
pointerEvents: 'auto',
backgroundColor: 'var(--affine-white-30)',
boxShadow: 'var(--affine-shadow-1)',
':hover': {
background:
'linear-gradient(0deg, var(--affine-hover-color) 0%, var(--affine-hover-color) 100%), var(--affine-white-30)',
},
},
},
'@media (max-width: 720px)': {
@@ -63,9 +63,7 @@ export const Condition = ({
<div className={styles.filterTypeIconStyle}>
{variableDefineMap[ast.left.name].icon}
</div>
<div>
<FilterTag name={ast.left.name} />
</div>
<FilterTag name={ast.left.name} />
</div>
</Menu>
<Menu
@@ -53,6 +53,8 @@ export const switchStyle = style({
color: 'var(--affine-text-secondary-color)',
padding: '2px 4px',
transition: 'all 0.15s ease-in-out',
display: 'flex',
alignItems: 'center',
':hover': {
cursor: 'pointer',
background: 'var(--affine-hover-color)',
@@ -62,6 +64,7 @@ export const switchStyle = style({
export const filterTypeStyle = style({
fontSize: 'var(--affine-font-sm)',
display: 'flex',
alignItems: 'center',
padding: '2px 4px',
transition: 'all 0.15s ease-in-out',
marginRight: '6px',
@@ -76,5 +79,6 @@ export const filterTypeIconStyle = style({
marginRight: '6px',
padding: '1px 0',
display: 'flex',
alignItems: 'center',
color: 'var(--affine-icon-color)',
});
@@ -42,9 +42,15 @@ export const createDefaultFilter = (
}
return {
type: 'filter',
left: { type: 'ref', name: variable.name },
left: {
type: 'ref',
name: variable.name,
},
funcName: data.name,
args: data.defaultArgs().map(value => ({ type: 'literal', value })),
args: data.defaultArgs().map(value => ({
type: 'literal',
value,
})),
};
};
@@ -137,7 +143,10 @@ filterMatcher.register(
);
filterMatcher.register(
tFunction({ args: [tDate.create(), tDate.create()], rt: tBoolean.create() }),
tFunction({
args: [tDate.create(), tDate.create()],
rt: tBoolean.create(),
}),
{
name: 'after',
defaultArgs: () => {
@@ -153,7 +162,10 @@ filterMatcher.register(
);
filterMatcher.register(
tFunction({ args: [tDate.create(), tDate.create()], rt: tBoolean.create() }),
tFunction({
args: [tDate.create(), tDate.create()],
rt: tBoolean.create(),
}),
{
name: 'before',
defaultArgs: () => [dayjs().endOf('day').valueOf()],
@@ -165,31 +177,35 @@ filterMatcher.register(
},
}
);
const safeArray = (arr: unknown): LiteralValue[] => {
return Array.isArray(arr) ? arr : [];
};
filterMatcher.register(
tFunction({ args: [tArray(tTag.create())], rt: tBoolean.create() }),
tFunction({
args: [tArray(tTag.create())],
rt: tBoolean.create(),
}),
{
name: 'is not empty',
defaultArgs: () => [],
impl: tags => {
if (Array.isArray(tags)) {
return tags.length > 0;
}
return true;
const safeTags = safeArray(tags);
return safeTags.length > 0;
},
}
);
filterMatcher.register(
tFunction({ args: [tArray(tTag.create())], rt: tBoolean.create() }),
tFunction({
args: [tArray(tTag.create())],
rt: tBoolean.create(),
}),
{
name: 'is empty',
defaultArgs: () => [],
impl: tags => {
if (Array.isArray(tags)) {
return tags.length == 0;
}
return true;
const safeTags = safeArray(tags);
return safeTags.length == 0;
},
}
);
@@ -204,10 +220,11 @@ filterMatcher.register(
name: 'contains all',
defaultArgs: () => [],
impl: (tags, target) => {
if (Array.isArray(tags) && Array.isArray(target)) {
return target.every(id => tags.includes(id));
if (!Array.isArray(target)) {
return true;
}
return true;
const safeTags = safeArray(tags);
return target.every(id => safeTags.includes(id));
},
}
);
@@ -222,10 +239,11 @@ filterMatcher.register(
name: 'contains one of',
defaultArgs: () => [],
impl: (tags, target) => {
if (Array.isArray(tags) && Array.isArray(target)) {
return target.some(id => tags.includes(id));
if (!Array.isArray(target)) {
return true;
}
return true;
const safeTags = safeArray(tags);
return target.some(id => safeTags.includes(id));
},
}
);
@@ -240,10 +258,11 @@ filterMatcher.register(
name: 'does not contains all',
defaultArgs: () => [],
impl: (tags, target) => {
if (Array.isArray(tags) && Array.isArray(target)) {
return !target.every(id => tags.includes(id));
if (!Array.isArray(target)) {
return true;
}
return true;
const safeTags = safeArray(tags);
return !target.every(id => safeTags.includes(id));
},
}
);
@@ -258,10 +277,11 @@ filterMatcher.register(
name: 'does not contains one of',
defaultArgs: () => [],
impl: (tags, target) => {
if (Array.isArray(tags) && Array.isArray(target)) {
return !target.some(id => tags.includes(id));
if (!Array.isArray(target)) {
return true;
}
return true;
const safeTags = safeArray(tags);
return !target.some(id => safeTags.includes(id));
},
}
);
@@ -1,19 +1,25 @@
import { useState } from 'react';
type SorterConfig<T> = {
type SorterConfig<
T extends Record<string | number | symbol, unknown> = Record<
string | number | symbol,
unknown
>,
> = {
data: T[];
key: keyof T;
order: 'asc' | 'desc' | 'none';
sortingFn?: (
ctx: {
key: keyof T;
order: 'asc' | 'desc';
},
a: T,
b: T
) => number;
};
const defaultSortingFn = <T extends Record<keyof any, unknown>>(
ctx: {
key: keyof T;
order: 'asc' | 'desc';
},
a: T,
b: T
) => {
const defaultSortingFn: SorterConfig['sortingFn'] = (ctx, a, b) => {
const valA = a[ctx.key];
const valB = b[ctx.key];
const revert = ctx.order === 'desc';
@@ -33,6 +39,10 @@ const defaultSortingFn = <T extends Record<keyof any, unknown>>(
if (!valB) {
return 1 * revertSymbol;
}
if (Array.isArray(valA) && Array.isArray(valB)) {
return (valA.length - valB.length) * revertSymbol;
}
console.warn(
'Unsupported sorting type! Please use custom sorting function.',
valA,
@@ -43,6 +53,7 @@ const defaultSortingFn = <T extends Record<keyof any, unknown>>(
export const useSorter = <T extends Record<keyof any, unknown>>({
data,
sortingFn = defaultSortingFn,
...defaultSorter
}: SorterConfig<T> & { order: 'asc' | 'desc' }) => {
const [sorter, setSorter] = useState<Omit<SorterConfig<T>, 'data'>>({
@@ -60,9 +71,8 @@ export const useSorter = <T extends Record<keyof any, unknown>>({
key: sorter.key,
order: sorter.order,
};
// TODO supports custom sorting function
const sortingFn = (a: T, b: T) => defaultSortingFn(sortCtx, a, b);
const sortedData = data.sort(sortingFn);
const compareFn = (a: T, b: T) => sortingFn(sortCtx, a, b);
const sortedData = data.sort(compareFn);
const shiftOrder = (key?: keyof T) => {
const orders = ['asc', 'desc', 'none'] as const;
@@ -22,22 +22,15 @@ import {
import { FilterList } from '../filter';
import * as styles from './collection-list.css';
type CreateCollectionProps = {
interface EditCollectionModelProps {
init?: Collection;
title?: string;
init: Collection;
onConfirm: (collection: Collection) => void;
onConfirmText?: string;
open: boolean;
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
};
type SaveCollectionButtonProps = {
onConfirm: (collection: Collection) => Promise<void>;
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
filterList: Filter[];
workspaceId: string;
};
onClose: () => void;
onConfirm: (view: Collection) => Promise<void>;
}
export const EditCollectionModel = ({
init,
@@ -47,15 +40,7 @@ export const EditCollectionModel = ({
getPageInfo,
propertiesMeta,
title,
}: {
init?: Collection;
onConfirm: (view: Collection) => Promise<void>;
open: boolean;
onClose: () => void;
title?: string;
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
}) => {
}: EditCollectionModelProps) => {
const t = useAFFiNEI18N();
const onConfirmOnCollection = useCallback(
(view: Collection) => {
@@ -95,47 +80,46 @@ export const EditCollectionModel = ({
);
};
const Page = ({
id,
onClick,
getPageInfo,
}: {
interface PageProps {
id: string;
onClick: (id: string) => void;
getPageInfo: GetPageInfoById;
}) => {
onClick: (id: string) => void;
}
const Page = ({ id, onClick, getPageInfo }: PageProps) => {
const page = getPageInfo(id);
if (!page) {
return null;
}
const icon = page.isEdgeless ? (
<EdgelessIcon
style={{
width: 17.5,
height: 17.5,
}}
/>
) : (
<PageIcon
style={{
width: 17.5,
height: 17.5,
}}
/>
);
const click = () => {
onClick(id);
};
const handleClick = useCallback(() => onClick(id), [id, onClick]);
return (
<div className={styles.pageContainer}>
<div className={styles.pageIcon}>{icon}</div>
<div className={styles.pageTitle}>{page.title}</div>
<div onClick={click} className={styles.deleteIcon}>
<RemoveIcon />
</div>
</div>
<>
{page ? (
<div className={styles.pageContainer}>
<div className={styles.pageIcon}>
{page.isEdgeless ? (
<EdgelessIcon style={{ width: 17.5, height: 17.5 }} />
) : (
<PageIcon style={{ width: 17.5, height: 17.5 }} />
)}
</div>
<div className={styles.pageTitle}>{page.title}</div>
<div onClick={handleClick} className={styles.deleteIcon}>
<RemoveIcon />
</div>
</div>
) : null}
</>
);
};
interface EditCollectionProps {
title?: string;
onConfirmText?: string;
init: Collection;
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
onCancel: () => void;
onConfirm: (collection: Collection) => void;
}
export const EditCollection = ({
title,
init,
@@ -144,9 +128,7 @@ export const EditCollection = ({
onConfirmText,
getPageInfo,
propertiesMeta,
}: CreateCollectionProps & {
onCancel: () => void;
}) => {
}: EditCollectionProps) => {
const t = useAFFiNEI18N();
const [value, onChange] = useState<Collection>(init);
const removeFromExcludeList = useCallback(
@@ -223,13 +205,8 @@ export const EditCollection = ({
<FilterList
propertiesMeta={propertiesMeta}
value={value.filterList}
onChange={list =>
onChange({
...value,
filterList: list,
})
}
></FilterList>
onChange={filterList => onChange({ ...value, filterList })}
/>
{value.allowList ? (
<div className={styles.allowList}>
<div className={styles.allowTitle}>With follow pages:</div>
@@ -253,12 +230,7 @@ export const EditCollection = ({
data-testid="input-collection-title"
placeholder={t['Untitled Collection']()}
defaultValue={value.name}
onChange={text =>
onChange({
...value,
name: text,
})
}
onChange={name => onChange({ ...value, name })}
/>
</div>
</ScrollableContainer>
@@ -288,6 +260,15 @@ export const EditCollection = ({
</div>
);
};
interface SaveCollectionButtonProps {
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
filterList: Filter[];
workspaceId: string;
onConfirm: (collection: Collection) => Promise<void>;
}
export const SaveCollectionButton = ({
onConfirm,
getPageInfo,
@@ -316,10 +297,10 @@ export const SaveCollectionButton = ({
size="large"
style={{ padding: '7px 8px' }}
>
{t['Save As New Collection']()}
{t['Save as New Collection']()}
</Button>
<EditCollectionModel
title={t['Save As New Collection']()}
title={t['Save as New Collection']()}
propertiesMeta={propertiesMeta}
init={init}
onConfirm={onConfirm}
@@ -10,9 +10,13 @@ import {
useSensor,
useSensors,
} from '@dnd-kit/core';
import { SortableContext, useSortable } from '@dnd-kit/sortable';
import {
restrictToParentElement,
restrictToVerticalAxis,
} from '@dnd-kit/modifiers';
import { arrayMove, SortableContext, useSortable } from '@dnd-kit/sortable';
import type { CSSProperties } from 'react';
import { useMemo } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { WorkspaceCard } from '../../components/card/workspace-card';
import { workspaceItemStyle } from './index.css';
@@ -31,18 +35,20 @@ interface SortableWorkspaceItemProps extends Omit<WorkspaceListProps, 'items'> {
}
const SortableWorkspaceItem = (props: SortableWorkspaceItemProps) => {
const { setNodeRef, attributes, listeners, transform } = useSortable({
id: props.item.id,
});
const { setNodeRef, attributes, listeners, transform, transition } =
useSortable({
id: props.item.id,
});
const style: CSSProperties = useMemo(
() => ({
transform: transform
? `translate3d(${transform.x}px, ${transform.y}px, 0)`
: undefined,
transition,
pointerEvents: props.disabled ? 'none' : undefined,
opacity: props.disabled ? 0.6 : undefined,
}),
[props.disabled, transform]
[props.disabled, transform, transition]
);
return (
<div
@@ -63,6 +69,8 @@ const SortableWorkspaceItem = (props: SortableWorkspaceItemProps) => {
);
};
const modifiers = [restrictToParentElement, restrictToVerticalAxis];
export const WorkspaceList = (props: WorkspaceListProps) => {
const sensors = useSensors(
useSensor(PointerSensor, {
@@ -71,10 +79,33 @@ export const WorkspaceList = (props: WorkspaceListProps) => {
},
})
);
const workspaceList = props.items;
const [optimisticList, setOptimisticList] = useState(workspaceList);
useEffect(() => {
setOptimisticList(workspaceList);
}, [workspaceList]);
const onDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
setOptimisticList(workspaceList => {
const oldIndex = workspaceList.findIndex(w => w.id === active.id);
const newIndex = workspaceList.findIndex(w => w.id === over?.id);
const newList = arrayMove(workspaceList, oldIndex, newIndex);
return newList;
});
props.onDragEnd(event);
}
},
[props]
);
return (
<DndContext sensors={sensors} onDragEnd={props.onDragEnd}>
<SortableContext items={props.items}>
{props.items.map(item => (
<DndContext sensors={sensors} onDragEnd={onDragEnd} modifiers={modifiers}>
<SortableContext items={optimisticList}>
{optimisticList.map(item => (
<SortableWorkspaceItem {...props} item={item} key={item.id} />
))}
</SortableContext>
+19 -19
View File
@@ -1,5 +1,6 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { useCallback, useMemo } from 'react';
import type { ModalProps } from '../modal';
import { Modal, ModalCloseButton } from '../modal';
@@ -39,33 +40,36 @@ export const Confirm = ({
confirmButtonTestId = '',
}: ConfirmProps) => {
const t = useAFFiNEI18N();
const cancelText_ = useMemo<string>(() => {
return cancelText === 'Cancel' ? t['Cancel']() : cancelText;
}, [cancelText, t]);
const handleCancel = useCallback(() => {
onCancel?.();
}, [onCancel]);
const handleConfirm = useCallback(() => {
onConfirm?.();
}, [onConfirm]);
return (
<Modal open={open} disablePortal={false}>
<StyledModalWrapper>
<ModalCloseButton
onClick={() => {
onCancel?.();
}}
/>
<ModalCloseButton onClick={handleCancel} />
<StyledConfirmTitle>{title}</StyledConfirmTitle>
<StyledConfirmContent>{content}</StyledConfirmContent>
{buttonDirection === 'row' ? (
<StyledRowButtonWrapper>
<Button
onClick={() => {
onCancel?.();
}}
onClick={handleCancel}
size="large"
style={{ marginRight: '24px' }}
data-testid={cancelButtonTestId}
>
{cancelText === 'Cancel' ? t['Cancel']() : cancelText}
{cancelText_}
</Button>
<Button
type={confirmType}
onClick={() => {
onConfirm?.();
}}
onClick={handleConfirm}
size="large"
data-testid={confirmButtonTestId}
>
@@ -76,18 +80,14 @@ export const Confirm = ({
<StyledColumnButtonWrapper>
<Button
type={confirmType}
onClick={() => {
onConfirm?.();
}}
onClick={handleConfirm}
style={{ width: '284px', height: '38px', textAlign: 'center' }}
data-testid={confirmButtonTestId}
>
{confirmText}
</Button>
<Button
onClick={() => {
onCancel?.();
}}
onClick={handleCancel}
style={{
marginTop: '16px',
width: '284px',
@@ -96,7 +96,7 @@ export const Confirm = ({
}}
data-testid={cancelButtonTestId}
>
{cancelText === 'Cancel' ? t['Cancel']() : cancelText}
{cancelText_}
</Button>
</StyledColumnButtonWrapper>
)}
+1
View File
@@ -5,6 +5,7 @@
"sourceRoot": "packages/i18n/src",
"targets": {
"build": {
"outputs": ["{workspaceRoot}/src/i18n-generated.ts"],
"executor": "nx:run-commands",
"options": {
"cwd": "packages/i18n",
+1 -1
View File
@@ -237,7 +237,7 @@
"Retain cached cloud data": "Retain cached cloud data",
"Retain local cached data": "Retain local cached data",
"Save": "Save",
"Save As New Collection": "Save As New Collection",
"Save as New Collection": "Save as New Collection",
"Saved then enable AFFiNE Cloud": "All changes are saved locally, click to enable AFFiNE Cloud.",
"Select": "Select",
"Select All": "Select All",
+1 -1
View File
@@ -237,7 +237,7 @@
"Retain cached cloud data": "Conserver les données mises en cache dans le cloud",
"Retain local cached data": "Conserver les données du cache local",
"Save": "Enregistrer",
"Save As New Collection": "Enregistrer en tant que nouvelle collection",
"Save as New Collection": "Enregistrer en tant que nouvelle collection",
"Saved then enable AFFiNE Cloud": "Toutes les modifications sont sauvegardées localement, cliquez ici pour activer la sauvegarde AFFiNE Cloud",
"Select": "Sélectionner ",
"Select All": "Sélectionner l'ensemble",
+1 -1
View File
@@ -237,7 +237,7 @@
"Retain cached cloud data": "保留緩衝的雲數據",
"Retain local cached data": "保留本機緩衝數據",
"Save": "保存",
"Save As New Collection": "另存為新收藏集",
"Save as New Collection": "另存為新收藏集",
"Saved then enable AFFiNE Cloud": "所有修訂已儲存在本機,點按以啟用 AFFiNE Cloud。",
"Select": "選擇",
"Select All": "全選",
+1 -1
View File
@@ -10,7 +10,7 @@
"tsconfig.json"
],
"dependencies": {
"@endo/static-module-record": "^0.8.1",
"@endo/static-module-record": "^0.8.2",
"@swc/core": "^1.3.77",
"@toeverything/infra": "workspace:^",
"@vanilla-extract/rollup-plugin": "^1.2.2",
+1 -1
View File
@@ -215,7 +215,7 @@
}
},
{
"insert": " feely."
"insert": " freely."
}
],
"prop:checked": false
+8 -1
View File
@@ -50,10 +50,17 @@ export const HeaderItem = ({
const [open, setOpen] = useState(false);
const pushLayout = useSetAtom(pushLayoutAtom);
const deleteLayout = useSetAtom(deleteLayoutAtom);
const [container, setContainer] = useState<HTMLButtonElement | null>(null);
return (
<Tooltip content={`${open ? 'Collapse' : 'Expand'} table of contents`}>
<Tooltip
content={`${open ? 'Collapse' : 'Expand'} table of contents`}
portalOptions={{
container,
}}
>
<IconButton
ref={setContainer}
onClick={useCallback(() => {
if (!open) {
setOpen(true);
+31 -17
View File
@@ -143,6 +143,7 @@ __metadata:
"@blocksuite/lit": 0.0.0-20230830111255-92eab248-nightly
"@blocksuite/store": 0.0.0-20230830111255-92eab248-nightly
"@dnd-kit/core": ^6.0.8
"@dnd-kit/modifiers": ^6.0.1
"@dnd-kit/sortable": ^7.0.2
"@emotion/cache": ^11.11.0
"@emotion/react": ^11.11.1
@@ -274,7 +275,7 @@ __metadata:
react-resizable-panels: ^0.0.55
react-router-dom: ^6.15.0
rxjs: ^7.8.1
ses: ^0.18.7
ses: ^0.18.8
source-map-loader: ^4.0.1
style-loader: ^3.3.3
swc-loader: ^0.2.3
@@ -573,7 +574,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@affine/plugin-cli@workspace:packages/plugin-cli"
dependencies:
"@endo/static-module-record": ^0.8.1
"@endo/static-module-record": ^0.8.2
"@swc/core": ^1.3.77
"@toeverything/infra": "workspace:^"
"@vanilla-extract/rollup-plugin": ^1.2.2
@@ -723,7 +724,7 @@ __metadata:
react: 18.2.0
react-dom: 18.2.0
serve: ^14.2.0
ses: ^0.18.7
ses: ^0.18.8
storybook: ^7.3.1
storybook-addon-react-router-v6: ^2.0.4
storybook-dark-mode: ^3.0.1
@@ -3861,6 +3862,19 @@ __metadata:
languageName: node
linkType: hard
"@dnd-kit/modifiers@npm:^6.0.1":
version: 6.0.1
resolution: "@dnd-kit/modifiers@npm:6.0.1"
dependencies:
"@dnd-kit/utilities": ^3.2.1
tslib: ^2.0.0
peerDependencies:
"@dnd-kit/core": ^6.0.6
react: ">=16.8.0"
checksum: cd31715aac81baa2398558dc7c877a483d1c4c41cdb2f466557fdc41bb742db5cd1b34b3b84332b94ac4a2835e7e5a0e28c64e621936e976399788f1dd029ea4
languageName: node
linkType: hard
"@dnd-kit/sortable@npm:^7.0.2":
version: 7.0.2
resolution: "@dnd-kit/sortable@npm:7.0.2"
@@ -4411,23 +4425,23 @@ __metadata:
languageName: node
linkType: hard
"@endo/env-options@npm:^0.1.3":
version: 0.1.3
resolution: "@endo/env-options@npm:0.1.3"
checksum: da8c66865d4d30b0053a00960657dc36f022975a888f0dd6a2f6bb37b9fe731f45a02a2cf263d93b1a40fcb37b25f8ba7076cb8af9e93fd95f496365d9382930
"@endo/env-options@npm:^0.1.4":
version: 0.1.4
resolution: "@endo/env-options@npm:0.1.4"
checksum: 6099f0a6b700a60bee7b226aa2a39bb5748e22f25e9606d70e5a66a8e62cbd8c972b0fe578735a658f80bf2ebece62e28c20aa3f16417cbfe6c19a8689966dd3
languageName: node
linkType: hard
"@endo/static-module-record@npm:^0.8.1":
version: 0.8.1
resolution: "@endo/static-module-record@npm:0.8.1"
"@endo/static-module-record@npm:^0.8.2":
version: 0.8.2
resolution: "@endo/static-module-record@npm:0.8.2"
dependencies:
"@agoric/babel-generator": ^7.17.6
"@babel/parser": ^7.17.3
"@babel/traverse": ^7.17.3
"@babel/types": ^7.17.0
ses: ^0.18.7
checksum: c9dc18a5b8ec13008784e30aa15214a67771c325759b42d54362f891d0328b0abf4cafc7be8fa17d539f5f7ca28b9555127e22e9596f5e52d9bf7f3fa90c7963
ses: ^0.18.8
checksum: 0e65f28e95d4ddd495de4cc7e5f0a47a70b89ba5449660158c8813121ab76aa3a8db7e6b2bb9efd3cb24833f8613a952e3a85a8af9fc9b7e4ad49d1d47a9f06c
languageName: node
linkType: hard
@@ -29770,12 +29784,12 @@ __metadata:
languageName: node
linkType: hard
"ses@npm:^0.18.7":
version: 0.18.7
resolution: "ses@npm:0.18.7"
"ses@npm:^0.18.8":
version: 0.18.8
resolution: "ses@npm:0.18.8"
dependencies:
"@endo/env-options": ^0.1.3
checksum: 75ac014771d9bc1f747193c6d0f9e7d2d7700a10311ba8d805d9bc78d4c20d4ef40537f0535b1ea6abf06babf67e70f8bd37b2ad68ad54992a0c5ce842181c87
"@endo/env-options": ^0.1.4
checksum: d7976d2ee218baec021c5cfdfb193d63b52bf2b6cbdbbb90c19d835915a1872b6924910f7fd42bc849eb2de78fc7bdd6e7b4667e1df3c79244cc92d4ede48aa6
languageName: node
linkType: hard