mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-22 07:02:18 +03:00
style: enable no-non-null-assertion rule (#2723)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
This commit is contained in:
parent
1ad2e629ac
commit
18dc427bc3
@ -6,3 +6,4 @@ storybook-static
|
||||
affine-out
|
||||
_next
|
||||
lib
|
||||
.eslintrc.js
|
||||
|
35
.eslintrc.js
35
.eslintrc.js
@ -1,3 +1,5 @@
|
||||
const { readdirSync, statSync } = require('fs');
|
||||
|
||||
const createPattern = packageName => [
|
||||
{
|
||||
group: ['**/dist', '**/dist/**'],
|
||||
@ -21,22 +23,14 @@ const createPattern = packageName => [
|
||||
},
|
||||
];
|
||||
|
||||
const allPackages = [
|
||||
'cli',
|
||||
'component',
|
||||
'debug',
|
||||
'env',
|
||||
'graphql',
|
||||
'hooks',
|
||||
'i18n',
|
||||
'jotai',
|
||||
'native',
|
||||
'plugin-infra',
|
||||
'templates',
|
||||
'theme',
|
||||
'workspace',
|
||||
'y-indexeddb',
|
||||
];
|
||||
const pkgs = readdirSync('./packages').filter(pkg => {
|
||||
return statSync(`./packages/${pkg}`).isDirectory();
|
||||
});
|
||||
const apps = readdirSync('./apps').filter(pkg => {
|
||||
return statSync(`./apps/${pkg}`).isDirectory();
|
||||
});
|
||||
|
||||
const allPackages = pkgs.concat(apps);
|
||||
|
||||
/**
|
||||
* @type {import('eslint').Linter.Config}
|
||||
@ -67,6 +61,7 @@ const config = {
|
||||
},
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.eslint.json',
|
||||
},
|
||||
plugins: [
|
||||
'react',
|
||||
@ -83,7 +78,7 @@ const config = {
|
||||
'no-cond-assign': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
@ -136,6 +131,12 @@ const config = {
|
||||
'@typescript-eslint/no-var-requires': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/__tests__/**/*', '**/*.stories.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
},
|
||||
},
|
||||
...allPackages.map(pkg => ({
|
||||
files: [`packages/${pkg}/src/**/*.ts`, `packages/${pkg}/src/**/*.tsx`],
|
||||
rules: {
|
||||
|
@ -10,9 +10,7 @@ export abstract class BaseSQLiteAdapter {
|
||||
db: SqliteConnection | null = null;
|
||||
abstract role: string;
|
||||
|
||||
constructor(public readonly path: string) {
|
||||
logger.info(`[SQLiteAdapter]`, 'path:', path);
|
||||
}
|
||||
constructor(public readonly path: string) {}
|
||||
|
||||
async connectIfNeeded() {
|
||||
if (!this.db) {
|
||||
|
@ -100,6 +100,7 @@ function getWorkspaceDB$(id: string) {
|
||||
)
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return db$Map.get(id)!;
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,6 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
|
||||
return;
|
||||
}
|
||||
this.firstConnected = true;
|
||||
const { db } = this;
|
||||
|
||||
const onUpstreamUpdate = (update: Uint8Array, origin: YOrigin) => {
|
||||
if (origin === 'renderer') {
|
||||
@ -118,8 +117,8 @@ export class SecondaryWorkspaceSQLiteDB extends BaseSQLiteAdapter {
|
||||
|
||||
const onSelfUpdate = (update: Uint8Array, origin: YOrigin) => {
|
||||
// for self update from upstream, we need to push it to external DB
|
||||
if (origin === 'upstream') {
|
||||
this.addUpdateToUpdateQueue(db!, update);
|
||||
if (origin === 'upstream' && this.db) {
|
||||
this.addUpdateToUpdateQueue(this.db, update);
|
||||
}
|
||||
|
||||
if (origin === 'self') {
|
||||
|
@ -67,7 +67,7 @@ export const registerUpdater = async () => {
|
||||
// register events for checkForUpdatesAndNotify
|
||||
_autoUpdater.on('update-available', info => {
|
||||
if (allowAutoUpdate) {
|
||||
_autoUpdater!.downloadUpdate();
|
||||
_autoUpdater?.downloadUpdate();
|
||||
logger.info('Update available, downloading...', info);
|
||||
}
|
||||
updaterSubjects.updateAvailable.next({
|
||||
|
@ -14,7 +14,7 @@ export function getRequestResponseFromContext(context: ExecutionContext) {
|
||||
}>();
|
||||
return {
|
||||
req: gqlContext.req,
|
||||
res: gqlContext.req.res!,
|
||||
res: gqlContext.req.res,
|
||||
};
|
||||
}
|
||||
case 'http': {
|
||||
@ -37,7 +37,7 @@ export function getRequestResponseFromHost(host: ArgumentsHost) {
|
||||
}>();
|
||||
return {
|
||||
req: gqlContext.req,
|
||||
res: gqlContext.req.res!,
|
||||
res: gqlContext.req.res,
|
||||
};
|
||||
}
|
||||
case 'http': {
|
||||
|
@ -4,7 +4,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CloseIcon, NewIcon, ResetIcon } from '@blocksuite/icons';
|
||||
import clsx from 'clsx';
|
||||
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
||||
import { startTransition } from 'react';
|
||||
import { startTransition, useCallback } from 'react';
|
||||
|
||||
import * as styles from './index.css';
|
||||
import {
|
||||
@ -50,53 +50,67 @@ export function AppUpdaterButton({ className, style }: AddPageButtonProps) {
|
||||
const downloadProgress = useAtomValue(downloadProgressAtom);
|
||||
const setChangelogCheckAtom = useSetAtom(changelogCheckedAtom);
|
||||
|
||||
const onDismissCurrentChangelog = () => {
|
||||
const onDismissCurrentChangelog = useCallback(() => {
|
||||
if (!currentVersion) {
|
||||
return;
|
||||
}
|
||||
startTransition(() =>
|
||||
setChangelogCheckAtom(mapping => {
|
||||
return {
|
||||
...mapping,
|
||||
[currentVersion!]: true,
|
||||
[currentVersion]: true,
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
}, [currentVersion, setChangelogCheckAtom]);
|
||||
const onClickUpdate = useCallback(() => {
|
||||
if (updateReady) {
|
||||
window.apis?.updater.quitAndInstall();
|
||||
} else if (updateAvailable) {
|
||||
if (updateAvailable.allowAutoUpdate) {
|
||||
// wait for download to finish
|
||||
} else {
|
||||
window.open(
|
||||
`https://github.com/toeverything/AFFiNE/releases/tag/v${currentVersion}`,
|
||||
'_blank'
|
||||
);
|
||||
}
|
||||
} else if (currentChangelogUnread) {
|
||||
window.open(config.changelogUrl, '_blank');
|
||||
onDismissCurrentChangelog();
|
||||
} else {
|
||||
throw new Unreachable();
|
||||
}
|
||||
}, [
|
||||
currentChangelogUnread,
|
||||
currentVersion,
|
||||
onDismissCurrentChangelog,
|
||||
updateAvailable,
|
||||
updateReady,
|
||||
]);
|
||||
|
||||
if (!updateAvailable && !currentChangelogUnread) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const updateAvailableNode = updateAvailable
|
||||
? updateAvailable.allowAutoUpdate
|
||||
? renderUpdateAvailableAllowAutoUpdate()
|
||||
: renderUpdateAvailableNotAllowAutoUpdate()
|
||||
: null;
|
||||
const whatsNew =
|
||||
!updateAvailable && currentChangelogUnread ? renderWhatsNew() : null;
|
||||
|
||||
return (
|
||||
<button
|
||||
style={style}
|
||||
className={clsx([styles.root, className])}
|
||||
data-has-update={updateAvailable ? 'true' : 'false'}
|
||||
data-disabled={updateAvailable?.allowAutoUpdate && !updateReady}
|
||||
onClick={() => {
|
||||
if (updateReady) {
|
||||
window.apis?.updater.quitAndInstall();
|
||||
} else if (updateAvailable) {
|
||||
if (updateAvailable.allowAutoUpdate) {
|
||||
// wait for download to finish
|
||||
} else {
|
||||
window.open(
|
||||
`https://github.com/toeverything/AFFiNE/releases/tag/v${currentVersion}`,
|
||||
'_blank'
|
||||
);
|
||||
}
|
||||
} else if (currentChangelogUnread) {
|
||||
window.open(config.changelogUrl, '_blank');
|
||||
onDismissCurrentChangelog();
|
||||
} else {
|
||||
throw new Unreachable();
|
||||
}
|
||||
}}
|
||||
onClick={onClickUpdate}
|
||||
>
|
||||
{updateAvailable &&
|
||||
(updateAvailable.allowAutoUpdate
|
||||
? renderUpdateAvailableAllowAutoUpdate()
|
||||
: renderUpdateAvailableNotAllowAutoUpdate())}
|
||||
|
||||
{!updateAvailable && currentChangelogUnread && renderWhatsNew()}
|
||||
{updateAvailableNode}
|
||||
{whatsNew}
|
||||
<div className={styles.particles} aria-hidden="true"></div>
|
||||
<span className={styles.halo} aria-hidden="true"></span>
|
||||
</button>
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
ExportToMarkdownIcon,
|
||||
ExportToPdfIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import { useRef } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
|
||||
import { Menu, MenuItem } from '../../..';
|
||||
import type { CommonMenuItemProps } from './types';
|
||||
@ -18,35 +18,39 @@ const ExportToPdfMenuItem = ({
|
||||
}: CommonMenuItemProps<{ type: 'pdf' }>) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const contentParserRef = useRef<ContentParser>();
|
||||
return (
|
||||
<>
|
||||
{globalThis.currentEditor!.mode === 'page' && (
|
||||
<MenuItem
|
||||
data-testid="export-to-pdf"
|
||||
onClick={async () => {
|
||||
if (!contentParserRef.current) {
|
||||
contentParserRef.current = new ContentParser(
|
||||
globalThis.currentEditor!.page
|
||||
);
|
||||
}
|
||||
const result = await window.apis?.export.savePDFFileAs(
|
||||
(
|
||||
globalThis.currentEditor!.page.root as PageBlockModel
|
||||
).title.toString()
|
||||
);
|
||||
if (result !== undefined) {
|
||||
return;
|
||||
}
|
||||
contentParserRef.current.exportPdf();
|
||||
onSelect?.({ type: 'pdf' });
|
||||
}}
|
||||
icon={<ExportToPdfIcon />}
|
||||
>
|
||||
{t['Export to PDF']()}
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const { currentEditor } = globalThis;
|
||||
const onClickDownloadPDF = useCallback(() => {
|
||||
if (!currentEditor) {
|
||||
return;
|
||||
}
|
||||
const contentParser =
|
||||
contentParserRef.current ??
|
||||
(contentParserRef.current = new ContentParser(currentEditor.page));
|
||||
|
||||
window.apis?.export
|
||||
.savePDFFileAs(
|
||||
(currentEditor.page.root as PageBlockModel).title.toString()
|
||||
)
|
||||
.then(result => {
|
||||
if (result !== undefined) {
|
||||
return;
|
||||
}
|
||||
contentParser.exportPdf();
|
||||
onSelect?.({ type: 'pdf' });
|
||||
});
|
||||
}, [currentEditor, onSelect]);
|
||||
if (currentEditor && currentEditor.mode === 'page') {
|
||||
return (
|
||||
<MenuItem
|
||||
data-testid="export-to-pdf"
|
||||
onClick={onClickDownloadPDF}
|
||||
icon={<ExportToPdfIcon />}
|
||||
>
|
||||
{t['Export to PDF']()}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const ExportToHtmlMenuItem = ({
|
||||
@ -54,19 +58,22 @@ const ExportToHtmlMenuItem = ({
|
||||
}: CommonMenuItemProps<{ type: 'html' }>) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const contentParserRef = useRef<ContentParser>();
|
||||
const { currentEditor } = globalThis;
|
||||
const onClickExportHtml = useCallback(() => {
|
||||
if (!currentEditor) {
|
||||
return;
|
||||
}
|
||||
if (!contentParserRef.current) {
|
||||
contentParserRef.current = new ContentParser(currentEditor.page);
|
||||
}
|
||||
contentParserRef.current.exportHtml();
|
||||
onSelect?.({ type: 'html' });
|
||||
}, [onSelect, currentEditor]);
|
||||
return (
|
||||
<>
|
||||
<MenuItem
|
||||
data-testid="export-to-html"
|
||||
onClick={() => {
|
||||
if (!contentParserRef.current) {
|
||||
contentParserRef.current = new ContentParser(
|
||||
globalThis.currentEditor!.page
|
||||
);
|
||||
}
|
||||
contentParserRef.current.exportHtml();
|
||||
onSelect?.({ type: 'html' });
|
||||
}}
|
||||
onClick={onClickExportHtml}
|
||||
icon={<ExportToHtmlIcon />}
|
||||
>
|
||||
{t['Export to HTML']()}
|
||||
@ -108,19 +115,22 @@ const ExportToMarkdownMenuItem = ({
|
||||
}: CommonMenuItemProps<{ type: 'markdown' }>) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const contentParserRef = useRef<ContentParser>();
|
||||
const { currentEditor } = globalThis;
|
||||
const onClickExportMarkdown = useCallback(() => {
|
||||
if (!currentEditor) {
|
||||
return;
|
||||
}
|
||||
if (!contentParserRef.current) {
|
||||
contentParserRef.current = new ContentParser(currentEditor.page);
|
||||
}
|
||||
contentParserRef.current.exportMarkdown();
|
||||
onSelect?.({ type: 'markdown' });
|
||||
}, [onSelect, currentEditor]);
|
||||
return (
|
||||
<>
|
||||
<MenuItem
|
||||
data-testid="export-to-markdown"
|
||||
onClick={() => {
|
||||
if (!contentParserRef.current) {
|
||||
contentParserRef.current = new ContentParser(
|
||||
globalThis.currentEditor!.page
|
||||
);
|
||||
}
|
||||
contentParserRef.current.exportMarkdown();
|
||||
onSelect?.({ type: 'markdown' });
|
||||
}}
|
||||
onClick={onClickExportMarkdown}
|
||||
icon={<ExportToMarkdownIcon />}
|
||||
>
|
||||
{t['Export to Markdown']()}
|
||||
|
@ -12,7 +12,7 @@ import { useCallback, useState } from 'react';
|
||||
import useCollapsed from './hooks/use-collapsed';
|
||||
import useSelectWithKeyboard from './hooks/use-select-with-keyboard';
|
||||
import { TreeNode, TreeNodeWithDnd } from './tree-node';
|
||||
import type { TreeViewProps } from './types';
|
||||
import type { Node, TreeViewProps } from './types';
|
||||
import { findNode } from './utils';
|
||||
export const TreeView = <RenderProps,>({
|
||||
data,
|
||||
@ -58,6 +58,39 @@ export const TreeView = <RenderProps,>({
|
||||
setDraggingId(e.active.id as string);
|
||||
}, []);
|
||||
if (enableDnd) {
|
||||
const treeNodes = data.map((node, index) => (
|
||||
<TreeNodeWithDnd
|
||||
key={node.id}
|
||||
index={index}
|
||||
collapsedIds={collapsedIds}
|
||||
setCollapsed={setCollapsed}
|
||||
node={node}
|
||||
selectedId={selectedId}
|
||||
enableDnd={enableDnd}
|
||||
disableCollapse={disableCollapse}
|
||||
draggingId={draggingId}
|
||||
{...otherProps}
|
||||
/>
|
||||
));
|
||||
const draggingNode = (function () {
|
||||
let draggingNode: Node<RenderProps> | undefined;
|
||||
if (draggingId) {
|
||||
draggingNode = findNode(draggingId, data);
|
||||
}
|
||||
if (draggingNode) {
|
||||
return (
|
||||
<TreeNode
|
||||
node={draggingNode}
|
||||
index={0}
|
||||
allowDrop={false}
|
||||
collapsedIds={collapsedIds}
|
||||
setCollapsed={() => {}}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
@ -65,33 +98,8 @@ export const TreeView = <RenderProps,>({
|
||||
onDragMove={onDragMove}
|
||||
onDragEnd={onDragEnd}
|
||||
>
|
||||
{data.map((node, index) => (
|
||||
<TreeNodeWithDnd
|
||||
key={node.id}
|
||||
index={index}
|
||||
collapsedIds={collapsedIds}
|
||||
setCollapsed={setCollapsed}
|
||||
node={node}
|
||||
selectedId={selectedId}
|
||||
enableDnd={enableDnd}
|
||||
disableCollapse={disableCollapse}
|
||||
draggingId={draggingId}
|
||||
{...otherProps}
|
||||
/>
|
||||
))}
|
||||
|
||||
<DragOverlay>
|
||||
{draggingId && (
|
||||
<TreeNode
|
||||
node={findNode(draggingId, data)!}
|
||||
index={0}
|
||||
allowDrop={false}
|
||||
collapsedIds={collapsedIds}
|
||||
setCollapsed={() => {}}
|
||||
{...otherProps}
|
||||
/>
|
||||
)}
|
||||
</DragOverlay>
|
||||
{treeNodes}
|
||||
<DragOverlay>{draggingNode}</DragOverlay>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
|
@ -46,14 +46,15 @@ export function useAffineListener(
|
||||
if (!fnRef.current) {
|
||||
fnRef.current = listener;
|
||||
}
|
||||
const ipcListener = fnRef.current ?? (fnRef.current = listener);
|
||||
useEffect(() => {
|
||||
if (once) {
|
||||
window.affine.ipcRenderer.once(channel, fnRef.current!);
|
||||
window.affine.ipcRenderer.once(channel, ipcListener);
|
||||
} else {
|
||||
window.affine.ipcRenderer.on(channel, fnRef.current!);
|
||||
window.affine.ipcRenderer.on(channel, ipcListener);
|
||||
}
|
||||
return () => {
|
||||
window.affine.ipcRenderer.removeListener(channel, fnRef.current!);
|
||||
window.affine.ipcRenderer.removeListener(channel, ipcListener);
|
||||
};
|
||||
}, [channel, once]);
|
||||
}, [channel, once, ipcListener]);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ export const createBroadCastChannelProvider = (
|
||||
case 'doc:diff': {
|
||||
const [, diff, clientId] = event.data;
|
||||
const update = Y.encodeStateAsUpdate(doc, diff);
|
||||
broadcastChannel!.postMessage(['doc:update', update, clientId]);
|
||||
broadcastChannel?.postMessage(['doc:update', update, clientId]);
|
||||
break;
|
||||
}
|
||||
case 'doc:update': {
|
||||
@ -47,7 +47,7 @@ export const createBroadCastChannelProvider = (
|
||||
const [, clientId] = event.data;
|
||||
const clients = getClients(awareness);
|
||||
const update = encodeAwarenessUpdate(awareness, clients);
|
||||
broadcastChannel!.postMessage(['awareness:update', update, clientId]);
|
||||
broadcastChannel?.postMessage(['awareness:update', update, clientId]);
|
||||
break;
|
||||
}
|
||||
case 'awareness:update': {
|
||||
|
@ -162,8 +162,7 @@ const sqliteOrigin = Symbol('sqlite-provider-origin');
|
||||
const createSQLiteProvider = (
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace
|
||||
): SQLiteProvider => {
|
||||
const apis = window.apis!;
|
||||
const events = window.events!;
|
||||
const { apis, events } = window;
|
||||
// make sure it is being used in Electron with APIs
|
||||
assertExists(apis);
|
||||
assertExists(events);
|
||||
@ -216,7 +215,7 @@ const createSQLiteProvider = (
|
||||
const createSQLiteDBDownloadProvider = (
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace
|
||||
): SQLiteDBDownloadProvider => {
|
||||
const apis = window.apis!;
|
||||
const { apis } = window;
|
||||
let disconnected = false;
|
||||
|
||||
let _resolve: () => void;
|
||||
@ -273,7 +272,7 @@ const createSQLiteDBDownloadProvider = (
|
||||
return;
|
||||
}
|
||||
|
||||
return window.apis?.db.addBlob(
|
||||
return apis?.db.addBlob(
|
||||
blockSuiteWorkspace.id,
|
||||
k,
|
||||
new Uint8Array(await blob.arrayBuffer())
|
||||
|
@ -65,9 +65,10 @@ export function createEmptyBlockSuiteWorkspace(
|
||||
const blobStorages: StoreOptions['blobStorages'] = [];
|
||||
|
||||
if (flavour === WorkspaceFlavour.AFFINE) {
|
||||
blobStorages.push(id =>
|
||||
createAffineBlobStorage(id, config!.workspaceApis!)
|
||||
);
|
||||
if (config && config.workspaceApis) {
|
||||
const workspaceApis = config.workspaceApis;
|
||||
blobStorages.push(id => createAffineBlobStorage(id, workspaceApis));
|
||||
}
|
||||
} else {
|
||||
if (typeof window !== 'undefined') {
|
||||
blobStorages.push(createIndexeddbStorage);
|
||||
|
@ -47,8 +47,8 @@ const handleEnter = ({
|
||||
null
|
||||
>;
|
||||
const vEditor = getVirgoByModel(blockRange.models[0]);
|
||||
const linkInfo = vEditor!
|
||||
.getDeltasByVRange({
|
||||
const linkInfo = vEditor
|
||||
?.getDeltasByVRange({
|
||||
index: blockRange.startOffset,
|
||||
length: 0,
|
||||
})
|
||||
@ -70,7 +70,7 @@ const handleEnter = ({
|
||||
currentBlockIndex + 1
|
||||
);
|
||||
|
||||
vEditor!.deleteText({
|
||||
vEditor?.deleteText({
|
||||
index,
|
||||
length,
|
||||
});
|
||||
|
16
tsconfig.eslint.json
Normal file
16
tsconfig.eslint.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": [
|
||||
"target",
|
||||
"node_modules",
|
||||
"dist",
|
||||
"lib",
|
||||
".coverage",
|
||||
".yarn",
|
||||
"test-results"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user