← All changes
|
js/components/ManageMenu/SnippetsTable/TableColumns.tsx
+66
-4
3.10.0
→
4.0.0-beta.2
View file →
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { humanTimeDiff } from '@wordpress/date' |
| 2 | 2 | import { RawHTML } from '@wordpress/element' |
| 3 | -import { __ } from '@wordpress/i18n' | |
| 3 | +import { __, sprintf } from '@wordpress/i18n' | |
| 4 | 4 | import React, { Fragment } from 'react' |
| 5 | 5 | import { useSnippetsAPI } from '../../../hooks/useSnippetsAPI' |
| 6 | 6 | import { useSnippetsList } from '../../../hooks/useSnippetsList' |
| 7 | 7 | import { handleUnknownError } from '../../../utils/errors' |
| @@ -6,8 +6,9 @@ | ||
| 6 | 6 | import { useSnippetsList } from '../../../hooks/useSnippetsList' |
| 7 | 7 | import { handleUnknownError } from '../../../utils/errors' |
| 8 | 8 | import { isNetworkAdmin } from '../../../utils/screen' |
| 9 | 9 | import { getSnippetDisplayName, getSnippetEditUrl, getSnippetType } from '../../../utils/snippets/snippets' |
| 10 | +import { getRunOnceNonce } from '../../../utils/restAPI' | |
| 10 | 11 | import { buildUrl } from '../../../utils/urls' |
| 11 | 12 | import { Badge } from '../../common/Badge' |
| 12 | 13 | import { SnippetPriorityInput } from '../../common/snippets/SnippetPriorityInput' |
| 13 | 14 | import { Tooltip } from '../../common/Tooltip' |
| @@ -21,13 +22,32 @@ | ||
| 21 | 22 | interface ColumnProps { |
| 22 | 23 | snippet: Snippet |
| 23 | 24 | } |
| 24 | 25 | |
| 26 | +const runOnceUrl = (snippet: Snippet, nonce: string): string => | |
| 27 | + buildUrl(window.location.href, { | |
| 28 | + action: 'run-once', | |
| 29 | + snippet: snippet.id, | |
| 30 | + network: snippet.network ? 'true' : 'false', | |
| 31 | + _wpnonce: nonce | |
| 32 | + }) | |
| 33 | + | |
| 34 | +// The rendered link carries the nonce from page load. Before any navigation | |
| 35 | +// starts, whether a click, a middle-click or "open in new tab", the href is | |
| 36 | +// rebuilt from the nonce the Heartbeat has refreshed since, so a page left open | |
| 37 | +// still works. | |
| 25 | 38 | const RunOnceButton: React.FC<ColumnProps> = ({ snippet }) => |
| 26 | 39 | <a |
| 27 | 40 | className="snippet-execution-button" |
| 28 | 41 | title={__('Run Once', 'code-snippets')} |
| 29 | - href={buildUrl(window.location.href, { action: 'run-once', snippet: snippet.id })} | |
| 42 | + href={runOnceUrl(snippet, window.CODE_SNIPPETS_MANAGE?.runOnceNonce ?? '')} | |
| 43 | + onMouseDown={event => { | |
| 44 | + event.currentTarget.href = runOnceUrl(snippet, getRunOnceNonce()) | |
| 45 | + }} | |
| 46 | + onClick={event => { | |
| 47 | + event.preventDefault() | |
| 48 | + window.location.assign(runOnceUrl(snippet, getRunOnceNonce())) | |
| 49 | + }} | |
| 30 | 50 | > |
| 31 | 51 | <span className="screen-reader-text">{__('Run Once', 'code-snippets')}</span> |
| 32 | 52 | <span aria-hidden="true"> </span> |
| 33 | 53 | </a> |
| @@ -95,9 +115,9 @@ | ||
| 95 | 115 | </Tooltip>)} |
| 96 | 116 | </div> |
| 97 | 117 | |
| 98 | 118 | export const SnippetName: React.FC<ColumnProps> = ({ snippet }) => |
| 99 | - <> | |
| 119 | + <span className="snippet-name-group"> | |
| 100 | 120 | {!snippet.trashed && (isNetworkAdmin() || !snippet.network || window.CODE_SNIPPETS_MANAGE?.hasNetworkCap) |
| 101 | 121 | ? <a |
| 102 | 122 | href={getSnippetEditUrl(snippet)} |
| 103 | 123 | className="snippet-name" |
| @@ -109,9 +129,9 @@ | ||
| 109 | 129 | {getSnippetDisplayName(snippet)} |
| 110 | 130 | </span>} |
| 111 | 131 | |
| 112 | 132 | {snippet.shared_network && <span className="badge">{__('Shared on Network', 'code-snippets')}</span>} |
| 113 | - </> | |
| 133 | + </span> | |
| 114 | 134 | |
| 115 | 135 | const NameColumn: React.FC<ColumnProps> = ({ snippet }) => |
| 116 | 136 | <> |
| 117 | 137 | <SnippetExtraIcons snippet={snippet} /> |
| @@ -153,8 +173,38 @@ | ||
| 153 | 173 | </time> |
| 154 | 174 | </span> |
| 155 | 175 | : <>—</> |
| 156 | 176 | |
| 177 | +export const PriorityColumn: React.FC<ColumnProps> = ({ snippet }) => | |
| 178 | + <SnippetPriorityInput snippet={snippet} /> | |
| 179 | + | |
| 180 | +export const getAuthorDisplayName = (snippet: Snippet): string => | |
| 181 | + (snippet.updatedBy ?? snippet.createdBy)?.displayName ?? '' | |
| 182 | + | |
| 183 | +const AuthorColumn: React.FC<ColumnProps> = ({ snippet }) => { | |
| 184 | + const creator = snippet.createdBy | |
| 185 | + const updater = snippet.updatedBy | |
| 186 | + const primary = updater ?? creator | |
| 187 | + | |
| 188 | + if (!primary) { | |
| 189 | + return <span className="snippet-author is-unknown">—</span> | |
| 190 | + } | |
| 191 | + | |
| 192 | + const tooltip = creator && updater && creator.id !== updater.id | |
| 193 | + ? sprintf(__('Created by %1$s ยท Last edited by %2$s', 'code-snippets'), creator.displayName, updater.displayName) | |
| 194 | + : creator | |
| 195 | + ? sprintf(__('Created by %s', 'code-snippets'), creator.displayName) | |
| 196 | + : sprintf(__('Last edited by %s', 'code-snippets'), primary.displayName) | |
| 197 | + | |
| 198 | + return ( | |
| 199 | + <span className="snippet-author" title={tooltip}> | |
| 200 | + {primary.avatarUrl && | |
| 201 | + <img className="snippet-author__avatar" src={primary.avatarUrl} alt="" width={20} height={20} />} | |
| 202 | + <span className="snippet-author__name">{primary.displayName}</span> | |
| 203 | + </span> | |
| 204 | + ) | |
| 205 | +} | |
| 206 | + | |
| 157 | 207 | const baseTableColumns: ListTableColumn<Snippet>[] = [ |
| 158 | 208 | { |
| 159 | 209 | id: 'activate', |
| 160 | 210 | title: <span className="screen-reader-text">{__('Activate', 'code-snippets')}</span>, |
| @@ -160,8 +210,14 @@ | ||
| 160 | 210 | title: <span className="screen-reader-text">{__('Activate', 'code-snippets')}</span>, |
| 161 | 211 | render: snippet => <ActivateColumn snippet={snippet} /> |
| 162 | 212 | }, |
| 163 | 213 | { |
| 214 | + id: 'id', | |
| 215 | + title: __('ID', 'code-snippets'), | |
| 216 | + sortedValue: snippet => snippet.id, | |
| 217 | + render: snippet => snippet.id | |
| 218 | + }, | |
| 219 | + { | |
| 164 | 220 | id: 'name', |
| 165 | 221 | title: __('Name', 'code-snippets'), |
| 166 | 222 | isPrimary: true, |
| 167 | 223 | sortedValue: snippet => getSnippetDisplayName(snippet).toLowerCase(), |
| @@ -193,8 +249,14 @@ | ||
| 193 | 249 | id: 'priority', |
| 194 | 250 | title: __('Priority', 'code-snippets'), |
| 195 | 251 | sortedValue: snippet => snippet.priority, |
| 196 | 252 | render: snippet => <SnippetPriorityInput snippet={snippet} /> |
| 253 | + }, | |
| 254 | + { | |
| 255 | + id: 'author', | |
| 256 | + title: __('Author', 'code-snippets'), | |
| 257 | + sortedValue: snippet => getAuthorDisplayName(snippet).toLowerCase(), | |
| 258 | + render: snippet => <AuthorColumn snippet={snippet} /> | |
| 197 | 259 | } |
| 198 | 260 | ] |
| 199 | 261 | |
| 200 | 262 | export const getTableColumns = (hiddenColumns: Set<Key>): ListTableColumn<Snippet>[] => |