From c0d0b4dd36fda551be28453bbcf5947c5e964693 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sat, 12 May 2018 15:00:11 -0700 Subject: [PATCH] add option to copy link to clipboard (#289) Fixes #288 --- bin/svgs.js | 3 +- routes/_components/Toast.html | 1 + routes/_components/dialog/asyncDialogs.js | 4 + .../AccountProfileOptionsDialog.html | 94 +++++++----- .../dialog/components/CopyDialog.html | 64 +++++++++ .../components/StatusOptionsDialog.html | 135 ++++++++++-------- .../dialog/creators/showCopyDialog.js | 16 +++ templates/2xx.html | 1 + 8 files changed, 219 insertions(+), 99 deletions(-) create mode 100644 routes/_components/dialog/components/CopyDialog.html create mode 100644 routes/_components/dialog/creators/showCopyDialog.js diff --git a/bin/svgs.js b/bin/svgs.js index 0366bb2..f722516 100644 --- a/bin/svgs.js +++ b/bin/svgs.js @@ -34,5 +34,6 @@ module.exports = [ {id: 'fa-pencil', src: 'node_modules/font-awesome-svg-png/white/svg/pencil.svg', title: 'Compose'}, {id: 'fa-times', src: 'node_modules/font-awesome-svg-png/white/svg/times.svg', title: 'Close'}, {id: 'fa-volume-off', src: 'node_modules/font-awesome-svg-png/white/svg/volume-off.svg', title: 'Mute'}, - {id: 'fa-volume-up', src: 'node_modules/font-awesome-svg-png/white/svg/volume-up.svg', title: 'Unmute'} + {id: 'fa-volume-up', src: 'node_modules/font-awesome-svg-png/white/svg/volume-up.svg', title: 'Unmute'}, + {id: 'fa-copy', src: 'node_modules/font-awesome-svg-png/white/svg/copy.svg', title: 'Copy'} ] diff --git a/routes/_components/Toast.html b/routes/_components/Toast.html index 2df2361..2d97c69 100644 --- a/routes/_components/Toast.html +++ b/routes/_components/Toast.html @@ -15,6 +15,7 @@ flex-direction: column; align-items: center; pointer-events: none; + z-index: 100000; } .toast-container { diff --git a/routes/_components/dialog/asyncDialogs.js b/routes/_components/dialog/asyncDialogs.js index b3eeb7a..be9b1dc 100644 --- a/routes/_components/dialog/asyncDialogs.js +++ b/routes/_components/dialog/asyncDialogs.js @@ -28,4 +28,8 @@ export const importShowStatusOptionsDialog = () => import( export const importShowVideoDialog = () => import( /* webpackChunkName: 'showVideoDialog' */ './creators/showVideoDialog' + ).then(mod => mod.default) + +export const importShowCopyDialog = () => import( + /* webpackChunkName: 'showCopyDialog' */ './creators/showCopyDialog' ).then(mod => mod.default) \ No newline at end of file diff --git a/routes/_components/dialog/components/AccountProfileOptionsDialog.html b/routes/_components/dialog/components/AccountProfileOptionsDialog.html index a683b4d..c118c78 100644 --- a/routes/_components/dialog/components/AccountProfileOptionsDialog.html +++ b/routes/_components/dialog/components/AccountProfileOptionsDialog.html @@ -10,7 +10,7 @@ import ModalDialog from './ModalDialog.html' import { store } from '../../../_store/store' import GenericDialogList from './GenericDialogList.html' -import { importShowComposeDialog } from '../asyncDialogs' +import { importShowComposeDialog, importShowCopyDialog } from '../asyncDialogs' import { createDialogId } from '../helpers/createDialogId' import { show } from '../helpers/showDialog' import { close } from '../helpers/closeDialog' @@ -26,7 +26,9 @@ export default { id: createDialogId() }), computed: { - // begin account data copypasta + // + // begin copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html) + // verifyCredentialsId: ({ verifyCredentials }) => verifyCredentials.id, following: ({ relationship }) => relationship && relationship.following, followRequested: ({ relationship }) => relationship && relationship.requested, @@ -42,46 +44,52 @@ export default { ? `Unfollow @${acct}` : `Follow @${acct}` }, - followIcon: ({ following, followRequested }) => { - return following ? '#fa-user-times' : followRequested ? '#fa-hourglass' : '#fa-user-plus' - }, - blockLabel: ({ blocking, acct }) => { - return blocking ? `Unblock @${acct}` : `Block @${acct}` - }, + followIcon: ({ following, followRequested }) => ( + following ? '#fa-user-times' : followRequested ? '#fa-hourglass' : '#fa-user-plus' + ), + blockLabel: ({ blocking, acct }) => ( + blocking ? `Unblock @${acct}` : `Block @${acct}` + ), blockIcon: ({ blocking }) => blocking ? '#fa-unlock' : '#fa-ban', - muteLabel: ({ muting, acct }) => { - return muting ? `Unmute @${acct}` : `Mute @${acct}` - }, + muteLabel: ({ muting, acct }) => ( + muting ? `Unmute @${acct}` : `Mute @${acct}` + ), muteIcon: ({ muting }) => muting ? '#fa-volume-up' : '#fa-volume-off', - // end account data copypasta - items: ({ blockLabel, blocking, blockIcon, muteLabel, muteIcon, + isUser: ({ accountId, verifyCredentialsId }) => accountId === verifyCredentialsId, + // + // end copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html) + // + items: ({ + blockLabel, blocking, blockIcon, muteLabel, muteIcon, followLabel, followIcon, following, followRequested, - accountId, verifyCredentialsId, acct }) => { - let isUser = accountId === verifyCredentialsId - return [ - !isUser && { - key: 'mention', - label: `Mention @${acct}`, - icon: '#fa-comments' - }, - !isUser && !blocking && { - key: 'follow', - label: followLabel, - icon: followIcon - }, - !isUser && { - key: 'block', - label: blockLabel, - icon: blockIcon - }, - !isUser && !blocking && { - key: 'mute', - label: muteLabel, - icon: muteIcon - } - - ].filter(Boolean) - } + accountId, verifyCredentialsId, acct, isUser + }) => ([ + !isUser && { + key: 'mention', + label: `Mention @${acct}`, + icon: '#fa-comments' + }, + !isUser && !blocking && { + key: 'follow', + label: followLabel, + icon: followIcon + }, + !isUser && { + key: 'block', + label: blockLabel, + icon: blockIcon + }, + !isUser && !blocking && { + key: 'mute', + label: muteLabel, + icon: muteIcon + }, + { + key: 'copy', + label: 'Copy link to account', + icon: '#fa-copy' + } + ].filter(Boolean)) }, methods: { show, @@ -96,6 +104,8 @@ export default { return this.onBlockClicked() case 'mute': return this.onMuteClicked() + case 'copy': + return this.onCopyClicked() } }, async onMentionClicked () { @@ -121,6 +131,12 @@ export default { let { accountId, muting } = this.get() this.close() await setAccountMuted(accountId, !muting, true) + }, + async onCopyClicked () { + let { account } = this.get() + let { url } = account + let showCopyDialog = await importShowCopyDialog() + showCopyDialog(url) } }, components: { diff --git a/routes/_components/dialog/components/CopyDialog.html b/routes/_components/dialog/components/CopyDialog.html new file mode 100644 index 0000000..b6cc57a --- /dev/null +++ b/routes/_components/dialog/components/CopyDialog.html @@ -0,0 +1,64 @@ + +
+ + +
+
+ + \ No newline at end of file diff --git a/routes/_components/dialog/components/StatusOptionsDialog.html b/routes/_components/dialog/components/StatusOptionsDialog.html index 801ba40..a66d7c0 100644 --- a/routes/_components/dialog/components/StatusOptionsDialog.html +++ b/routes/_components/dialog/components/StatusOptionsDialog.html @@ -18,24 +18,27 @@ import { oncreate } from '../helpers/onCreateDialog' import { setAccountBlocked } from '../../../_actions/block' import { setAccountMuted } from '../../../_actions/mute' import { setStatusPinnedOrUnpinned } from '../../../_actions/pin' +import { importShowCopyDialog } from '../asyncDialogs' export default { oncreate, computed: { - relationship: ({ $currentAccountRelationship }) => $currentAccountRelationship, - account: ({ $currentAccountProfile }) => $currentAccountProfile, - verifyCredentials: ({ $currentVerifyCredentials }) => $currentVerifyCredentials, - statusId: ({ status }) => status.id, - pinned: ({ status }) => status.pinned, - // begin account data copypasta - verifyCredentialsId: ({ verifyCredentials }) => verifyCredentials.id, - following: ({ relationship }) => relationship && relationship.following, - followRequested: ({ relationship }) => relationship && relationship.requested, - accountId: ({ account }) => account && account.id, - acct: ({ account }) => account.acct, - muting: ({ relationship }) => relationship.muting, - blocking: ({ relationship }) => relationship.blocking, - followLabel: ({ following, followRequested, account, acct }) => { + relationship: ({$currentAccountRelationship}) => $currentAccountRelationship, + account: ({$currentAccountProfile}) => $currentAccountProfile, + verifyCredentials: ({$currentVerifyCredentials}) => $currentVerifyCredentials, + statusId: ({status}) => status.id, + pinned: ({status}) => status.pinned, + // + // begin copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html) + // + verifyCredentialsId: ({verifyCredentials}) => verifyCredentials.id, + following: ({relationship}) => relationship && relationship.following, + followRequested: ({relationship}) => relationship && relationship.requested, + accountId: ({account}) => account && account.id, + acct: ({account}) => account.acct, + muting: ({relationship}) => relationship.muting, + blocking: ({relationship}) => relationship.blocking, + followLabel: ({following, followRequested, account, acct}) => { if (typeof following === 'undefined' || !account) { return '' } @@ -43,51 +46,57 @@ export default { ? `Unfollow @${acct}` : `Follow @${acct}` }, - followIcon: ({ following, followRequested }) => { - return following ? '#fa-user-times' : followRequested ? '#fa-hourglass' : '#fa-user-plus' - }, - blockLabel: ({ blocking, acct }) => { - return blocking ? `Unblock @${acct}` : `Block @${acct}` - }, - blockIcon: ({ blocking }) => blocking ? '#fa-unlock' : '#fa-ban', - muteLabel: ({ muting, acct }) => { - return muting ? `Unmute @${acct}` : `Mute @${acct}` - }, - muteIcon: ({ muting }) => muting ? '#fa-volume-up' : '#fa-volume-off', - // end account data copypasta - isUser: ({ accountId, verifyCredentialsId }) => accountId === verifyCredentialsId, - pinLabel: ({ pinned, isUser }) => isUser ? (pinned ? 'Unpin from profile' : 'Pin to profile') : '', - items: ({ blockLabel, blocking, blockIcon, muteLabel, muteIcon, followLabel, followIcon, - following, followRequested, pinLabel, isUser }) => { - return [ - isUser && { - key: 'delete', - label: 'Delete', - icon: '#fa-trash' - }, - isUser && { - key: 'pin', - label: pinLabel, - icon: '#fa-thumb-tack' - }, - !isUser && !blocking && { - key: 'follow', - label: followLabel, - icon: followIcon - }, - !isUser && { - key: 'block', - label: blockLabel, - icon: blockIcon - }, - !isUser && !blocking && { - key: 'mute', - label: muteLabel, - icon: muteIcon - } - - ].filter(Boolean) - } + followIcon: ({following, followRequested}) => ( + following ? '#fa-user-times' : followRequested ? '#fa-hourglass' : '#fa-user-plus' + ), + blockLabel: ({blocking, acct}) => ( + blocking ? `Unblock @${acct}` : `Block @${acct}` + ), + blockIcon: ({blocking}) => blocking ? '#fa-unlock' : '#fa-ban', + muteLabel: ({muting, acct}) => ( + muting ? `Unmute @${acct}` : `Mute @${acct}` + ), + muteIcon: ({muting}) => muting ? '#fa-volume-up' : '#fa-volume-off', + isUser: ({accountId, verifyCredentialsId}) => accountId === verifyCredentialsId, + // + // end copypasta (StatusOptionsDialog.html / AccountProfileOptionsDialog.html) + // + pinLabel: ({pinned, isUser}) => isUser ? (pinned ? 'Unpin from profile' : 'Pin to profile') : '', + items: ({ + blockLabel, blocking, blockIcon, muteLabel, muteIcon, followLabel, followIcon, + following, followRequested, pinLabel, isUser + }) => ([ + isUser && { + key: 'delete', + label: 'Delete', + icon: '#fa-trash' + }, + isUser && { + key: 'pin', + label: pinLabel, + icon: '#fa-thumb-tack' + }, + !isUser && !blocking && { + key: 'follow', + label: followLabel, + icon: followIcon + }, + !isUser && { + key: 'block', + label: blockLabel, + icon: blockIcon + }, + !isUser && !blocking && { + key: 'mute', + label: muteLabel, + icon: muteIcon + }, + { + key: 'copy', + label: 'Copy link to toot', + icon: '#fa-copy' + } + ].filter(Boolean)) }, components: { ModalDialog, @@ -109,6 +118,8 @@ export default { return this.onBlockClicked() case 'mute': return this.onMuteClicked() + case 'copy': + return this.onCopyClicked() } }, async onDeleteClicked () { @@ -135,6 +146,12 @@ export default { let { accountId, muting } = this.get() this.close() await setAccountMuted(accountId, !muting, true) + }, + async onCopyClicked () { + let { status } = this.get() + let { url } = status + let showCopyDialog = await importShowCopyDialog() + showCopyDialog(url) } } } diff --git a/routes/_components/dialog/creators/showCopyDialog.js b/routes/_components/dialog/creators/showCopyDialog.js new file mode 100644 index 0000000..9fcc81d --- /dev/null +++ b/routes/_components/dialog/creators/showCopyDialog.js @@ -0,0 +1,16 @@ +import CopyDialog from '../components/CopyDialog.html' +import { createDialogElement } from '../helpers/createDialogElement' +import { createDialogId } from '../helpers/createDialogId' + +export default function showCopyDialog (text) { + let dialog = new CopyDialog({ + target: createDialogElement(), + data: { + id: createDialogId(), + label: 'Copy dialog', + title: 'Copy link', + text + } + }) + dialog.show() +} diff --git a/templates/2xx.html b/templates/2xx.html index aa0c226..bfd76d9 100644 --- a/templates/2xx.html +++ b/templates/2xx.html @@ -117,6 +117,7 @@ if (!localStorage.store_currentInstance) { Close Mute Unmute +Copy