DemoSiteTemplatesImport.js
3 years ago
FeedbackNotice.js
1 year ago
Filters.js
1 year ago
ImportTemplatesModal.js
1 year ago
Library.js
1 year ago
ListItem.js
1 year ago
PreviewFrame.js
1 year ago
common.js
2 years ago
Library.js
619 lines
| 1 | /* global tiobDash */ |
| 2 | import classnames from 'classnames'; |
| 3 | import VizSensor from 'react-visibility-sensor'; |
| 4 | |
| 5 | import { chevronLeft, chevronRight, close } from '@wordpress/icons'; |
| 6 | import { useEffect, useState, Fragment, useContext } from '@wordpress/element'; |
| 7 | import { withDispatch, withSelect } from '@wordpress/data'; |
| 8 | import { Spinner, Button, Icon } from '@wordpress/components'; |
| 9 | import { compose } from '@wordpress/compose'; |
| 10 | import { __, isRTL } from '@wordpress/i18n'; |
| 11 | |
| 12 | import { fetchLibrary } from './common'; |
| 13 | import ListItem from './ListItem'; |
| 14 | import Loading from '../Loading'; |
| 15 | import Filters from './Filters'; |
| 16 | import PreviewFrame from './PreviewFrame'; |
| 17 | import ImportTemplatesModal from './ImportTemplatesModal'; |
| 18 | import Logo from '../Icon'; |
| 19 | import { LicensePanelContext } from '../LicensePanelContext'; |
| 20 | import FeedbackNotice from './FeedbackNotice'; |
| 21 | import {EDITOR_MAP} from '../../utils/common'; |
| 22 | import EditorSelector from '../EditorSelector'; |
| 23 | |
| 24 | const Library = ( { |
| 25 | isGeneral, |
| 26 | setPreview, |
| 27 | setTemplateModal, |
| 28 | templateModal, |
| 29 | currentTab, |
| 30 | userStatus, |
| 31 | themeData, |
| 32 | } ) => { |
| 33 | const [ library, setLibrary ] = useState( { |
| 34 | gutenberg: [], |
| 35 | elementor: [], |
| 36 | } ); |
| 37 | const [ type, setType ] = useState( 'gutenberg' ); |
| 38 | const [ toImport, setToImport ] = useState( [] ); |
| 39 | const [ isGrid, setIsGrid ] = useState( isGeneral ); |
| 40 | const [ showFSE, setShowFSE ] = useState( |
| 41 | tiobDash.isFSETheme |
| 42 | ? window?.localStorage?.tpcShowFse === 'true' |
| 43 | : false |
| 44 | ); |
| 45 | const [ searchQuery, setSearchQuery ] = useState( '' ); |
| 46 | const [ currentPage, setCurrentPage ] = useState( { |
| 47 | gutenberg: 0, |
| 48 | elementor: 0, |
| 49 | beaver: 0, |
| 50 | } ); |
| 51 | const [ totalPages, setTotalPages ] = useState( { |
| 52 | gutenberg: 0, |
| 53 | elementor: 0, |
| 54 | beaver: 0, |
| 55 | } ); |
| 56 | const [ isLoading, setLoading ] = useState( false ); |
| 57 | const [ isSearch, setSearch ] = useState( false ); |
| 58 | const [ isFetching, setFetching ] = useState( false ); |
| 59 | const [ previewUrl, setPreviewUrl ] = useState( '' ); |
| 60 | |
| 61 | const [ sortingOrder, setSortingOrder ] = useState( { |
| 62 | templates: { |
| 63 | order: 'DESC', |
| 64 | orderby: 'date', |
| 65 | }, |
| 66 | library: { |
| 67 | order: 'DESC', |
| 68 | orderby: 'date', |
| 69 | }, |
| 70 | } ); |
| 71 | |
| 72 | |
| 73 | |
| 74 | useEffect( () => { |
| 75 | setLoading( true ); |
| 76 | setSearchQuery( '' ); |
| 77 | |
| 78 | setCurrentPage( { |
| 79 | gutenberg: 0, |
| 80 | elementor: 0, |
| 81 | beaver: 0, |
| 82 | } ); |
| 83 | |
| 84 | setTotalPages( { |
| 85 | gutenberg: 0, |
| 86 | elementor: 0, |
| 87 | beaver: 0, |
| 88 | } ); |
| 89 | |
| 90 | loadTemplates(); |
| 91 | }, [ isGeneral, type ] ); |
| 92 | |
| 93 | const EDITORS = { |
| 94 | gutenberg: { |
| 95 | label: __( 'Gutenberg', 'templates-patterns-collection' ), |
| 96 | icon: 'gutenberg.jpg', |
| 97 | }, |
| 98 | elementor: { |
| 99 | label: __( 'Elementor', 'templates-patterns-collection' ), |
| 100 | icon: 'elementor.jpg', |
| 101 | }, |
| 102 | beaver: { |
| 103 | label: __( 'Beaver', 'templates-patterns-collection' ), |
| 104 | icon: 'beaver.jpg', |
| 105 | }, |
| 106 | }; |
| 107 | |
| 108 | const loadTemplates = ( updateItem = {} ) => { |
| 109 | const params = { |
| 110 | page: currentPage[ type ], |
| 111 | per_page: 12, |
| 112 | type, |
| 113 | ...updateItem, |
| 114 | }; |
| 115 | |
| 116 | if ( isGeneral ) { |
| 117 | params.template_site_slug = 'general'; |
| 118 | params.premade = true; |
| 119 | } |
| 120 | |
| 121 | if ( searchQuery ) { |
| 122 | params.search = searchQuery; |
| 123 | } |
| 124 | |
| 125 | if ( type === 'gutenberg' && showFSE ) { |
| 126 | params.type = [ 'gutenberg', 'fse' ]; |
| 127 | } |
| 128 | |
| 129 | fetchLibrary( isGeneral, params ).then( ( r ) => { |
| 130 | setLibrary( { |
| 131 | ...library, |
| 132 | [ type ]: r.templates, |
| 133 | } ); |
| 134 | setTotalPages( { |
| 135 | ...totalPages, |
| 136 | [ type ]: r.total, |
| 137 | } ); |
| 138 | setLoading( false ); |
| 139 | } ); |
| 140 | }; |
| 141 | |
| 142 | const handlePageChange = async ( index = currentPage[ type ] + 1 ) => { |
| 143 | setFetching( true ); |
| 144 | setCurrentPage( { |
| 145 | ...currentPage, |
| 146 | [ type ]: index, |
| 147 | } ); |
| 148 | |
| 149 | const params = { |
| 150 | page: index, |
| 151 | per_page: 12, |
| 152 | type, |
| 153 | }; |
| 154 | |
| 155 | if ( isGeneral ) { |
| 156 | params.template_site_slug = 'general'; |
| 157 | params.premade = true; |
| 158 | } |
| 159 | |
| 160 | if ( searchQuery ) { |
| 161 | params.search = searchQuery; |
| 162 | } |
| 163 | |
| 164 | await fetchLibrary( isGeneral, params ).then( ( r ) => { |
| 165 | setLibrary( { |
| 166 | ...library, |
| 167 | [ type ]: [ ...library[ type ], ...r.templates ], |
| 168 | } ); |
| 169 | setTotalPages( { |
| 170 | ...totalPages, |
| 171 | [ type ]: r.total, |
| 172 | } ); |
| 173 | } ); |
| 174 | |
| 175 | setFetching( false ); |
| 176 | }; |
| 177 | |
| 178 | const handleSearch = ( query = searchQuery ) => { |
| 179 | setLoading( true ); |
| 180 | |
| 181 | if ( query ) { |
| 182 | setSearch( true ); |
| 183 | } else { |
| 184 | setSearch( false ); |
| 185 | } |
| 186 | |
| 187 | const params = { |
| 188 | search: query, |
| 189 | type, |
| 190 | ...getOrder(), |
| 191 | }; |
| 192 | if ( isGeneral ) { |
| 193 | params.template_site_slug = 'general'; |
| 194 | params.premade = true; |
| 195 | } |
| 196 | |
| 197 | if ( type === 'gutenberg' && showFSE ) { |
| 198 | params.type = [ 'gutenberg', 'fse' ]; |
| 199 | } |
| 200 | |
| 201 | fetchLibrary( isGeneral, params ).then( ( r ) => { |
| 202 | setLibrary( { |
| 203 | ...library, |
| 204 | [ type ]: [ ...r.templates ], |
| 205 | } ); |
| 206 | setTotalPages( { |
| 207 | ...totalPages, |
| 208 | [ type ]: r.total, |
| 209 | } ); |
| 210 | setLoading( false ); |
| 211 | } ); |
| 212 | }; |
| 213 | |
| 214 | const handleFSEToggle = () => { |
| 215 | setLoading( true ); |
| 216 | |
| 217 | const newValue = ! showFSE; |
| 218 | setShowFSE( newValue ); |
| 219 | window.localStorage.setItem( 'tpcShowFse', newValue.toString() ); |
| 220 | |
| 221 | const params = { |
| 222 | search: searchQuery, |
| 223 | type, |
| 224 | ...getOrder(), |
| 225 | }; |
| 226 | |
| 227 | if ( isGeneral ) { |
| 228 | params.template_site_slug = 'general'; |
| 229 | params.premade = true; |
| 230 | } |
| 231 | |
| 232 | if ( type === 'gutenberg' && newValue ) { |
| 233 | params.type = [ 'gutenberg', 'fse' ]; |
| 234 | } |
| 235 | |
| 236 | fetchLibrary( isGeneral, params ).then( ( r ) => { |
| 237 | setLibrary( { |
| 238 | ...library, |
| 239 | [ type ]: [ ...r.templates ], |
| 240 | } ); |
| 241 | setTotalPages( { |
| 242 | ...totalPages, |
| 243 | [ type ]: r.total, |
| 244 | } ); |
| 245 | setLoading( false ); |
| 246 | } ); |
| 247 | }; |
| 248 | |
| 249 | const handlePreview = ( url ) => { |
| 250 | setPreviewUrl( url ); |
| 251 | setPreview( true ); |
| 252 | }; |
| 253 | |
| 254 | const handleClose = () => { |
| 255 | setPreviewUrl( '' ); |
| 256 | setPreview( false ); |
| 257 | }; |
| 258 | |
| 259 | const handleImport = ( id ) => { |
| 260 | setToImport( [ id ] ); |
| 261 | setTemplateModal( true ); |
| 262 | }; |
| 263 | |
| 264 | const currentPreviewIndex = |
| 265 | library[ type ] && |
| 266 | library[ type ].findIndex( ( item ) => item.link === previewUrl ); |
| 267 | const previewedItem = |
| 268 | library[ type ] && |
| 269 | library[ type ].find( ( item ) => previewUrl === item.link ); |
| 270 | const wrapClasses = classnames( 'cloud-items', { |
| 271 | 'is-grid': isGrid || ( ! userStatus && ! isGeneral ), |
| 272 | 'is-dummy': ( ! userStatus && ! isGeneral ) || themeData !== false, |
| 273 | } ); |
| 274 | |
| 275 | const handlePrevious = () => { |
| 276 | let newIndex = currentPreviewIndex - 1; |
| 277 | if ( currentPreviewIndex === 0 ) { |
| 278 | newIndex = library[ type ].length - 1; |
| 279 | } |
| 280 | setPreviewUrl( library[ type ][ newIndex ].link ); |
| 281 | }; |
| 282 | |
| 283 | const handleNext = () => { |
| 284 | let newIndex = currentPreviewIndex + 1; |
| 285 | if ( currentPreviewIndex === library[ type ].length - 1 ) { |
| 286 | newIndex = 0; |
| 287 | } |
| 288 | setPreviewUrl( library[ type ][ newIndex ].link ); |
| 289 | }; |
| 290 | |
| 291 | const getOrder = () => { |
| 292 | if ( isGeneral ) { |
| 293 | return sortingOrder.templates; |
| 294 | } |
| 295 | |
| 296 | return sortingOrder.library; |
| 297 | }; |
| 298 | |
| 299 | const setSorting = ( order ) => { |
| 300 | if ( isGeneral ) { |
| 301 | return setSortingOrder( { |
| 302 | ...sortingOrder, |
| 303 | templates: order, |
| 304 | } ); |
| 305 | } |
| 306 | |
| 307 | return setSortingOrder( { |
| 308 | ...sortingOrder, |
| 309 | library: order, |
| 310 | } ); |
| 311 | }; |
| 312 | |
| 313 | const changeOrder = async ( order ) => { |
| 314 | setLoading( true ); |
| 315 | const params = { |
| 316 | search: searchQuery, |
| 317 | type, |
| 318 | ...order, |
| 319 | }; |
| 320 | |
| 321 | if ( isGeneral ) { |
| 322 | params.template_site_slug = 'general'; |
| 323 | params.premade = true; |
| 324 | } |
| 325 | |
| 326 | fetchLibrary( isGeneral, params ).then( ( r ) => { |
| 327 | setLibrary( { |
| 328 | ...library, |
| 329 | [ type ]: [ ...r.templates ], |
| 330 | } ); |
| 331 | setTotalPages( { |
| 332 | ...totalPages, |
| 333 | [ type ]: r.total, |
| 334 | } ); |
| 335 | setLoading( false ); |
| 336 | } ); |
| 337 | }; |
| 338 | |
| 339 | const { upgradeURLTpc } = window.tiobDash; |
| 340 | |
| 341 | const UpsellModal = ( { |
| 342 | title, |
| 343 | description, |
| 344 | showUpgradeBtn = true, |
| 345 | showLicenseToggle = true, |
| 346 | } ) => { |
| 347 | const { setLicenseOpen } = useContext( LicensePanelContext ); |
| 348 | |
| 349 | return ( |
| 350 | <div className={ wrapClasses }> |
| 351 | <div className="table"> |
| 352 | { [ ...Array( 12 ) ].map( ( i, n ) => ( |
| 353 | <ListItem |
| 354 | sortingOrder={ getOrder() } |
| 355 | onPreview={ handlePreview } |
| 356 | userTemplate={ false } |
| 357 | key={ n } |
| 358 | item={ {} } |
| 359 | grid={ true } |
| 360 | /> |
| 361 | ) ) } |
| 362 | </div> |
| 363 | |
| 364 | <div className="upsell-modal-overlay"> |
| 365 | <div className="upsell-modal"> |
| 366 | <div className="upsell-modal-content"> |
| 367 | <div className="info"> |
| 368 | <h3>{ title }</h3> |
| 369 | |
| 370 | <p |
| 371 | dangerouslySetInnerHTML={ { |
| 372 | __html: description, |
| 373 | } } |
| 374 | ></p> |
| 375 | |
| 376 | { showUpgradeBtn && ( |
| 377 | <Button |
| 378 | variant="primary" |
| 379 | isPrimary |
| 380 | href={ upgradeURLTpc } |
| 381 | target="_blank" |
| 382 | > |
| 383 | { __( |
| 384 | 'Upgrade to PRO', |
| 385 | 'templates-patterns-collection' |
| 386 | ) } |
| 387 | </Button> |
| 388 | ) } |
| 389 | |
| 390 | { showLicenseToggle && ( |
| 391 | <Button isLink style={{ marginLeft: '12px' }} href={ tiobDash?.tiobSettings }> |
| 392 | { __( 'I already have a key', 'templates-patterns-collection' ) } |
| 393 | </Button> |
| 394 | ) } |
| 395 | </div> |
| 396 | <div className="icon"> |
| 397 | <Icon icon={ Logo } /> |
| 398 | </div> |
| 399 | </div> |
| 400 | </div> |
| 401 | </div> |
| 402 | </div> |
| 403 | ); |
| 404 | }; |
| 405 | |
| 406 | if ( ! userStatus && ! isGeneral ) { |
| 407 | return ( |
| 408 | <UpsellModal |
| 409 | title={ __( |
| 410 | 'Templates Cloud is a PRO Feature', |
| 411 | 'templates-patterns-collection' |
| 412 | ) } |
| 413 | description={ __( |
| 414 | 'Unlock the Templates Cloud features and save your pages or posts in the cloud.', |
| 415 | 'templates-patterns-collection' |
| 416 | ) } |
| 417 | /> |
| 418 | ); |
| 419 | } |
| 420 | |
| 421 | return ( |
| 422 | <div className={ wrapClasses }> |
| 423 | <> |
| 424 | <div style={ { display: "flex" } }> |
| 425 | <FeedbackNotice importTemplate={templateModal} /> |
| 426 | </div> |
| 427 | <Filters |
| 428 | currentTab={ currentTab } |
| 429 | isGrid={ isGrid } |
| 430 | showFSE={ showFSE } |
| 431 | setShowFSE={ handleFSEToggle } |
| 432 | setGrid={ setIsGrid } |
| 433 | isSearch={ isSearch } |
| 434 | searchQuery={ searchQuery } |
| 435 | setSearchQuery={ setSearchQuery } |
| 436 | onSearch={ handleSearch } |
| 437 | sortingOrder={ getOrder() } |
| 438 | setSortingOrder={ setSorting } |
| 439 | changeOrder={ changeOrder } |
| 440 | type={ type } |
| 441 | setType={ setType } |
| 442 | /> |
| 443 | { isLoading && <Loading isGrid={ isGrid } /> } |
| 444 | { ! isLoading && |
| 445 | ( library[ type ] && library[ type ].length > 0 ? ( |
| 446 | <> |
| 447 | <div className="table"> |
| 448 | { library[ type ].map( ( item ) => ( |
| 449 | <ListItem |
| 450 | sortingOrder={ getOrder() } |
| 451 | onPreview={ handlePreview } |
| 452 | userTemplate={ ! isGeneral } |
| 453 | key={ item.template_id } |
| 454 | item={ item } |
| 455 | loadTemplates={ loadTemplates } |
| 456 | onImport={ () => handleImport( item ) } |
| 457 | grid={ isGrid } |
| 458 | /> |
| 459 | ) ) } |
| 460 | </div> |
| 461 | |
| 462 | <VizSensor |
| 463 | onChange={ ( isVisible ) => { |
| 464 | if ( ! isVisible ) { |
| 465 | return false; |
| 466 | } |
| 467 | |
| 468 | if ( |
| 469 | Number( totalPages[ type ] ) > |
| 470 | currentPage[ type ] |
| 471 | ) { |
| 472 | handlePageChange(); |
| 473 | } |
| 474 | } } |
| 475 | > |
| 476 | <span |
| 477 | style={ { |
| 478 | height: 10, |
| 479 | width: 10, |
| 480 | display: 'block', |
| 481 | } } |
| 482 | /> |
| 483 | </VizSensor> |
| 484 | |
| 485 | { isFetching && ( |
| 486 | <div className="fetching-loader"> |
| 487 | <Spinner /> |
| 488 | </div> |
| 489 | ) } |
| 490 | </> |
| 491 | ) : ( |
| 492 | <div className="empty-information"> |
| 493 | <img |
| 494 | src={ |
| 495 | window.tiobDash.assets + '/img/layout.jpg' |
| 496 | } |
| 497 | alt={ __( |
| 498 | 'No Templates Found', |
| 499 | 'templates-patterns-collection' |
| 500 | ) } |
| 501 | /> |
| 502 | <h3> |
| 503 | { __( |
| 504 | 'There are no templates yet', |
| 505 | 'templates-patterns-collection' |
| 506 | ) } |
| 507 | </h3> |
| 508 | <p> |
| 509 | { __( |
| 510 | 'You can add a page or post to the cloud by accessing it with the WordPress or Elementor/Beaver editor. Learn more about this in our docs.', |
| 511 | 'templates-patterns-collection' |
| 512 | ) } |
| 513 | </p> |
| 514 | <Button |
| 515 | variant="secondary" |
| 516 | isSecondary |
| 517 | href="https://docs.themeisle.com/article/1354-neve-template-cloud-library?utm_medium=nevedashboard&utm_source=wpadmin&utm_campaign=templatescloud&utm_content=neve" |
| 518 | target="_blank" |
| 519 | > |
| 520 | { __( |
| 521 | 'Learn more', |
| 522 | 'templates-patterns-collection' |
| 523 | ) } |
| 524 | </Button> |
| 525 | </div> |
| 526 | ) ) } |
| 527 | { previewUrl && ( |
| 528 | <PreviewFrame |
| 529 | previewUrl={ previewUrl } |
| 530 | rightButtons={ |
| 531 | <Button |
| 532 | isPrimary |
| 533 | onClick={ () => handleImport( previewedItem ) } |
| 534 | > |
| 535 | { __( |
| 536 | 'Import Template', |
| 537 | 'templates-patterns-collection' |
| 538 | ) } |
| 539 | </Button> |
| 540 | } |
| 541 | heading={ previewedItem.template_name } |
| 542 | leftButtons={ |
| 543 | <> |
| 544 | <Button |
| 545 | icon={ close } |
| 546 | onClick={ handleClose } |
| 547 | /> |
| 548 | { library[ type ].length > 1 && ( |
| 549 | <> |
| 550 | <Button |
| 551 | icon={ |
| 552 | isRTL() |
| 553 | ? chevronRight |
| 554 | : chevronLeft |
| 555 | } |
| 556 | onClick={ handlePrevious } |
| 557 | /> |
| 558 | <Button |
| 559 | icon={ |
| 560 | isRTL() |
| 561 | ? chevronLeft |
| 562 | : chevronRight |
| 563 | } |
| 564 | onClick={ handleNext } |
| 565 | /> |
| 566 | </> |
| 567 | ) } |
| 568 | </> |
| 569 | } |
| 570 | /> |
| 571 | ) } |
| 572 | </> |
| 573 | { templateModal && |
| 574 | toImport && |
| 575 | ! isLoading && |
| 576 | toImport.length > 0 && ( |
| 577 | <ImportTemplatesModal |
| 578 | generalTemplates={ true } |
| 579 | isUserTemplate={ ! isGeneral } |
| 580 | templatesData={ toImport } |
| 581 | /> |
| 582 | ) } |
| 583 | </div> |
| 584 | ); |
| 585 | }; |
| 586 | |
| 587 | export default compose( |
| 588 | withDispatch( ( dispatch ) => { |
| 589 | const { |
| 590 | setInstallModalStatus, |
| 591 | setTemplateModal, |
| 592 | setPreviewStatus, |
| 593 | } = dispatch( 'neve-onboarding' ); |
| 594 | |
| 595 | return { |
| 596 | setPreview: ( status ) => setPreviewStatus( status ), |
| 597 | setInstallModal: ( status ) => setInstallModalStatus( status ), |
| 598 | setTemplateModal, |
| 599 | }; |
| 600 | } ), |
| 601 | withSelect( ( select ) => { |
| 602 | const { |
| 603 | getTemplateModal, |
| 604 | getThemeAction, |
| 605 | getCurrentEditor, |
| 606 | getCurrentTab, |
| 607 | getUserStatus, |
| 608 | } = select( 'neve-onboarding' ); |
| 609 | |
| 610 | return { |
| 611 | templateModal: getTemplateModal(), |
| 612 | themeData: getThemeAction() || false, |
| 613 | editor: getCurrentEditor(), |
| 614 | currentTab: getCurrentTab(), |
| 615 | userStatus: getUserStatus(), |
| 616 | }; |
| 617 | } ) |
| 618 | )( Library ); |
| 619 |