From f42820e4ba70118c32c27e03c4dbf9cdc50d53ad Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 4 Dec 2020 18:28:40 +0100 Subject: [PATCH] add copy invite button to text clients --- src/create/CreateLinkView.js | 28 ++-------------------- src/open/ClientView.js | 13 +++++------ src/open/ClientViewModel.js | 19 ++++----------- src/open/clients/Element.js | 1 + src/open/clients/Weechat.js | 7 ++++++ src/utils/copy.js | 45 ++++++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 47 deletions(-) create mode 100644 src/utils/copy.js diff --git a/src/create/CreateLinkView.js b/src/create/CreateLinkView.js index 3dcebcd..0dd5bc6 100644 --- a/src/create/CreateLinkView.js +++ b/src/create/CreateLinkView.js @@ -16,31 +16,7 @@ limitations under the License. import {TemplateView} from "../utils/TemplateView.js"; import {PreviewView} from "../preview/PreviewView.js"; - -function selectNode(node) { - let selection = window.getSelection(); - selection.removeAllRanges(); - let range = document.createRange(); - range.selectNode(node); - selection.addRange(range); -} - -function copyButton(t, copyNode, label, classNames) { - return t.button({className: `${classNames} icon copy`, onClick: evt => { - const button = evt.target; - selectNode(copyNode); - if (document.execCommand("copy")) { - button.innerText = "Copied!"; - button.classList.remove("copy"); - button.classList.add("tick"); - setTimeout(() => { - button.classList.remove("tick"); - button.classList.add("copy"); - button.innerText = label; - }, 2000); - } - }}, label); -} +import {copyButton} from "../utils/copy.js"; export class CreateLinkView extends TemplateView { render(t, vm) { @@ -53,7 +29,7 @@ export class CreateLinkView extends TemplateView { t.mapView(vm => vm.previewViewModel, childVM => childVM ? new PreviewView(childVM) : null), t.div({className: {hidden: vm => !vm.linkUrl}}, [ t.h2(link), - t.div(copyButton(t, link, "Copy link", "fullwidth primary")), + t.div(copyButton(t, () => vm.linkUrl, "Copy link", "fullwidth primary")), t.div(t.button({className: "secondary fullwidth", onClick: () => this._clear()}, "Or create another link")), ]), t.form({action: "#", onSubmit: evt => this._onSubmit(evt), className: {hidden: vm => vm.linkUrl}}, [ diff --git a/src/open/ClientView.js b/src/open/ClientView.js index ba959f1..696a011 100644 --- a/src/open/ClientView.js +++ b/src/open/ClientView.js @@ -15,6 +15,8 @@ limitations under the License. */ import {TemplateView} from "../utils/TemplateView.js"; +import {copyButton} from "../utils/copy.js"; +import {text} from "../utils/html.js"; function formatPlatforms(platforms) { return platforms.reduce((str, p, i, all) => { @@ -60,17 +62,14 @@ class OpenClientView extends TemplateView { } class InstallClientView extends TemplateView { - - copyToClipboard() { - - } - render(t, vm) { const children = []; if (vm.textInstructions) { - const copy = t.button({className: "primary", onClick: evt => this.copyToClipboard(evt)}, "Copy"); - children.push(t.p([vm.textInstructions, copy])); + children.push(t.p({}, vm.textInstructions)); + if (vm.copyString) { + children.push(t.p(copyButton(t, () => vm.copyString, "Copy invite", "fullwidth primary"))); + } } const actions = t.div({className: "actions"}, vm.actions.map(a => { diff --git a/src/open/ClientViewModel.js b/src/open/ClientViewModel.js index 80a3717..b47ce69 100644 --- a/src/open/ClientViewModel.js +++ b/src/open/ClientViewModel.js @@ -16,6 +16,7 @@ limitations under the License. import {isWebPlatform, isDesktopPlatform, Platform} from "../Platform.js"; import {ViewModel} from "../utils/ViewModel.js"; +import {IdentifierKind} from "../Link.js"; export class ClientViewModel extends ViewModel { constructor(options) { @@ -102,6 +103,10 @@ export class ClientViewModel extends ViewModel { return this._client.getLinkInstructions(this._proposedPlatform, this._link); } + get copyString() { + return this._client.getCopyString(this._proposedPlatform, this._link); + } + get showDeepLinkInInstall() { return this._clientCanIntercept && this.deepLink; } @@ -137,17 +142,3 @@ export class ClientViewModel extends ViewModel { } } } - -/* -if (this._preferredClient.getLinkSupport(this.preferences.platform, this._link)) { - const deepLink = this._preferredClient.getDeepLink(this.preferences.platform, this._link); - this.openLink(deepLink); - const protocol = new URL(deepLink).protocol; - const isWebProtocol = protocol === "http:" || protocol === "https:"; - if (!isWebProtocol) { - this.missingClientViewModel = new ClientViewModel(this.childOptions({client: this._preferredClient, link: this._link})); - } - } else { - this.acceptInstructions = this._preferredClient.getLinkInstructions(this.preferences.platform, this._link); - } - */ diff --git a/src/open/clients/Element.js b/src/open/clients/Element.js index 9fa3f58..8811361 100644 --- a/src/open/clients/Element.js +++ b/src/open/clients/Element.js @@ -70,6 +70,7 @@ export class Element { } getLinkInstructions(platform, link) {} + getCopyString(platform, link) {} getName(platform) { if (platform === Platform.DesktopWeb || platform === Platform.MobileWeb) { diff --git a/src/open/clients/Weechat.js b/src/open/clients/Weechat.js index 15229b2..8e66559 100644 --- a/src/open/clients/Weechat.js +++ b/src/open/clients/Weechat.js @@ -39,5 +39,12 @@ export class Weechat { } } + getCopyString(platform, link) { + switch (link.kind) { + case LinkKind.User: return `/invite ${link.identifier}`; + case LinkKind.Room: return `/join ${link.identifier}`; + } + } + getInstallLinks(platform) {} } diff --git a/src/utils/copy.js b/src/utils/copy.js new file mode 100644 index 0000000..1a0773c --- /dev/null +++ b/src/utils/copy.js @@ -0,0 +1,45 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +function selectNode(node) { + const selection = window.getSelection(); + selection.removeAllRanges(); + const range = document.createRange(); + range.selectNode(node); + selection.addRange(range); +} + +export function copyButton(t, getCopyText, label, classNames) { + return t.button({className: `${classNames} icon copy`, onClick: evt => { + const button = evt.target; + const text = getCopyText(); + const span = document.createElement("span"); + span.innerText = text; + button.parentElement.appendChild(span); + selectNode(span); + if (document.execCommand("copy")) { + button.innerText = "Copied!"; + button.classList.remove("copy"); + button.classList.add("tick"); + setTimeout(() => { + button.classList.remove("tick"); + button.classList.add("copy"); + button.innerText = label; + }, 2000); + } + span.parentElement.removeChild(span); + }}, label); +}