mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 20:16:26 +08:00
feat: add new rule for floating promise (#2726)
Co-authored-by: Himself65 <himself65@outlook.com>
(cherry picked from commit bedf838fe5)
This commit is contained in:
@@ -26,9 +26,13 @@ function rpcToObservable<
|
||||
subscriber.complete();
|
||||
return () => {};
|
||||
}
|
||||
handler?.().then(t => {
|
||||
subscriber.next(t);
|
||||
});
|
||||
handler?.()
|
||||
.then(t => {
|
||||
subscriber.next(t);
|
||||
})
|
||||
.catch(err => {
|
||||
subscriber.error(err);
|
||||
});
|
||||
return event(t => {
|
||||
subscriber.next(t);
|
||||
});
|
||||
|
||||
@@ -92,20 +92,25 @@ const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => {
|
||||
return;
|
||||
}
|
||||
if (page.awarenessStore.getFlag('enable_block_hub')) {
|
||||
editor.createBlockHub().then(blockHub => {
|
||||
if (blockHubRef.current) {
|
||||
blockHubRef.current.remove();
|
||||
}
|
||||
blockHubRef.current = blockHub;
|
||||
const toolWrapper = document.querySelector('#toolWrapper');
|
||||
if (!toolWrapper) {
|
||||
console.warn(
|
||||
'toolWrapper not found, block hub feature will not be available.'
|
||||
);
|
||||
} else {
|
||||
toolWrapper.appendChild(blockHub);
|
||||
}
|
||||
});
|
||||
editor
|
||||
.createBlockHub()
|
||||
.then(blockHub => {
|
||||
if (blockHubRef.current) {
|
||||
blockHubRef.current.remove();
|
||||
}
|
||||
blockHubRef.current = blockHub;
|
||||
const toolWrapper = document.querySelector('#toolWrapper');
|
||||
if (!toolWrapper) {
|
||||
console.warn(
|
||||
'toolWrapper not found, block hub feature will not be available.'
|
||||
);
|
||||
} else {
|
||||
toolWrapper.appendChild(blockHub);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
container.appendChild(editor);
|
||||
|
||||
@@ -52,8 +52,8 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
|
||||
const onClickCreateLink = useCallback(() => {
|
||||
setIsPublic(true);
|
||||
}, [setIsPublic]);
|
||||
const onClickCopyLink = useCallback(() => {
|
||||
navigator.clipboard.writeText(sharingUrl);
|
||||
const onClickCopyLink = useCallback(async () => {
|
||||
await navigator.clipboard.writeText(sharingUrl);
|
||||
toast(t['Copied link to clipboard']());
|
||||
}, [sharingUrl, t]);
|
||||
const onDisablePublic = useCallback(() => {
|
||||
|
||||
@@ -32,8 +32,8 @@ describe('GraphQL fetcher', () => {
|
||||
|
||||
const gql = gqlFetcherFactory('https://example.com/graphql');
|
||||
|
||||
it('should send POST request to given endpoint', () => {
|
||||
gql(
|
||||
it('should send POST request to given endpoint', async () => {
|
||||
await gql(
|
||||
// @ts-expect-error variables is actually optional
|
||||
{ query }
|
||||
);
|
||||
@@ -44,8 +44,8 @@ describe('GraphQL fetcher', () => {
|
||||
expect(ctx.method).toBe('POST');
|
||||
});
|
||||
|
||||
it('should send with correct graphql JSON body', () => {
|
||||
gql({
|
||||
it('should send with correct graphql JSON body', async () => {
|
||||
await gql({
|
||||
query,
|
||||
// @ts-expect-error forgive the fake variables
|
||||
variables: { a: 1, b: '2', c: { d: false } },
|
||||
@@ -63,8 +63,8 @@ describe('GraphQL fetcher', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should correctly ignore nil variables', () => {
|
||||
gql({
|
||||
it('should correctly ignore nil variables', async () => {
|
||||
await gql({
|
||||
query,
|
||||
// @ts-expect-error forgive the fake variables
|
||||
variables: { a: false, b: null, c: undefined },
|
||||
@@ -74,7 +74,7 @@ describe('GraphQL fetcher', () => {
|
||||
'"{\\"query\\":\\"query { field }\\",\\"variables\\":{\\"a\\":false,\\"b\\":null},\\"operationName\\":\\"query\\"}"'
|
||||
);
|
||||
|
||||
gql({
|
||||
await gql({
|
||||
query,
|
||||
// @ts-expect-error forgive the fake variables
|
||||
variables: { a: false, b: null, c: undefined },
|
||||
|
||||
@@ -68,15 +68,23 @@ const standardizeLocale = (language: string) => {
|
||||
|
||||
export const createI18n = () => {
|
||||
const i18n = i18next.createInstance();
|
||||
i18n.use(initReactI18next).init({
|
||||
lng: 'en',
|
||||
fallbackLng,
|
||||
debug: false,
|
||||
resources,
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
});
|
||||
i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng,
|
||||
debug: false,
|
||||
resources,
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
console.info('i18n init success');
|
||||
})
|
||||
.catch(() => {
|
||||
console.error('i18n init failed');
|
||||
});
|
||||
|
||||
i18n.on('languageChanged', lng => {
|
||||
localStorage.setItem(STORAGE_KEY, lng);
|
||||
|
||||
@@ -47,9 +47,9 @@ const getBaseTranslations = async (baseLanguage: { tag: string }) => {
|
||||
|
||||
const main = async () => {
|
||||
try {
|
||||
fs.access(RES_DIR);
|
||||
await fs.access(RES_DIR);
|
||||
} catch (error) {
|
||||
fs.mkdir(RES_DIR);
|
||||
fs.mkdir(RES_DIR).catch(console.error);
|
||||
console.log('Create directory', RES_DIR);
|
||||
}
|
||||
console.log('Loading project languages...');
|
||||
@@ -149,4 +149,7 @@ const main = async () => {
|
||||
console.log('Done');
|
||||
};
|
||||
|
||||
main();
|
||||
main().catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -152,4 +152,7 @@ const main = async () => {
|
||||
// TODO send notification
|
||||
};
|
||||
|
||||
main();
|
||||
main().catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -40,7 +40,10 @@ export function definePlugin<ID extends string>(
|
||||
|
||||
blockSuiteAdapter
|
||||
.load()
|
||||
.then(({ default: adapter }) => updateAdapter(adapter));
|
||||
.then(({ default: adapter }) => updateAdapter(adapter))
|
||||
.catch(err => {
|
||||
pluginLogger.error('[definePlugin] blockSuiteAdapter error', err);
|
||||
});
|
||||
|
||||
if (import.meta.webpackHot) {
|
||||
blockSuiteAdapter.hotModuleReload(async _ => {
|
||||
@@ -64,7 +67,10 @@ export function definePlugin<ID extends string>(
|
||||
|
||||
uiAdapterLoader
|
||||
.load()
|
||||
.then(({ default: adapter }) => updateAdapter(adapter));
|
||||
.then(({ default: adapter }) => updateAdapter(adapter))
|
||||
.catch(err => {
|
||||
pluginLogger.error('[definePlugin] blockSuiteAdapter error', err);
|
||||
});
|
||||
|
||||
if (import.meta.webpackHot) {
|
||||
uiAdapterLoader.hotModuleReload(async _ => {
|
||||
|
||||
@@ -37,6 +37,9 @@ fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
|
||||
},
|
||||
frameId
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to load large-image.png', err);
|
||||
});
|
||||
|
||||
export const Default = () => {
|
||||
|
||||
@@ -54,6 +54,10 @@ fetch(new URL('@affine-test/fixtures/smile.png', import.meta.url))
|
||||
new Blob([buffer], { type: 'image/png' })
|
||||
);
|
||||
avatarBlockSuiteWorkspace.meta.setAvatar(id);
|
||||
})
|
||||
.catch(() => {
|
||||
// just ignore
|
||||
console.error('Failed to load smile.png');
|
||||
});
|
||||
|
||||
export const BlobExample: StoryFn<WorkspaceAvatarProps> = props => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import type { BlobStorage } from '@blocksuite/store';
|
||||
import { createIndexeddbStorage } from '@blocksuite/store';
|
||||
import { openDB } from 'idb';
|
||||
@@ -19,6 +20,8 @@ interface AffineBlob extends DBSchema {
|
||||
// todo: migrate blob storage from `createIndexeddbStorage`
|
||||
}
|
||||
|
||||
const logger = new DebugLogger('affine:blob');
|
||||
|
||||
export const createAffineBlobStorage = (
|
||||
workspaceId: string,
|
||||
workspaceApis: ReturnType<typeof createWorkspaceApis>
|
||||
@@ -29,19 +32,25 @@ export const createAffineBlobStorage = (
|
||||
db.createObjectStore('uploading', { keyPath: 'key' });
|
||||
},
|
||||
});
|
||||
dbPromise.then(async db => {
|
||||
const t = db.transaction('uploading', 'readwrite').objectStore('uploading');
|
||||
await t.getAll().then(blobs =>
|
||||
blobs.map(({ arrayBuffer, type }) =>
|
||||
workspaceApis.uploadBlob(workspaceId, arrayBuffer, type).then(key => {
|
||||
const t = db
|
||||
.transaction('uploading', 'readwrite')
|
||||
.objectStore('uploading');
|
||||
return t.delete(key);
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
dbPromise
|
||||
.then(async db => {
|
||||
const t = db
|
||||
.transaction('uploading', 'readwrite')
|
||||
.objectStore('uploading');
|
||||
await t.getAll().then(blobs =>
|
||||
blobs.map(({ arrayBuffer, type }) =>
|
||||
workspaceApis.uploadBlob(workspaceId, arrayBuffer, type).then(key => {
|
||||
const t = db
|
||||
.transaction('uploading', 'readwrite')
|
||||
.objectStore('uploading');
|
||||
return t.delete(key);
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error('[createAffineBlobStorage] dbPromise error', err);
|
||||
});
|
||||
return {
|
||||
crud: {
|
||||
get: async key => {
|
||||
@@ -60,19 +69,21 @@ export const createAffineBlobStorage = (
|
||||
.transaction('uploading', 'readwrite')
|
||||
.objectStore('uploading');
|
||||
let uploaded = false;
|
||||
t.put({
|
||||
await t.put({
|
||||
key,
|
||||
arrayBuffer,
|
||||
type: value.type,
|
||||
}).then(() => {
|
||||
// delete the uploading blob after uploaded
|
||||
if (uploaded) {
|
||||
const t = db
|
||||
.transaction('uploading', 'readwrite')
|
||||
.objectStore('uploading');
|
||||
t.delete(key);
|
||||
}
|
||||
});
|
||||
// delete the uploading blob after uploaded
|
||||
if (uploaded) {
|
||||
const t = db
|
||||
.transaction('uploading', 'readwrite')
|
||||
.objectStore('uploading');
|
||||
// don't await here, we don't care if it's deleted
|
||||
t.delete(key).catch(err => {
|
||||
logger.error('[createAffineBlobStorage] delete error', err);
|
||||
});
|
||||
}
|
||||
await Promise.all([
|
||||
storage.crud.set(key, value),
|
||||
workspaceApis
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('crud', () => {
|
||||
});
|
||||
|
||||
test('delete not exist', async () => {
|
||||
expect(async () =>
|
||||
await expect(async () =>
|
||||
CRUD.delete({
|
||||
id: 'not_exist',
|
||||
flavour: WorkspaceFlavour.LOCAL,
|
||||
|
||||
@@ -197,7 +197,7 @@ describe('indexeddb provider', () => {
|
||||
test('cleanup when connecting', async () => {
|
||||
const provider = createIndexedDBProvider(workspace.id, workspace.doc);
|
||||
provider.connect();
|
||||
expect(() => provider.cleanup()).rejects.toThrowError(
|
||||
await expect(() => provider.cleanup()).rejects.toThrowError(
|
||||
CleanupWhenConnectingError
|
||||
);
|
||||
await provider.whenSynced;
|
||||
@@ -259,7 +259,7 @@ describe('indexeddb provider', () => {
|
||||
yDoc.getMap().set('foo', 'bar');
|
||||
const persistence = new IndexeddbPersistence('test', yDoc);
|
||||
await persistence.whenSynced;
|
||||
persistence.destroy();
|
||||
await persistence.destroy();
|
||||
}
|
||||
{
|
||||
const yDoc = new Doc();
|
||||
@@ -274,7 +274,7 @@ describe('indexeddb provider', () => {
|
||||
indexedDB.databases = vi.fn(async () => {
|
||||
throw new Error('not supported');
|
||||
});
|
||||
expect(indexedDB.databases).rejects.toThrow('not supported');
|
||||
await expect(indexedDB.databases).rejects.toThrow('not supported');
|
||||
const yDoc = new Doc();
|
||||
expect(indexedDB.databases).toBeCalledTimes(1);
|
||||
const provider = createIndexedDBProvider('test', yDoc);
|
||||
|
||||
@@ -286,7 +286,7 @@ export const createIndexedDBProvider = (
|
||||
if (connected) {
|
||||
throw new CleanupWhenConnectingError();
|
||||
}
|
||||
(await dbPromise).delete('workspace', id);
|
||||
await (await dbPromise).delete('workspace', id);
|
||||
},
|
||||
whenSynced: Promise.resolve(),
|
||||
get connected() {
|
||||
|
||||
Reference in New Issue
Block a user