PluginProbe
Code Snippets / 4.0.0-beta.1
Code Snippets v4.0.0-beta.1
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / components / ManageMenu / CommunityCloud / SearchResult.tsx

SearchResult.tsx in Code Snippets 4.0.0-beta.1, at js/components/ManageMenu/CommunityCloud/SearchResult.tsx

219 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React, { useEffect, useState } from 'react'
2 import classnames from 'classnames'
3 import { __, _x } from '@wordpress/i18n'
4 import { Modal, Spinner } from '@wordpress/components'
5 import { useRestAPI } from '../../../hooks/useRestAPI'
6 import { CloudStatus } from '../../../types/schema/CloudSnippetSchema'
7 import { Prism } from '../../../utils/Prism'
8 import { REST_BASES } from '../../../utils/restAPI'
9 import { isLicensed } from '../../../utils/screen'
10 import { getSnippetEditUrl, getSnippetType, isProSnippet } from '../../../utils/snippets/snippets'
11 import { Badge } from '../../common/Badge'
12 import { Button } from '../../common/Button'
13 import { ErrorTooltip } from '../../common/Tooltip'
14 import { UpsellDialog } from '../../common/UpsellDialog'
15 import { STATUS_LABELS } from './SearchFilters'
16 import type { CloudSnippetSchema } from '../../../types/schema/CloudSnippetSchema'
17
18 const MAX_DESCRIPTION_LENGTH = 150
19
20 interface DownloadSnippetResponse {
21 success: boolean
22 snippet_id: number
23 link_id: number
24 }
25
26 interface CloudSnippetDetailsProps {
27 snippet: CloudSnippetSchema
28 setIsPreviewOpen: (isOpen: boolean) => void
29 }
30
31 const CloudSnippetDetails: React.FC<CloudSnippetDetailsProps> = ({ snippet, setIsPreviewOpen }) =>
32 <div className="cloud-snippet">
33 <h3>
34 <button
35 type="button"
36 className="cloud-snippet-title-button"
37 title={__('Preview this snippet', 'code-snippets')}
38 onClick={() => {
39 setIsPreviewOpen(true)
40 }}
41 >
42 {snippet.name}
43 </button>
44 </h3>
45
46 <div className="cloud-snippet-meta">
47 <Badge name={getSnippetType(snippet)} />
48 <span className="cloud-snippet-votes">
49 <span className="dashicons dashicons-thumbs-up" aria-hidden="true"></span>
50 <span>
51 {snippet.vote_count}
52 <span className="screen-reader-text">{` ${__('votes', 'code-snippets')}`}</span>
53 </span>
54 </span>
55 {0 < snippet.tags.length
56 ? <span className="cloud-snippet-category">
57 <strong>{__('Category: ', 'code-snippets')}</strong>
58 {snippet.tags[0]}
59 </span>
60 : null}
61 </div>
62
63 {snippet.description && (
64 <p className="cloud-snippet-description">
65 {snippet.description.length > MAX_DESCRIPTION_LENGTH
66 ? `${snippet.description.slice(0, MAX_DESCRIPTION_LENGTH)}…`
67 : snippet.description}
68 </p>
69 )}
70
71 <p className="cloud-snippet-author">
72 {_x('by ', 'snippet author', 'code-snippets')}
73 <a href={`${window.CODE_SNIPPETS?.urls.cloud}/codevault/${snippet.codevault}`} target="_blank" rel="noopener noreferrer">
74 {snippet.codevault}
75 </a>
76 </p>
77 </div>
78
79 interface PreviewModalProps {
80 isOpen: boolean
81 snippet: CloudSnippetSchema
82 setIsOpen: (isOpen: boolean) => void
83 }
84
85 const PreviewModal: React.FC<PreviewModalProps> = ({ snippet, isOpen, setIsOpen }) => {
86 const snippetType = getSnippetType(snippet)
87
88 return isOpen
89 ? <Modal onRequestClose={() => setIsOpen(false)} title={snippet.name}>
90 <pre className="line-numbers">
91 <code className={`language-${snippetType}`}>
92 {'php' === snippetType ? '<?php\n\n' : ''}
93 {snippet.code}
94 </code>
95 </pre>
96 </Modal>
97 : null
98 }
99
100 interface DownloadOrViewButtonProps {
101 snippet: CloudSnippetSchema
102 isWorking: boolean
103 onDownload: VoidFunction
104 localSnippetId: number | undefined
105 }
106
107 const DownloadOrViewButton: React.FC<DownloadOrViewButtonProps> = ({
108 snippet,
109 isWorking,
110 onDownload,
111 localSnippetId,
112 }) => {
113 const [isUpsellOpen, setIsUpsellOpen] = useState(false)
114
115 if (localSnippetId) {
116 return (
117 <a className="button button-primary" href={getSnippetEditUrl({ id: localSnippetId })} target="_blank" rel="noopener noreferrer">
118 {__('View', 'code-snippets')}
119 </a>
120 )
121 }
122
123 if (isProSnippet(snippet) && !isLicensed()) {
124 return (
125 <>
126 <Button className="cloud-pro-button" onClick={() => setIsUpsellOpen(true)}>
127 {__('Pro Only', 'code-snippets')}
128 </Button>
129 <UpsellDialog isOpen={isUpsellOpen} setIsOpen={setIsUpsellOpen} />
130 </>
131 )
132 }
133
134 return (
135 <Button primary onClick={onDownload} disabled={isWorking}>
136 {__('Download', 'code-snippets')}
137 </Button>
138 )
139 }
140
141 interface DownloadButtonProps {
142 snippet: CloudSnippetSchema
143 }
144
145 const DownloadButton: React.FC<DownloadButtonProps> = ({ snippet }) => {
146 const { api } = useRestAPI()
147 const [isWorking, setIsWorking] = useState(false)
148 const [errorMessage, setErrorMessage] = useState<string>()
149 const [localSnippetId, setLocalSnippetId] = useState<number>()
150
151 const handleDownload = () => {
152 setIsWorking(true)
153 setErrorMessage(undefined)
154
155 api.post<DownloadSnippetResponse>(`${REST_BASES.cloud.snippets}/${snippet.id}/download`)
156 .then(response => {
157 setLocalSnippetId(response.snippet_id)
158 })
159 .catch((error: unknown) => {
160 setErrorMessage('string' === typeof error
161 ? error
162 : __('An error occurred while trying to download the snippet. Please try again later.', 'code-snippets'))
163 })
164 .finally(() => setIsWorking(false))
165 }
166
167 return (
168 <>
169 {isWorking && <Spinner />}
170 {errorMessage && <ErrorTooltip message={errorMessage} />}
171
172 <DownloadOrViewButton
173 snippet={snippet}
174 isWorking={isWorking}
175 onDownload={handleDownload}
176 localSnippetId={localSnippetId}
177 />
178 </>
179 )
180 }
181
182 export interface SearchResultProps {
183 snippet: CloudSnippetSchema
184 }
185
186 export const SearchResult: React.FC<SearchResultProps> = ({ snippet }) => {
187 const [isPreviewOpen, setIsPreviewOpen] = useState(false)
188
189 useEffect(() => {
190 if (isPreviewOpen) {
191 Prism.highlightAll()
192 }
193 }, [isPreviewOpen])
194
195 return (
196 <li className="cloud-search-result">
197 <CloudSnippetDetails snippet={snippet} setIsPreviewOpen={setIsPreviewOpen} />
198
199 <footer>
200 <span className={classnames(
201 'cloud-snippet-status',
202 `cloud-snippet-status-${CloudStatus[snippet.status].toLowerCase().replace('_', '-')}`
203 )}>
204 {STATUS_LABELS[snippet.status]}
205 </span>
206
207 <DownloadButton snippet={snippet} />
208
209 <Button secondary onClick={() => setIsPreviewOpen(true)}>
210 {__('Preview', 'code-snippets')}
211 </Button>
212
213 </footer>
214
215 <PreviewModal snippet={snippet} isOpen={isPreviewOpen} setIsOpen={setIsPreviewOpen} />
216 </li>
217 )
218 }
219