Skip to content

Commit 8e1132f

Browse files
feat: add neve sidebar promo in the dashboard (#2290)
I've added the neve theme promo notice in the otter dashboard. For more details here: Codeinwp/otter-internals#178 (comment)
1 parent 7b54161 commit 8e1132f

File tree

7 files changed

+308
-15
lines changed

7 files changed

+308
-15
lines changed

assets/images/neve-logo.png

3.22 KB
Loading

assets/images/neve-upsell-img.png

753 KB
Loading

assets/images/star.png

1.37 KB
Loading

inc/plugins/class-dashboard.php

+39-12
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public function enqueue_options_assets() {
206206
wp_enqueue_script(
207207
'otter-blocks-scripts',
208208
OTTER_BLOCKS_URL . 'build/dashboard/index.js',
209-
$asset_file['dependencies'],
209+
array_merge( $asset_file['dependencies'], [ 'updates' ] ),
210210
$asset_file['version'],
211211
true
212212
);
@@ -221,17 +221,44 @@ public function enqueue_options_assets() {
221221
apply_filters(
222222
'otter_dashboard_data',
223223
array(
224-
'version' => OTTER_BLOCKS_VERSION,
225-
'assetsPath' => OTTER_BLOCKS_URL . 'assets/',
226-
'stylesExist' => is_dir( $basedir ) || boolval( get_transient( 'otter_animations_parsed' ) ),
227-
'hasPro' => Pro::is_pro_installed(),
228-
'upgradeLink' => tsdk_utmify( Pro::get_url(), 'options', Pro::get_reference() ),
229-
'docsLink' => Pro::get_docs_url(),
230-
'showFeedbackNotice' => $this->should_show_feedback_notice(),
231-
'deal' => ! Pro::is_pro_installed() ? $offer->get_localized_data() : array(),
232-
'hasOnboarding' => false !== get_theme_support( FSE_Onboarding::SUPPORT_KEY ),
233-
'days_since_install' => round( ( time() - get_option( 'otter_blocks_install', time() ) ) / DAY_IN_SECONDS ),
234-
'rootUrl' => get_site_url(),
224+
'version' => OTTER_BLOCKS_VERSION,
225+
'assetsPath' => OTTER_BLOCKS_URL . 'assets/',
226+
'stylesExist' => is_dir( $basedir ) || boolval( get_transient( 'otter_animations_parsed' ) ),
227+
'hasPro' => Pro::is_pro_installed(),
228+
'upgradeLink' => tsdk_utmify( Pro::get_url(), 'options', Pro::get_reference() ),
229+
'docsLink' => Pro::get_docs_url(),
230+
'showFeedbackNotice' => $this->should_show_feedback_notice(),
231+
'deal' => ! Pro::is_pro_installed() ? $offer->get_localized_data() : array(),
232+
'hasOnboarding' => false !== get_theme_support( FSE_Onboarding::SUPPORT_KEY ),
233+
'days_since_install' => round( ( time() - get_option( 'otter_blocks_install', time() ) ) / DAY_IN_SECONDS ),
234+
'rootUrl' => get_site_url(),
235+
'neveThemePreviewUrl' => esc_url(
236+
add_query_arg(
237+
array(
238+
'theme' => 'neve',
239+
),
240+
admin_url( 'theme-install.php' )
241+
)
242+
),
243+
'neveThemeActivationUrl' => esc_url(
244+
add_query_arg(
245+
array(
246+
'action' => 'activate',
247+
'stylesheet' => 'neve',
248+
'_wpnonce' => wp_create_nonce( 'switch-theme_neve' ),
249+
),
250+
admin_url( 'themes.php' )
251+
)
252+
),
253+
'neveDashboardUrl' => esc_url(
254+
add_query_arg(
255+
array(
256+
'page' => 'neve-welcome',
257+
),
258+
admin_url( 'admin.php' )
259+
)
260+
),
261+
'neveInstalled' => defined( 'NEVE_VERSION' ),
235262
)
236263
)
237264
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* WordPress dependencies.
3+
*/
4+
import { __ } from '@wordpress/i18n';
5+
6+
import {
7+
Button,
8+
ExternalLink
9+
} from '@wordpress/components';
10+
11+
import {
12+
Fragment,
13+
useEffect,
14+
useState
15+
} from '@wordpress/element';
16+
17+
// Install theme.
18+
const InstallTheme = ( slug ) => {
19+
return new Promise( ( resolve ) => {
20+
wp.updates.ajax( 'install-theme', {
21+
slug,
22+
success: () => {
23+
resolve({ success: true });
24+
},
25+
error: ( err ) => {
26+
resolve({ success: false, code: err.errorCode });
27+
}
28+
});
29+
});
30+
};
31+
32+
// Activate theme.
33+
const activateTheme = ( url ) => {
34+
return new Promise( ( resolve ) => {
35+
jQuery
36+
.get( url )
37+
.done( () => {
38+
resolve({ success: true });
39+
})
40+
.fail( () => {
41+
resolve({ success: false });
42+
});
43+
});
44+
};
45+
46+
47+
const NeveSidebarUpsell = () => {
48+
49+
const [ isShowNeveUpsell, setShowNeveUpsell ] = useState( false );
50+
const [ isNeveInstalled, setNeveInstalled ] = useState( window.otterObj?.neveInstalled );
51+
const [ showStatus, setShowStatus ] = useState( false );
52+
const [ progress, setProgress ] = useState( null );
53+
54+
useEffect( () => {
55+
if ( Boolean( isNeveInstalled ) ) {
56+
setShowNeveUpsell( true );
57+
}
58+
}, [ isNeveInstalled ]);
59+
60+
const ThemeInstallActivateRequest = async( e ) => {
61+
e.preventDefault();
62+
setShowStatus( true );
63+
setProgress( 'installing' );
64+
await InstallTheme( 'neve' );
65+
66+
setProgress( 'activating' );
67+
await activateTheme( window.otterObj?.neveThemeActivationUrl );
68+
69+
setProgress( 'done' );
70+
};
71+
72+
const installThemeRequestStatus = () => {
73+
if ( 'done' === progress ) {
74+
return (
75+
<div className={'done'}>
76+
<p> { __( 'Awesome! You are all set!', 'otter-blocks' ) }</p>
77+
<Button icon={'external'} isPrimary href={window.otterObj?.neveDashboardUrl} target="_blank">
78+
{ __( 'Go to Neve dashboard', 'otter-blocks' ) }
79+
</Button>
80+
</div>
81+
);
82+
}
83+
if ( progress ) {
84+
return (
85+
<p className="otter-neve-progress">
86+
<span className="dashicons dashicons-update spin"/>
87+
<span>
88+
{ 'installing' === progress && __( 'Installing', 'otter-blocks' ) }
89+
{ 'activating' === progress && __( 'Activating', 'otter-blocks' ) }
90+
&hellip;
91+
</span>
92+
</p>
93+
);
94+
}
95+
};
96+
97+
return (
98+
<Fragment>
99+
{( ! isShowNeveUpsell &&
100+
<div className="otter-nv-sidebar-upsell">
101+
<div className="otter-nv-sidebar-left">
102+
<div className="otter-nv-sidebar-heading">
103+
<img src={ window.otterObj.assetsPath + 'images/neve-logo.png' } />
104+
<h2>{ __( '- Experience lightning fast performance!', 'otter-blocks' ) }</h2>
105+
</div>
106+
<div className="otter-nv-sidebar-text">
107+
<p>
108+
<strong>{ __( 'Fast, Flexible, and Free:', 'otter-blocks' ) }</strong> { __( 'Whether you\'re managing a blog, an online store, or a business website, Neve ensures top-tier responsiveness and SEO optimization. Install now and experience the difference today.', 'otter-blocks' ) }
109+
</p>
110+
</div>
111+
<div className="otter-nv-sidebar-active-website">
112+
<div>
113+
<span>300,000+</span>
114+
{ __( 'Active websites', 'otter-blocks' ) }
115+
</div>
116+
<div>
117+
<span>1050+</span>
118+
<img src={ window.otterObj.assetsPath + 'images/star.png' } width = '57' />
119+
</div>
120+
</div>
121+
<div className="otter-nv-sidebar-action">
122+
{( ! showStatus && 'done' !== progress ) && (
123+
<Button variant="primary" onClick={ThemeInstallActivateRequest}>{ __( 'Install & Activate Neve Theme', 'otter-blocks' ) }</Button>
124+
)}
125+
{( showStatus || 'done' === progress ) && installThemeRequestStatus()}
126+
{( ! showStatus && 'done' !== progress ) && ( <Button icon={'external'} iconPosition={'right'} variant="secondary" href={window.otterObj?.neveThemePreviewUrl} target="_blank">{ __( 'Theme Preview', 'otter-blocks' ) }</Button>
127+
)}
128+
</div>
129+
</div>
130+
<div className="otter-nv-sidebar-right">
131+
<img src={ window.otterObj.assetsPath + 'images/neve-upsell-img.png' } />
132+
</div>
133+
</div>
134+
)}
135+
</Fragment>
136+
);
137+
};
138+
139+
export default NeveSidebarUpsell;

src/dashboard/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import Header from './components/Header.js';
2323
import Main from './components/Main.js';
2424
import Footer from './components/Footer.js';
2525
import useSettings from '../blocks/helpers/use-settings.js';
26+
import NeveSidebarUpsell from './components/NeveSidebarUpsell.js';
2627

2728
if ( undefined === window.otterUtils ) {
2829
window.otterUtils = {};
@@ -78,7 +79,7 @@ const App = () => {
7879
currentTab={ currentTab }
7980
setTab={ setTab }
8081
/>
81-
82+
<NeveSidebarUpsell />
8283
<Footer />
8384
</Fragment>
8485
);

src/dashboard/style.scss

+128-2
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@
598598
.o-blocks-header {
599599
display: flex;
600600
flex-direction: row;
601-
margin: 20px 20px 0 20px;
601+
margin: 20px 0 0 0;
602602

603603
.o-blocks-header__left {
604604
display: flex;
@@ -646,7 +646,7 @@
646646
display: grid;
647647
flex-wrap: wrap;
648648
gap: 20px;
649-
margin: 10px 20px;
649+
margin: 10px 0;
650650

651651
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
652652

@@ -811,6 +811,132 @@
811811
}
812812
}
813813

814+
.otter-nv-sidebar-upsell {
815+
max-width: 930px;
816+
margin: 0 auto;
817+
background: #fff;
818+
border: 1px solid #e2e4e7;
819+
border-radius: 10px;
820+
display: flex;
821+
align-items: center;
822+
flex-wrap: wrap;
823+
.otter-nv-sidebar-left{
824+
width: calc(100% - 360px);
825+
padding: 32px;
826+
@media(max-width: 991px) {
827+
width: 100%;
828+
padding: 16px;
829+
}
830+
}
831+
.otter-nv-sidebar-right{
832+
width: 360px;
833+
@media(max-width: 991px) {
834+
width: 100%;
835+
}
836+
img{
837+
max-width: 100%;
838+
width: 100%;
839+
height: auto;
840+
display: block;
841+
}
842+
}
843+
.otter-nv-sidebar-heading{
844+
display: flex;
845+
align-items: center;
846+
gap: 8px;
847+
padding-bottom: 24px;
848+
img{
849+
max-width: 155px;
850+
flex-shrink: 0;
851+
height: auto;
852+
}
853+
h2{
854+
margin: 0;
855+
color: #3B5DE6;
856+
font-size: 16px;
857+
}
858+
}
859+
.otter-nv-sidebar-text{
860+
padding-bottom: 12px;
861+
p{
862+
margin: 0;
863+
}
864+
}
865+
.otter-nv-sidebar-action{
866+
padding-top: 24px;
867+
display: flex;
868+
flex-wrap: wrap;
869+
gap: 8px;
870+
.components-button{
871+
&.is-primary{
872+
background: #3B5DE6 !important;
873+
&:focus:not(:disabled){
874+
box-shadow: none !important;
875+
}
876+
}
877+
&.is-secondary{
878+
.components-external-link__contents{
879+
text-decoration: none;
880+
}
881+
}
882+
}
883+
.done {
884+
display: flex;
885+
flex-direction: column;
886+
align-items: flex-start;
887+
a {
888+
width: auto;
889+
}
890+
p {
891+
font-size: 15px;
892+
font-weight: 500;
893+
}
894+
}
895+
}
896+
.otter-nv-sidebar-active-website{
897+
display: flex;
898+
gap: 24px;
899+
flex-wrap: wrap;
900+
color: #1F1D1D;
901+
>div{
902+
font-size: 10px;
903+
span{
904+
display: block;
905+
font-size: 12px;
906+
font-weight: 700
907+
}
908+
}
909+
}
910+
.otter-neve-progress {
911+
margin: 0;
912+
gap: 5px;
913+
font-size: 14px;
914+
display: flex;
915+
align-items: center;
916+
.spin {
917+
animation: otter-neve-rotation 2s infinite linear;
918+
}
919+
}
920+
}
921+
922+
.otter-main {
923+
&.is-upsell + .otter-nv-sidebar-upsell {
924+
margin-top: 25px;
925+
}
926+
&.is-blocks + .otter-nv-sidebar-upsell {
927+
margin-top: 25px;
928+
}
929+
}
930+
931+
@keyframes otter-neve-rotation {
932+
0% {
933+
transform: rotate(0deg);
934+
}
935+
100% {
936+
transform: rotate(359deg);
937+
}
938+
}
939+
814940
@media(max-width: 480px) {
815941
.otter-deal .o-urgency {
816942
font-size: 7px;

0 commit comments

Comments
 (0)