[TSK-225] Tracker: add issue shortlink resolver (Part 2) (#2173)

Signed-off-by: Anna <anna.no@xored.com>
This commit is contained in:
Anna No
2022-06-30 10:46:46 +07:00
committed by GitHub
parent 94ee6f5afe
commit bf6780a32f
16 changed files with 158 additions and 57 deletions
@@ -19,7 +19,7 @@
import { Icon } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import tracker from '../plugin'
import { getIssueId } from '../utils'
import { getIssueId } from '../issues'
export let value: Issue | AttachedData<Issue> | Issue[]
export let width: 'medium' | 'large' | 'full' = 'large'
@@ -18,7 +18,7 @@
import type { Issue, Team } from '@anticrm/tracker'
import { Icon } from '@anticrm/ui'
import tracker from '../../plugin'
import { getIssueId } from '../../utils'
import { getIssueId } from '../../issues'
export let value: WithLookup<Issue>
$: title = getIssueId(value.$lookup?.space as Team, value)
@@ -18,7 +18,7 @@
import { Spinner, IconClose, tooltip } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import tracker from '../../plugin'
import { getIssueId } from '../../utils'
import { getIssueId } from '../../issues'
export let issue: Issue
@@ -36,7 +36,7 @@
import { ContextMenu } from '@anticrm/view-resources'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import tracker from '../../../plugin'
import { getIssueId } from '../../../utils'
import { generateIssueShortLink, getIssueId } from '../../../issues'
import IssueStatusActivity from '../IssueStatusActivity.svelte'
import ControlPanel from './ControlPanel.svelte'
import CopyToClipboard from './CopyToClipboard.svelte'
@@ -269,7 +269,7 @@
{#if issueId}{issueId}{/if}
</span>
<svelte:fragment slot="actions">
<CopyToClipboard issueUrl={window.location.href} {issueId} />
<CopyToClipboard issueUrl={generateIssueShortLink(issueId)} {issueId} />
</svelte:fragment>
<svelte:fragment slot="custom-attributes">
@@ -26,7 +26,7 @@
} from '@anticrm/view-resources'
import { showPanel, showPopup } from '@anticrm/ui'
import tracker from '../../../plugin'
import { getIssueId } from '../../../utils'
import { getIssueId } from '../../../issues'
import Circles from '../../icons/Circles.svelte'
import AssigneeEditor from '../AssigneeEditor.svelte'
import DueDateEditor from '../DueDateEditor.svelte'
@@ -28,7 +28,7 @@
Spinner
} from '@anticrm/ui'
import tracker from '../../../plugin'
import { getIssueId } from '../../../utils'
import { getIssueId } from '../../../issues'
export let issue: WithLookup<Issue>
export let team: Team
@@ -19,7 +19,7 @@
import { Button, closeTooltip, ProgressCircle, SelectPopup, showPanel, showPopup } from '@anticrm/ui'
import { updateFocus } from '@anticrm/view-resources'
import tracker from '../../../plugin'
import { getIssueId } from '../../../utils'
import { getIssueId } from '../../../issues'
export let issue: WithLookup<Issue>
export let currentTeam: Team | undefined
+4 -1
View File
@@ -57,7 +57,7 @@ import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.sv
import Views from './components/views/Views.svelte'
import KanbanView from './components/issues/KanbanView.svelte'
import tracker from './plugin'
import { copyToClipboard, getIssueId, getIssueTitle } from './utils'
import { copyToClipboard, getIssueId, getIssueTitle, resolveLocation } from './issues'
import CreateIssue from './components/CreateIssue.svelte'
export async function queryIssue<D extends Issue> (
@@ -159,5 +159,8 @@ export default async (): Promise<Resources> => ({
},
actionImpl: {
CopyToClipboard: copyToClipboard
},
resolver: {
Location: resolveLocation
}
})
+98
View File
@@ -0,0 +1,98 @@
import { Doc, Ref, TxOperations } from '@anticrm/core'
import { getClient } from '@anticrm/presentation'
import { Issue, Team, trackerId } from '@anticrm/tracker'
import { getPanelURI, Location } from '@anticrm/ui'
import workbench from '@anticrm/workbench'
import tracker from './plugin'
export function getIssueId (team: Team, issue: Issue): string {
return `${team.identifier}-${issue.number}`
}
export function isIssueId (shortLink: string): boolean {
return /^\w+-\d+$/.test(shortLink)
}
export async function getIssueTitle (client: TxOperations, ref: Ref<Doc>): Promise<string> {
const object = await client.findOne(
tracker.class.Issue,
{ _id: ref as Ref<Issue> },
{ lookup: { space: tracker.class.Team } }
)
if (object?.$lookup?.space === undefined) throw new Error(`Issue Team not found, _id: ${ref}`)
return getIssueId(object.$lookup.space, object)
}
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> {
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':
// TODO: fix when short link is available
text = `${window.location.href}#${generateIssuePanelUri(object)}`
break
default:
return
}
await navigator.clipboard.writeText(text)
}
export function generateIssueShortLink (issueId: string): string {
return `${window.location.host}/${workbench.component.WorkbenchApp}/${trackerId}/${issueId}`
}
export async function generateIssueLocation (loc: Location, issueId: string): Promise<Location | undefined> {
const tokens = issueId.split('-')
if (tokens.length < 2) {
return undefined
}
const teamId = tokens[0]
const issueNumber = Number(tokens[1])
const client = getClient()
const team = await client.findOne(tracker.class.Team, { identifier: teamId })
if (team === undefined) {
console.error(
`Could not find team ${teamId}. Make sure you are in correct workspace and the team was not deleted or renamed.`
)
return undefined
}
const issue = await client.findOne(tracker.class.Issue, { number: issueNumber, space: team._id })
if (issue === undefined) {
console.error(`Could not find issue ${issueId}.`)
return undefined
}
const appComponent = loc.path[0] ?? ''
return {
path: [appComponent, trackerId, team._id, 'issues'],
fragment: generateIssuePanelUri(issue)
}
}
export async function resolveLocation (loc: Location): Promise<Location | undefined> {
const app = loc.path.length > 1 ? loc.path[1] : undefined
if (app !== trackerId) {
return undefined
}
const shortLink = loc.path.length > 2 ? loc.path[2] : undefined
if (shortLink === undefined || shortLink === null) {
return undefined
}
// issue shortlink
if (isIssueId(shortLink)) {
return await generateIssueLocation(loc, shortLink)
}
return undefined
}
+2 -43
View File
@@ -14,10 +14,9 @@
//
import contact, { Employee, formatName } from '@anticrm/contact'
import { Doc, DocumentQuery, Ref, SortingOrder, TxOperations } from '@anticrm/core'
import { DocumentQuery, Ref, SortingOrder, TxOperations } from '@anticrm/core'
import { TypeState } from '@anticrm/kanban'
import { Asset, IntlString, translate } from '@anticrm/platform'
import { getClient } from '@anticrm/presentation'
import {
Issue,
IssuesDateModificationPeriod,
@@ -27,13 +26,7 @@ import {
ProjectStatus,
Team
} from '@anticrm/tracker'
import {
AnyComponent,
AnySvelteComponent,
getMillisecondsInMonth,
getPanelURI,
MILLISECONDS_IN_WEEK
} from '@anticrm/ui'
import { AnyComponent, AnySvelteComponent, getMillisecondsInMonth, MILLISECONDS_IN_WEEK } from '@anticrm/ui'
import tracker from './plugin'
import { defaultPriorities, defaultProjectStatuses, issuePriorities } from './types'
@@ -346,10 +339,6 @@ export function getCategories (
return existingCategories
}
export function getIssueId (team: Team, issue: Issue): string {
return `${team.identifier}-${issue.number}`
}
export async function getKanbanStatuses (
client: TxOperations,
groupBy: IssuesGrouping,
@@ -460,33 +449,3 @@ export async function getKanbanStatuses (
}
return []
}
export async function getIssueTitle (client: TxOperations, ref: Ref<Doc>): Promise<string> {
const object = await client.findOne(
tracker.class.Issue,
{ _id: ref as Ref<Issue> },
{ lookup: { space: tracker.class.Team } }
)
if (object?.$lookup?.space === undefined) throw new Error(`Issue Team not found, _id: ${ref}`)
return getIssueId(object.$lookup.space, object)
}
export async function copyToClipboard (object: Issue, ev: Event, { type }: { type: string }): Promise<void> {
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':
// TODO: fix when short link is available
text = `${window.location.href}#${getPanelURI(tracker.component.EditIssue, object._id, object._class, 'content')}`
break
default:
return
}
await navigator.clipboard.writeText(text)
}