Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[No QA][TS migration] Migrate 'Clipboard' lib to TypeScript #28789

Merged
merged 18 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions src/libs/Clipboard/index.native.js

This file was deleted.

19 changes: 19 additions & 0 deletions src/libs/Clipboard/index.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Clipboard from '@react-native-clipboard/clipboard';
import {CanSetHtml, SetHtml, SetString} from './types';

/**
* Sets a string on the Clipboard object via @react-native-clipboard/clipboard
*/
const setString: SetString = (text) => {
Clipboard.setString(text);
};

// We don't want to set HTML on native platforms so noop them.
const canSetHtml: CanSetHtml = () => false;
const setHtml: SetHtml = () => {};

export default {
setString,
canSetHtml,
setHtml,
};
63 changes: 42 additions & 21 deletions src/libs/Clipboard/index.js → src/libs/Clipboard/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
import Clipboard from '@react-native-clipboard/clipboard';
import lodashGet from 'lodash/get';
import * as Browser from '@libs/Browser';
import CONST from '@src/CONST';
import {CanSetHtml, SetHtml, SetString} from './types';

const canSetHtml = () => lodashGet(navigator, 'clipboard.write');
type ComposerSelection = {
start: number;
end: number;
direction: 'forward' | 'backward' | 'none';
};

type AnchorSelection = {
anchorOffset: number;
focusOffset: number;
anchorNode: Node;
focusNode: Node;
};

type NullableObject<T> = {[K in keyof T]: T[K] | null};

type OriginalSelection = ComposerSelection | NullableObject<AnchorSelection>;

const canSetHtml: CanSetHtml =
() =>
(...args: ClipboardItems) =>
navigator?.clipboard?.write([...args]);

/**
* Deprecated method to write the content as HTML to clipboard.
* @param {String} html HTML representation
* @param {String} text Plain text representation
*/
function setHTMLSync(html, text) {
function setHTMLSync(html: string, text: string) {
const node = document.createElement('span');
node.textContent = html;
node.style.all = 'unset';
Expand All @@ -21,16 +39,21 @@ function setHTMLSync(html, text) {
node.addEventListener('copy', (e) => {
e.stopPropagation();
e.preventDefault();
e.clipboardData.clearData();
e.clipboardData.setData('text/html', html);
e.clipboardData.setData('text/plain', text);
e.clipboardData?.clearData();
e.clipboardData?.setData('text/html', html);
e.clipboardData?.setData('text/plain', text);
});
document.body.appendChild(node);

const selection = window.getSelection();
const firstAnchorChild = selection.anchorNode && selection.anchorNode.firstChild;
const selection = window?.getSelection();

if (selection === null) {
return;
}

const firstAnchorChild = selection.anchorNode?.firstChild;
const isComposer = firstAnchorChild instanceof HTMLTextAreaElement;
let originalSelection = null;
let originalSelection: OriginalSelection | null = null;
if (isComposer) {
originalSelection = {
start: firstAnchorChild.selectionStart,
Expand Down Expand Up @@ -60,23 +83,23 @@ function setHTMLSync(html, text) {

selection.removeAllRanges();

if (isComposer) {
const anchorSelection = originalSelection as AnchorSelection;

if (isComposer && 'start' in originalSelection) {
firstAnchorChild.setSelectionRange(originalSelection.start, originalSelection.end, originalSelection.direction);
} else if (originalSelection.anchorNode && originalSelection.focusNode) {
} else if (anchorSelection.anchorNode && anchorSelection.focusNode) {
// When copying to the clipboard here, the original values of anchorNode and focusNode will be null since there will be no user selection.
// We are adding a check to prevent null values from being passed to setBaseAndExtent, in accordance with the standards of the Selection API as outlined here: https://w3c.github.io/selection-api/#dom-selection-setbaseandextent.
selection.setBaseAndExtent(originalSelection.anchorNode, originalSelection.anchorOffset, originalSelection.focusNode, originalSelection.focusOffset);
selection.setBaseAndExtent(anchorSelection.anchorNode, anchorSelection.anchorOffset, anchorSelection.focusNode, anchorSelection.focusOffset);
}

document.body.removeChild(node);
}

/**
* Writes the content as HTML if the web client supports it.
* @param {String} html HTML representation
* @param {String} text Plain text representation
*/
const setHtml = (html, text) => {
const setHtml: SetHtml = (html: string, text: string) => {
if (!html || !text) {
return;
}
Expand All @@ -93,8 +116,8 @@ const setHtml = (html, text) => {
setHTMLSync(html, text);
} else {
navigator.clipboard.write([
// eslint-disable-next-line no-undef
new ClipboardItem({
/* eslint-disable @typescript-eslint/naming-convention */
'text/html': new Blob([html], {type: 'text/html'}),
'text/plain': new Blob([text], {type: 'text/plain'}),
}),
Expand All @@ -104,10 +127,8 @@ const setHtml = (html, text) => {

/**
* Sets a string on the Clipboard object via react-native-web
*
* @param {String} text
*/
const setString = (text) => {
const setString: SetString = (text) => {
Clipboard.setString(text);
};

Expand Down
5 changes: 5 additions & 0 deletions src/libs/Clipboard/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type SetString = (text: string) => void;
type SetHtml = (html: string, text: string) => void;
type CanSetHtml = (() => (...args: ClipboardItems) => Promise<void>) | (() => boolean);

export type {SetString, CanSetHtml, SetHtml};
11 changes: 11 additions & 0 deletions src/types/modules/react-native-web.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable import/prefer-default-export */
/* eslint-disable @typescript-eslint/consistent-type-definitions */
declare module 'react-native-web' {
class Clipboard {
static isAvailable(): boolean;
static getString(): Promise<string>;
static setString(text: string): boolean;
}

export {Clipboard};
}
Loading