mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 00:41:50 +03:00
chore: enchance copy link action (#8797)
What's Changed * enhance copy link action * detect compatibility * fall back to `document.execCommand`
This commit is contained in:
parent
b3b1ea2f33
commit
f85dfae63b
@ -5,6 +5,7 @@ import {
|
|||||||
} from '@affine/core/components/hooks/affine/use-share-url';
|
} from '@affine/core/components/hooks/affine/use-share-url';
|
||||||
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
|
import { copyLinkToBlockStdScopeClipboard } from '@affine/core/utils/clipboard';
|
||||||
import { I18n } from '@affine/i18n';
|
import { I18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import type {
|
import type {
|
||||||
@ -63,18 +64,15 @@ function createCopyLinkToBlockMenuItem(
|
|||||||
const type = model.flavour;
|
const type = model.flavour;
|
||||||
const page = editor.editorContainer$.value;
|
const page = editor.editorContainer$.value;
|
||||||
|
|
||||||
page?.host?.std.clipboard
|
copyLinkToBlockStdScopeClipboard(str, page?.host?.std.clipboard)
|
||||||
.writeToClipboard(items => {
|
.then(success => {
|
||||||
items['text/plain'] = str;
|
if (!success) return;
|
||||||
// wrap a link
|
|
||||||
items['text/html'] = `<a href="${str}">${str}</a>`;
|
|
||||||
return items;
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
track.doc.editor.toolbar.copyBlockToLink({ type });
|
|
||||||
notify.success({ title: I18n['Copied link to clipboard']() });
|
notify.success({ title: I18n['Copied link to clipboard']() });
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
|
||||||
|
track.doc.editor.toolbar.copyBlockToLink({ type });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
} from '@affine/core/components/hooks/affine/use-share-url';
|
} from '@affine/core/components/hooks/affine/use-share-url';
|
||||||
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
|
import { copyLinkToBlockStdScopeClipboard } from '@affine/core/utils/clipboard';
|
||||||
import { I18n } from '@affine/i18n';
|
import { I18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import type {
|
import type {
|
||||||
@ -77,7 +78,7 @@ function createCopyLinkToBlockMenuItem(
|
|||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
action: (ctx: MenuContext) => {
|
action: async (ctx: MenuContext) => {
|
||||||
const baseUrl = getAffineCloudBaseUrl();
|
const baseUrl = getAffineCloudBaseUrl();
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
ctx.close();
|
ctx.close();
|
||||||
@ -114,18 +115,16 @@ function createCopyLinkToBlockMenuItem(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.std.clipboard
|
const success = await copyLinkToBlockStdScopeClipboard(
|
||||||
.writeToClipboard(items => {
|
str,
|
||||||
items['text/plain'] = str;
|
ctx.std.clipboard
|
||||||
// wrap a link
|
);
|
||||||
items['text/html'] = `<a href="${str}">${str}</a>`;
|
|
||||||
return items;
|
if (success) {
|
||||||
})
|
notify.success({ title: I18n['Copied link to clipboard']() });
|
||||||
.then(() => {
|
}
|
||||||
track.doc.editor.toolbar.copyBlockToLink({ type });
|
|
||||||
notify.success({ title: I18n['Copied link to clipboard']() });
|
track.doc.editor.toolbar.copyBlockToLink({ type });
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
|
|
||||||
ctx.close();
|
ctx.close();
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { notify } from '@affine/component';
|
import { notify } from '@affine/component';
|
||||||
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
||||||
|
import { copyTextToClipboard } from '@affine/core/utils/clipboard';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import { type EditorHost } from '@blocksuite/affine/block-std';
|
import { type EditorHost } from '@blocksuite/affine/block-std';
|
||||||
@ -145,23 +146,18 @@ export const useSharingUrl = ({ workspaceId, pageId }: UseSharingUrl) => {
|
|||||||
elementIds,
|
elementIds,
|
||||||
});
|
});
|
||||||
if (sharingUrl) {
|
if (sharingUrl) {
|
||||||
navigator.clipboard
|
copyTextToClipboard(sharingUrl)
|
||||||
.writeText(sharingUrl)
|
.then(success => {
|
||||||
.then(() => {
|
if (success) {
|
||||||
notify.success({
|
notify.success({ title: t['Copied link to clipboard']() });
|
||||||
title: t['Copied link to clipboard'](),
|
}
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
track.$.sharePanel.$.copyShareLink({
|
track.$.sharePanel.$.copyShareLink({ type });
|
||||||
type,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
notify.error({
|
notify.error({ title: 'Network not available' });
|
||||||
title: 'Network not available',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[pageId, t, workspaceId]
|
[pageId, t, workspaceId]
|
||||||
|
49
packages/frontend/core/src/utils/clipboard/fake.ts
Normal file
49
packages/frontend/core/src/utils/clipboard/fake.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
const createFakeElement = (text: string) => {
|
||||||
|
const isRTL = document.documentElement.getAttribute('dir') === 'rtl';
|
||||||
|
const fakeElement = document.createElement('textarea');
|
||||||
|
// Prevent zooming on iOS
|
||||||
|
fakeElement.style.fontSize = '12pt';
|
||||||
|
// Reset box model
|
||||||
|
fakeElement.style.border = '0';
|
||||||
|
fakeElement.style.padding = '0';
|
||||||
|
fakeElement.style.margin = '0';
|
||||||
|
// Move element out of screen horizontally
|
||||||
|
fakeElement.style.position = 'absolute';
|
||||||
|
fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px';
|
||||||
|
// Move element to the same position vertically
|
||||||
|
const yPosition = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
fakeElement.style.top = `${yPosition}px`;
|
||||||
|
|
||||||
|
fakeElement.setAttribute('readonly', '');
|
||||||
|
fakeElement.value = text;
|
||||||
|
|
||||||
|
return fakeElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
function command(type: string) {
|
||||||
|
try {
|
||||||
|
return document.execCommand(type);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fakeCopyAction = (text: string, container = document.body) => {
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
const fakeElement = createFakeElement(text);
|
||||||
|
container.append(fakeElement);
|
||||||
|
|
||||||
|
try {
|
||||||
|
fakeElement.select();
|
||||||
|
fakeElement.setSelectionRange(0, fakeElement.value.length);
|
||||||
|
success = command('copy');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeElement.remove();
|
||||||
|
|
||||||
|
return success;
|
||||||
|
};
|
54
packages/frontend/core/src/utils/clipboard/index.ts
Normal file
54
packages/frontend/core/src/utils/clipboard/index.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { type Clipboard as BlockStdScopeClipboard } from '@blocksuite/affine/block-std';
|
||||||
|
|
||||||
|
import { fakeCopyAction } from './fake';
|
||||||
|
|
||||||
|
const clipboardWriteIsSupported =
|
||||||
|
'clipboard' in navigator && 'write' in navigator.clipboard;
|
||||||
|
|
||||||
|
const clipboardWriteTextIsSupported =
|
||||||
|
'clipboard' in navigator && 'writeText' in navigator.clipboard;
|
||||||
|
|
||||||
|
export const copyTextToClipboard = async (text: string) => {
|
||||||
|
// 1. try using Async API first, works on HTTPS domain
|
||||||
|
if (clipboardWriteTextIsSupported) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. try using `document.execCommand`
|
||||||
|
// https://github.com/zenorocha/clipboard.js/blob/master/src/actions/copy.js
|
||||||
|
return fakeCopyAction(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const copyLinkToBlockStdScopeClipboard = async (
|
||||||
|
text: string,
|
||||||
|
clipboard?: BlockStdScopeClipboard
|
||||||
|
) => {
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
if (!clipboard) return success;
|
||||||
|
|
||||||
|
if (clipboardWriteIsSupported) {
|
||||||
|
try {
|
||||||
|
await clipboard.writeToClipboard(items => {
|
||||||
|
items['text/plain'] = text;
|
||||||
|
// wrap a link
|
||||||
|
items['text/html'] = `<a href="${text}">${text}</a>`;
|
||||||
|
return items;
|
||||||
|
});
|
||||||
|
success = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
success = await copyTextToClipboard(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user