mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-22 21:50:34 +03:00
Merge pull request #2309 from hcengineering/tsk-257-copy-to-clipboard
Fix copying text to clipboard for Safari
This commit is contained in:
commit
7aeb3c19e9
@ -702,9 +702,9 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: recruit.actionImpl.CopyToClipboard,
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
type: 'id'
|
||||
textProvider: recruit.function.GetApplicationId
|
||||
},
|
||||
label: recruit.string.CopyId,
|
||||
icon: recruit.icon.Application,
|
||||
@ -723,9 +723,9 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: recruit.actionImpl.CopyToClipboard,
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
type: 'link'
|
||||
textProvider: recruit.function.GetApplicationLink
|
||||
},
|
||||
label: recruit.string.CopyLink,
|
||||
icon: recruit.icon.Application,
|
||||
@ -744,9 +744,9 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: recruit.actionImpl.CopyToClipboard,
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
type: 'link'
|
||||
textProvider: recruit.function.GetRecruitLink
|
||||
},
|
||||
label: recruit.string.CopyLink,
|
||||
icon: recruit.icon.Application,
|
||||
|
@ -32,12 +32,16 @@ export default mergeIds(recruitId, recruit, {
|
||||
CopyCandidateLink: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
CreateOpinion: '' as ViewAction,
|
||||
CopyToClipboard: '' as ViewAction
|
||||
CreateOpinion: '' as ViewAction
|
||||
},
|
||||
category: {
|
||||
Recruit: '' as Ref<ActionCategory>
|
||||
},
|
||||
function: {
|
||||
GetApplicationId: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||
GetApplicationLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||
GetRecruitLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>
|
||||
},
|
||||
string: {
|
||||
ApplicationShort: '' as IntlString,
|
||||
ApplicationsShort: '' as IntlString,
|
||||
|
@ -1129,9 +1129,9 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: tracker.actionImpl.CopyToClipboard,
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
type: 'id'
|
||||
textProvider: tracker.function.GetIssueId
|
||||
},
|
||||
label: tracker.string.CopyIssueId,
|
||||
icon: tracker.icon.CopyID,
|
||||
@ -1150,9 +1150,9 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: tracker.actionImpl.CopyToClipboard,
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
type: 'title'
|
||||
textProvider: tracker.function.GetIssueTitle
|
||||
},
|
||||
label: tracker.string.CopyIssueTitle,
|
||||
icon: tracker.icon.CopyBranch,
|
||||
@ -1171,9 +1171,9 @@ export function createModel (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: tracker.actionImpl.CopyToClipboard,
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
type: 'link'
|
||||
textProvider: tracker.function.GetIssueLink
|
||||
},
|
||||
label: tracker.string.CopyIssueUrl,
|
||||
icon: tracker.icon.CopyURL,
|
||||
|
@ -121,7 +121,8 @@
|
||||
} else {
|
||||
location.path.length = 4
|
||||
}
|
||||
await copyTextToClipboard(`${window.location.origin}${locationToUrl(location)}`)
|
||||
const text = `${window.location.origin}${locationToUrl(location)}`
|
||||
await copyTextToClipboard(text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ import VacancyItemPresenter from './components/VacancyItemPresenter.svelte'
|
||||
import VacancyModifiedPresenter from './components/VacancyModifiedPresenter.svelte'
|
||||
import VacancyPresenter from './components/VacancyPresenter.svelte'
|
||||
import recruit from './plugin'
|
||||
import { copyToClipboard, getApplicationTitle } from './utils'
|
||||
import { objectIdProvider, objectLinkProvider, getApplicationTitle } from './utils'
|
||||
import VacancyList from './components/VacancyList.svelte'
|
||||
|
||||
async function createOpinion (object: Doc): Promise<void> {
|
||||
@ -254,8 +254,7 @@ async function noneApplicant (filter: Filter, onUpdate: () => void): Promise<Obj
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
actionImpl: {
|
||||
CreateOpinion: createOpinion,
|
||||
CopyToClipboard: copyToClipboard
|
||||
CreateOpinion: createOpinion
|
||||
},
|
||||
validator: {
|
||||
ApplicantValidator: applicantValidator
|
||||
@ -305,6 +304,9 @@ export default async (): Promise<Resources> => ({
|
||||
ApplicationTitleProvider: getApplicationTitle,
|
||||
HasActiveApplicant: hasActiveApplicant,
|
||||
HasNoActiveApplicant: hasNoActiveApplicant,
|
||||
NoneApplications: noneApplicant
|
||||
NoneApplications: noneApplicant,
|
||||
GetApplicationId: objectIdProvider,
|
||||
GetApplicationLink: objectLinkProvider,
|
||||
GetRecruitLink: objectLinkProvider
|
||||
}
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import core, { Doc, Ref, TxOperations } from '@hcengineering/core'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import { copyTextToClipboard, getClient } from '@hcengineering/presentation'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Applicant, Candidate } from '@hcengineering/recruit'
|
||||
import { getPanelURI } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
@ -19,23 +19,13 @@ export async function getApplicationTitle (client: TxOperations, ref: Ref<Doc>):
|
||||
return `${label}-${object.number}`
|
||||
}
|
||||
|
||||
export async function copyToClipboard (
|
||||
object: Applicant | Candidate,
|
||||
ev: Event,
|
||||
{ type }: { type: string }
|
||||
): Promise<void> {
|
||||
export async function objectIdProvider (doc: Applicant | Candidate): Promise<string> {
|
||||
const client = getClient()
|
||||
let text: string
|
||||
switch (type) {
|
||||
case 'id':
|
||||
text = await getApplicationTitle(client, object._id)
|
||||
break
|
||||
case 'link':
|
||||
// TODO: fix when short link is available
|
||||
text = `${window.location.href}#${getPanelURI(view.component.EditDoc, object._id, object._class, 'content')}`
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
await copyTextToClipboard(text)
|
||||
return await getApplicationTitle(client, doc._id)
|
||||
}
|
||||
|
||||
export async function objectLinkProvider (doc: Applicant | Candidate): Promise<string> {
|
||||
return await Promise.resolve(
|
||||
`${window.location.href}#${getPanelURI(view.component.EditDoc, doc._id, doc._class, 'content')}`
|
||||
)
|
||||
}
|
||||
|
@ -59,7 +59,14 @@ import SetDueDateActionPopup from './components/SetDueDateActionPopup.svelte'
|
||||
import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.svelte'
|
||||
import Views from './components/views/Views.svelte'
|
||||
import Statuses from './components/workflow/Statuses.svelte'
|
||||
import { copyToClipboard, getIssueId, getIssueTitle, resolveLocation } from './issues'
|
||||
import {
|
||||
getIssueId,
|
||||
getIssueTitle,
|
||||
issueIdProvider,
|
||||
issueLinkProvider,
|
||||
issueTitleProvider,
|
||||
resolveLocation
|
||||
} from './issues'
|
||||
import tracker from './plugin'
|
||||
|
||||
import SprintEditor from './components/sprints/SprintEditor.svelte'
|
||||
@ -212,10 +219,12 @@ export default async (): Promise<Resources> => ({
|
||||
await queryIssue(tracker.class.Issue, client, query, filter)
|
||||
},
|
||||
function: {
|
||||
IssueTitleProvider: getIssueTitle
|
||||
IssueTitleProvider: getIssueTitle,
|
||||
GetIssueId: issueIdProvider,
|
||||
GetIssueLink: issueLinkProvider,
|
||||
GetIssueTitle: issueTitleProvider
|
||||
},
|
||||
actionImpl: {
|
||||
CopyToClipboard: copyToClipboard,
|
||||
EditWorkflowStatuses: editWorkflowStatuses
|
||||
},
|
||||
resolver: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Doc, DocumentUpdate, Ref, RelatedDocument, TxOperations } from '@hcengineering/core'
|
||||
import { copyTextToClipboard, getClient } from '@hcengineering/presentation'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Issue, Project, Sprint, Team, trackerId } from '@hcengineering/tracker'
|
||||
import { getCurrentLocation, getPanelURI, Location } from '@hcengineering/ui'
|
||||
import { workbenchId } from '@hcengineering/workbench'
|
||||
@ -31,24 +31,18 @@ export function generateIssuePanelUri (issue: Issue): string {
|
||||
return getPanelURI(tracker.component.EditIssue, issue._id, issue._class, 'content')
|
||||
}
|
||||
|
||||
export async function copyToClipboard (object: Issue, ev: Event, { type }: { type: string }): Promise<void> {
|
||||
export async function issueIdProvider (doc: Doc): Promise<string> {
|
||||
const client = getClient()
|
||||
let text: string
|
||||
switch (type) {
|
||||
case 'id':
|
||||
text = await getIssueTitle(client, object._id)
|
||||
break
|
||||
case 'title':
|
||||
text = object.title
|
||||
break
|
||||
case 'link':
|
||||
text = generateIssueShortLink(await getIssueTitle(client, object._id))
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
return await getIssueTitle(client, doc._id)
|
||||
}
|
||||
|
||||
await copyTextToClipboard(text)
|
||||
export async function issueTitleProvider (doc: Issue): Promise<string> {
|
||||
return await Promise.resolve(doc.title)
|
||||
}
|
||||
|
||||
export async function issueLinkProvider (doc: Doc): Promise<string> {
|
||||
const client = getClient()
|
||||
return await getIssueTitle(client, doc._id).then(generateIssueShortLink)
|
||||
}
|
||||
|
||||
export function generateIssueShortLink (issueId: string): string {
|
||||
|
@ -301,6 +301,9 @@ export default mergeIds(trackerId, tracker, {
|
||||
IssueTemplatePresenter: '' as AnyComponent
|
||||
},
|
||||
function: {
|
||||
IssueTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>) => Promise<string>>
|
||||
IssueTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>) => Promise<string>>,
|
||||
GetIssueId: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||
GetIssueLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||
GetIssueTitle: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>
|
||||
}
|
||||
})
|
||||
|
@ -18,6 +18,41 @@ import view from './plugin'
|
||||
import { FocusSelection, focusStore, previewDocument, SelectDirection, selectionStore } from './selection'
|
||||
import { deleteObject } from './utils'
|
||||
|
||||
/**
|
||||
* Action to be used for copying text to clipboard.
|
||||
* In Safari a request to write to the clipboard must be triggered during a user gesture.
|
||||
* A call to clipboard.write or clipboard.writeText outside the scope of a user
|
||||
* gesture(such as "click" or "touch" event handlers) will result in the immediate
|
||||
* rejection of the promise returned by the API call.
|
||||
* https://webkit.org/blog/10855/async-clipboard-api/
|
||||
*
|
||||
* * Require props:
|
||||
* - textProvider - a function that provides text to be copied.
|
||||
* - props - additional text provider props.
|
||||
*/
|
||||
async function CopyTextToClipboard (
|
||||
doc: Doc,
|
||||
evt: Event,
|
||||
props: {
|
||||
textProvider: Resource<(doc: Doc, props?: Record<string, any>) => Promise<string>>
|
||||
props?: Record<string, any>
|
||||
}
|
||||
): Promise<void> {
|
||||
const getText = await getResource(props.textProvider)
|
||||
try {
|
||||
// Safari specific behavior
|
||||
// see https://bugs.webkit.org/show_bug.cgi?id=222262
|
||||
const clipboardItem = new ClipboardItem({
|
||||
'text/plain': getText(doc, props.props)
|
||||
})
|
||||
await navigator.clipboard.write([clipboardItem])
|
||||
} catch {
|
||||
// Fallback to default clipboard API implementation
|
||||
const text = await getText(doc, props.props)
|
||||
await navigator.clipboard.writeText(text)
|
||||
}
|
||||
}
|
||||
|
||||
function Delete (object: Doc): void {
|
||||
showPopup(
|
||||
MessageBox,
|
||||
@ -334,6 +369,7 @@ async function getPopupAlignment (
|
||||
* @public
|
||||
*/
|
||||
export const actionImpl = {
|
||||
CopyTextToClipboard,
|
||||
Delete,
|
||||
Move,
|
||||
MoveUp,
|
||||
|
@ -503,6 +503,10 @@ const view = plugin(viewId, {
|
||||
PositionElementAlignment: '' as Resource<(e?: Event) => PopupAlignment | undefined>
|
||||
},
|
||||
actionImpl: {
|
||||
CopyTextToClipboard: '' as ViewAction<{
|
||||
textProvider: Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>
|
||||
props?: Record<string, any>
|
||||
}>,
|
||||
UpdateDocument: '' as ViewAction<{
|
||||
key: string
|
||||
value: any
|
||||
|
Loading…
Reference in New Issue
Block a user