|
1 | | -//// START GITLAB |
| 1 | +/** |
| 2 | + * @name Custom Gitlab Script (N4) |
| 3 | + * @urlAlias nfGitlabScript |
| 4 | + * @urlRegex gitlab.com |
| 5 | + */ |
| 6 | +'use strict' |
| 7 | + |
| 8 | +// --------------------------- |
| 9 | +// Issues (new GitLab UI) |
| 10 | +// --------------------------- |
2 | 11 | togglbutton.render( |
3 | | - '.issue-details .detail-page-description:not(.toggl)', |
4 | | - { observe: !0 }, |
5 | | - function (t) { |
6 | | - const n = [ |
7 | | - [a()] |
8 | | - .filter(Boolean) |
9 | | - .map(function (r) { |
10 | | - return '#' + r; |
11 | | - }) |
12 | | - .join(''), |
13 | | - i(t), |
14 | | - ] |
15 | | - .filter(Boolean) |
16 | | - .join(' '); |
17 | | - o($('.detail-page-header-actions'), n, !0), o($('.time-tracker'), n); |
18 | | - } |
19 | | -); |
| 12 | + 'span[data-testid="work-item-created"]:not(.toggl)', |
| 13 | + { observe: true }, |
| 14 | + function (createdSpan) { |
| 15 | + if (!createdSpan) return |
| 16 | + createdSpan.classList.add('toggl') |
| 17 | + |
| 18 | + const id = getIdFromBody() |
| 19 | + const prefix = id ? `#${id}` : '' |
| 20 | + |
| 21 | + const titleEl = document.querySelector('[data-testid="work-item-title"]') |
| 22 | + const title = titleEl ? titleEl.textContent.trim() : '' |
| 23 | + |
| 24 | + const description = [prefix, title].filter(Boolean).join(' ') |
| 25 | + |
| 26 | + const link = togglbutton.createTimerLink({ |
| 27 | + className: 'gitlab', |
| 28 | + description, |
| 29 | + tags: tagsSelector, |
| 30 | + taskId: (projects, tasks) => extractTaskId(projects, tasks), |
| 31 | + projectName: (projects, tasks) => extractProjectName(projects, tasks), |
| 32 | + }) |
| 33 | + |
| 34 | + createdSpan.insertAdjacentElement('afterend', link) |
| 35 | + }, |
| 36 | +) |
| 37 | + |
| 38 | +// --------------------------- |
| 39 | +// Merge Requests (new GitLab UI) |
| 40 | +// --------------------------- |
20 | 41 | togglbutton.render( |
21 | | - '.merge-request > .detail-page-header:not(.toggl)', |
22 | | - { observe: !0 }, |
23 | | - function (t) { |
24 | | - const n = [ |
25 | | - [a()] |
26 | | - .filter(Boolean) |
27 | | - .map(function (r) { |
28 | | - return 'MR' + r + '::'; |
29 | | - }) |
30 | | - .join(''), |
31 | | - i(t), |
32 | | - ] |
33 | | - .filter(Boolean) |
34 | | - .join(' '); |
35 | | - o($('.detail-page-header-actions'), n, !0), o($('.time-tracker'), n); |
36 | | - } |
37 | | -); |
38 | | -function o(t, e, n = !1) { |
39 | | - const r = togglbutton.createTimerLink({ |
40 | | - className: 'gitlab', |
41 | | - description: e, |
42 | | - tags: l, |
43 | | - taskId: (o, i) => { |
44 | | - return extractTaskId(o, i); |
45 | | - }, |
46 | | - projectName: (o, i) => { |
47 | | - return extractProjectName(o, i); |
48 | | - }, |
49 | | - }); |
50 | | - n ? t.parentElement.insertBefore(r, t) : t.parentElement.appendChild(r, t); |
51 | | -} |
52 | | -function i(t) { |
53 | | - const e = t.querySelector('.title'); |
54 | | - return e ? e.textContent.trim() : ''; |
55 | | -} |
56 | | -function a() { |
57 | | - const t = window.location.pathname, |
58 | | - e = /-\/(issues|merge_requests)\/(?<id>\d+)/; |
59 | | - return (e.test(t) ? t.match(e) : { groups: { id: '' } }).groups.id; |
| 42 | + '.detail-page-description:not(.toggl)', |
| 43 | + { observe: true }, |
| 44 | + function (descBlock) { |
| 45 | + if (!descBlock) return |
| 46 | + descBlock.classList.add('toggl') |
| 47 | + |
| 48 | + const id = getIdFromBody() |
| 49 | + const prefix = id ? `MR${id}::` : '' |
| 50 | + |
| 51 | + const titleEl = document.querySelector('[data-testid="title-content"]') |
| 52 | + const title = titleEl ? titleEl.textContent.trim() : '' |
| 53 | + |
| 54 | + const description = [prefix, title].filter(Boolean).join(' ') |
| 55 | + |
| 56 | + const link = togglbutton.createTimerLink({ |
| 57 | + className: 'gitlab', |
| 58 | + description, |
| 59 | + tags: tagsSelector, |
| 60 | + taskId: (projects, tasks) => extractTaskId(projects, tasks), |
| 61 | + projectName: (projects, tasks) => extractProjectName(projects, tasks), |
| 62 | + }) |
| 63 | + |
| 64 | + descBlock.insertAdjacentElement('afterbegin', link) |
| 65 | + }, |
| 66 | +) |
| 67 | + |
| 68 | +// --------------------------- |
| 69 | +// Shared helpers |
| 70 | +// --------------------------- |
| 71 | +function getIdFromBody() { |
| 72 | + const body = document.querySelector('body') |
| 73 | + return body ? body.getAttribute('data-page-type-id') : '' |
60 | 74 | } |
61 | | -function s() { |
62 | | - const t = |
63 | | - $('.title .project-item-select-holder') || |
64 | | - $('.breadcrumbs-list li:nth-last-child(3) .breadcrumb-item-text'); |
65 | | - return t ? t.textContent.trim() : ''; |
| 75 | + |
| 76 | +function getProjectSelector() { |
| 77 | + const el = document.querySelector( |
| 78 | + 'a[data-track-label="project_overview"] div[data-testid="nav-item-link-label"]', |
| 79 | + ) |
| 80 | + return el ? el.textContent.trim() : '' |
66 | 81 | } |
67 | | -function l() { |
68 | | - const t = document.querySelectorAll('div.labels span[data-qa-label-name]'); |
69 | | - if (!t) return []; |
70 | | - const e = []; |
71 | | - for (const n of Object.values(t)) { |
72 | | - const r = n.getAttribute('data-qa-label-name'); |
73 | | - e.push(r); |
| 82 | + |
| 83 | +function tagsSelector() { |
| 84 | + const nodeList = document.querySelectorAll( |
| 85 | + '[data-testid="selected-label-content"] span.gl-label-text', |
| 86 | + ) |
| 87 | + |
| 88 | + const tags = [] |
| 89 | + for (const node of Object.values(nodeList || {})) { |
| 90 | + const tagName = (node.textContent || '').trim() |
| 91 | + if (tagName && !tags.includes(tagName)) tags.push(tagName) |
74 | 92 | } |
75 | | - return e; |
| 93 | + return tags |
76 | 94 | } |
77 | 95 |
|
78 | | -////END GITLAB |
79 | | - |
80 | | -////START HELPER |
81 | | -const ExtractNFGitlabTogglTaskId = () => { |
82 | | - debugger; |
83 | | - let togglTaskId = null; |
84 | | - let tags = l(); |
85 | | - const GITLAB_LABEL_REGEX = /togg(e|)l(::|:)/gim; |
86 | | - const REMOVE_TRALING_LABEL_DESCRIPTION = / .*/; |
87 | | - |
88 | | - debugger; |
89 | | - if (Array.isArray(tags)) { |
90 | | - tags.forEach((element) => { |
91 | | - if (element.match(GITLAB_LABEL_REGEX)) { |
92 | | - togglTaskId = element |
93 | | - .replace(GITLAB_LABEL_REGEX, '') |
94 | | - .toUpperCase() |
95 | | - .replace(REMOVE_TRALING_LABEL_DESCRIPTION, ''); |
96 | | - return; |
97 | | - } |
98 | | - }); |
| 96 | +function extractN4GitlabTogglTaskCode() { |
| 97 | + const tags = tagsSelector() |
| 98 | + if (!Array.isArray(tags) || tags.length === 0) return null |
| 99 | + |
| 100 | + const GITLAB_LABEL_REGEX = /togg(e|)l(::|:)/gim |
| 101 | + const REMOVE_TRAILING_DESCRIPTION = / .*/ |
| 102 | + |
| 103 | + for (const tag of tags) { |
| 104 | + if (GITLAB_LABEL_REGEX.test(tag)) { |
| 105 | + // Reset regex lastIndex since we use 'g' flag |
| 106 | + GITLAB_LABEL_REGEX.lastIndex = 0 |
| 107 | + return tag |
| 108 | + .replace(GITLAB_LABEL_REGEX, '') |
| 109 | + .toUpperCase() |
| 110 | + .replace(REMOVE_TRAILING_DESCRIPTION, '') |
| 111 | + .trim() |
| 112 | + } |
99 | 113 | } |
100 | | - return togglTaskId; |
101 | | -}; |
| 114 | + return null |
| 115 | +} |
102 | 116 |
|
103 | 117 | function extractTaskId(projects, tasks) { |
104 | | - debugger; |
105 | | - let nfTogglTask = ExtractNFGitlabTogglTaskId(); |
106 | | - if (nfTogglTask === null) { |
107 | | - return {}; |
108 | | - } |
| 118 | + const code = extractN4GitlabTogglTaskCode() |
| 119 | + if (!code) return {} |
109 | 120 |
|
110 | | - const n = Object.keys(tasks).filter((o) => |
111 | | - tasks[o].name.startsWith(nfTogglTask) |
112 | | - ), |
113 | | - s = n.length > 0 ? tasks[n[0]].id : {}; |
114 | | - return s; |
| 121 | + const keys = Object.keys(tasks || {}) |
| 122 | + const matchKey = keys.find((k) => (tasks[k]?.name || '').startsWith(code)) |
| 123 | + return matchKey ? tasks[matchKey].id : {} |
115 | 124 | } |
116 | 125 |
|
117 | | -function extractTaskProjectId(nfTogglTask, projects, tasks) { |
118 | | - if (nfTogglTask === null) { |
119 | | - return null; |
120 | | - } |
| 126 | +function extractTaskProjectId(code, projects, tasks) { |
| 127 | + if (!code) return null |
121 | 128 |
|
122 | | - const n = Object.keys(tasks).filter((o) => |
123 | | - tasks[o].name.startsWith(nfTogglTask) |
124 | | - ), |
125 | | - s = n.length > 0 ? tasks[n[0]].project_id : null; |
126 | | - console.log(s); |
127 | | - return s; |
| 129 | + const keys = Object.keys(tasks || {}) |
| 130 | + const matchKey = keys.find((k) => (tasks[k]?.name || '').startsWith(code)) |
| 131 | + return matchKey ? tasks[matchKey].project_id : null |
128 | 132 | } |
129 | 133 |
|
130 | 134 | function extractProjectName(projects, tasks) { |
131 | | - debugger; |
132 | | - let nfTogglTask = ExtractNFGitlabTogglTaskId(); |
133 | | - if (nfTogglTask === null) { |
134 | | - return null; |
135 | | - } |
| 135 | + const code = extractN4GitlabTogglTaskCode() |
| 136 | + |
| 137 | + if (!code) return getProjectSelector() || null |
| 138 | + |
| 139 | + const projectId = extractTaskProjectId(code, projects, tasks) |
| 140 | + if (!projectId) return getProjectSelector() || null |
136 | 141 |
|
137 | | - let projectID = extractTaskProjectId(nfTogglTask, projects, tasks); |
138 | | - const n = Object.keys(projects).filter((o) => projects[o].id === projectID), |
139 | | - s = n.length > 0 ? projects[n[0]].name : null; |
140 | | - console.log(s); |
141 | | - return s; |
| 142 | + const keys = Object.keys(projects || {}) |
| 143 | + const matchKey = keys.find((k) => projects[k]?.id === projectId) |
| 144 | + return matchKey ? projects[matchKey].name : getProjectSelector() || null |
142 | 145 | } |
0 commit comments