Skip to content

Commit 595924c

Browse files
authored
Merge pull request #288 from wmde/C23_WMDE_Mobile_DE_10
2 parents 6d05354 + 63d1d8e commit 595924c

29 files changed

+1371
-44
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { createVueApp } from '@src/createVueApp';
2+
3+
import './styles/styles.scss';
4+
5+
import BannerConductor from '@src/components/BannerConductor/BannerConductor.vue';
6+
import Banner from './components/BannerCtrl.vue';
7+
import getBannerDelay from '@src/utils/getBannerDelay';
8+
import { WindowResizeHandler } from '@src/utils/ResizeHandler';
9+
import PageWPORG from '@src/page/PageWPORG';
10+
import { WindowMediaWiki } from '@src/page/MediaWiki/WindowMediaWiki';
11+
import { SkinFactory } from '@src/page/skin/SkinFactory';
12+
import { WindowSizeIssueChecker } from '@src/utils/SizeIssueChecker/WindowSizeIssueChecker';
13+
import TranslationPlugin from '@src/TranslationPlugin';
14+
import { Translator } from '@src/Translator';
15+
import DynamicTextPlugin from '@src/DynamicTextPlugin';
16+
import { LocalImpressionCount } from '@src/utils/LocalImpressionCount';
17+
import { WindowPageScroller } from '@src/utils/PageScroller/WindowPageScroller';
18+
import { LegacyTrackerWPORG } from '@src/tracking/LegacyTrackerWPORG';
19+
import eventMappings from './event_map';
20+
21+
// Locale-specific imports
22+
import messages from './messages';
23+
import { LocaleFactoryDe } from '@src/utils/LocaleFactory/LocaleFactoryDe';
24+
25+
// Channel specific form setup
26+
import { createFormItems } from './form_items';
27+
import { createFormActions } from '@src/createFormActions';
28+
29+
const localeFactory = new LocaleFactoryDe();
30+
const translator = new Translator( messages );
31+
const mediaWiki = new WindowMediaWiki();
32+
const page = new PageWPORG( mediaWiki, ( new SkinFactory( mediaWiki ) ).getSkin(), new WindowSizeIssueChecker() );
33+
const impressionCount = new LocalImpressionCount( page.getTracking().keyword );
34+
const tracker = new LegacyTrackerWPORG( mediaWiki, page.getTracking().keyword, eventMappings );
35+
const remainingImpressions = Math.max( page.getMaxBannerImpressions( 'mobile' ) - impressionCount.overallCountIncremented, 0 );
36+
37+
const app = createVueApp( BannerConductor, {
38+
page,
39+
bannerConfig: {
40+
delay: getBannerDelay( 7500 ),
41+
transitionDuration: 1000
42+
},
43+
bannerProps: {
44+
useOfFundsContent: localeFactory.getUseOfFundsLoader().getContent(),
45+
pageScroller: new WindowPageScroller(),
46+
remainingImpressions
47+
},
48+
resizeHandler: new WindowResizeHandler(),
49+
banner: Banner,
50+
impressionCount
51+
} );
52+
53+
app.use( TranslationPlugin, translator );
54+
app.use( DynamicTextPlugin, {
55+
campaignParameters: page.getCampaignParameters(),
56+
date: new Date(),
57+
formatters: localeFactory.getFormatters(),
58+
impressionCount,
59+
translator
60+
} );
61+
const currencyFormatter = localeFactory.getCurrencyFormatter();
62+
63+
app.provide( 'currencyFormatter', currencyFormatter );
64+
app.provide( 'formItems', createFormItems( translator, currencyFormatter.euroAmount.bind( currencyFormatter ) ) );
65+
app.provide( 'formActions', createFormActions( page.getTracking(), impressionCount ) );
66+
app.provide( 'tracker', tracker );
67+
68+
app.mount( page.getBannerContainer() );
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { createVueApp } from '@src/createVueApp';
2+
3+
import './styles/styles.scss';
4+
5+
import BannerConductor from '@src/components/BannerConductor/BannerConductor.vue';
6+
import Banner from './components/BannerVar.vue';
7+
import getBannerDelay from '@src/utils/getBannerDelay';
8+
import { WindowResizeHandler } from '@src/utils/ResizeHandler';
9+
import PageWPORG from '@src/page/PageWPORG';
10+
import { WindowMediaWiki } from '@src/page/MediaWiki/WindowMediaWiki';
11+
import { SkinFactory } from '@src/page/skin/SkinFactory';
12+
import { WindowSizeIssueChecker } from '@src/utils/SizeIssueChecker/WindowSizeIssueChecker';
13+
import TranslationPlugin from '@src/TranslationPlugin';
14+
import { Translator } from '@src/Translator';
15+
import DynamicTextPlugin from '@src/DynamicTextPlugin';
16+
import { LocalImpressionCount } from '@src/utils/LocalImpressionCount';
17+
import { WindowPageScroller } from '@src/utils/PageScroller/WindowPageScroller';
18+
import { LegacyTrackerWPORG } from '@src/tracking/LegacyTrackerWPORG';
19+
import eventMappings from './event_map';
20+
21+
// Locale-specific imports
22+
import messages from './messages';
23+
import { LocaleFactoryDe } from '@src/utils/LocaleFactory/LocaleFactoryDe';
24+
25+
// Channel specific form setup
26+
import { createFormItems } from './form_items';
27+
import { createFormActions } from '@src/createFormActions';
28+
29+
const localeFactory = new LocaleFactoryDe();
30+
const translator = new Translator( messages );
31+
const mediaWiki = new WindowMediaWiki();
32+
const page = new PageWPORG( mediaWiki, ( new SkinFactory( mediaWiki ) ).getSkin(), new WindowSizeIssueChecker() );
33+
const impressionCount = new LocalImpressionCount( page.getTracking().keyword );
34+
const tracker = new LegacyTrackerWPORG( mediaWiki, page.getTracking().keyword, eventMappings );
35+
const remainingImpressions = Math.max( page.getMaxBannerImpressions( 'mobile' ) - impressionCount.overallCountIncremented, 0 );
36+
37+
const app = createVueApp( BannerConductor, {
38+
page,
39+
bannerConfig: {
40+
delay: getBannerDelay( 7500 ),
41+
transitionDuration: 1000
42+
},
43+
bannerProps: {
44+
useOfFundsContent: localeFactory.getUseOfFundsLoader().getContent(),
45+
pageScroller: new WindowPageScroller(),
46+
remainingImpressions
47+
},
48+
resizeHandler: new WindowResizeHandler(),
49+
banner: Banner,
50+
impressionCount
51+
} );
52+
53+
app.use( TranslationPlugin, translator );
54+
app.use( DynamicTextPlugin, {
55+
campaignParameters: page.getCampaignParameters(),
56+
date: new Date(),
57+
formatters: localeFactory.getFormatters(),
58+
impressionCount,
59+
translator
60+
} );
61+
const currencyFormatter = localeFactory.getCurrencyFormatter();
62+
63+
app.provide( 'currencyFormatter', currencyFormatter );
64+
app.provide( 'formItems', createFormItems( translator, currencyFormatter.euroAmount.bind( currencyFormatter ) ) );
65+
app.provide( 'formActions', createFormActions( page.getTracking(), impressionCount ) );
66+
app.provide( 'tracker', tracker );
67+
68+
app.mount( page.getBannerContainer() );
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
<template>
2+
<div class="wmde-banner-wrapper" :class="contentState">
3+
<MiniBanner
4+
@close="onCloseMiniBanner"
5+
@show-full-page-banner="onshowFullPageBanner"
6+
@show-full-page-banner-preselected="onshowFullPageBannerPreselected"
7+
>
8+
<template #banner-slides>
9+
<KeenSlider :with-navigation="false" :play="slideshowShouldPlay" :interval="5000">
10+
11+
<template #slides="{ currentSlide }: any">
12+
<BannerSlides :currentSlide="currentSlide"/>
13+
</template>
14+
15+
</KeenSlider>
16+
</template>
17+
</MiniBanner>
18+
19+
<FullPageBanner
20+
@showFundsModal="isFundsModalVisible = true"
21+
@close="() => onClose( 'FullPageBanner', CloseChoices.Hide )"
22+
>
23+
<template #banner-text>
24+
<BannerText/>
25+
</template>
26+
27+
<template #progress>
28+
<ProgressBar amount-to-show-on-right="TARGET"/>
29+
</template>
30+
31+
<template #donation-form="{ formInteraction }: any">
32+
<MultiStepDonation :step-controllers="stepControllers" @form-interaction="formInteraction" :page-scroller="pageScroller">
33+
34+
<template #[FormStepNames.MainDonationFormStep]="{ pageIndex, submit, isCurrent, previous }: any">
35+
<MainDonationForm :page-index="pageIndex" @submit="submit" :is-current="isCurrent" @previous="previous"/>
36+
</template>
37+
38+
<template #[FormStepNames.UpgradeToYearlyFormStep]="{ pageIndex, submit, isCurrent, previous }: any">
39+
<UpgradeToYearlyButtonForm :page-index="pageIndex" @submit="submit" :is-current="isCurrent" @previous="previous">
40+
<template #back>
41+
<ChevronLeftIcon/> {{ $translate( 'back-button' ) }}
42+
</template>
43+
</UpgradeToYearlyButtonForm>
44+
</template>
45+
46+
</MultiStepDonation>
47+
</template>
48+
49+
<template #footer>
50+
<BannerFooter :show-funds-link="false"/>
51+
</template>
52+
</FullPageBanner>
53+
54+
<SoftClose
55+
v-if="contentState === ContentStates.SoftClosing"
56+
@close="() => onClose( 'SoftClose', CloseChoices.Close )"
57+
@maybe-later="() => onClose( 'SoftClose', CloseChoices.MaybeLater )"
58+
@time-out-close="() => onClose( 'SoftClose', CloseChoices.TimeOut )"
59+
/>
60+
61+
<FundsModal
62+
:content="useOfFundsContent"
63+
:is-funds-modal-visible="isFundsModalVisible"
64+
@hideFundsModal="onHideFundsModal"
65+
/>
66+
</div>
67+
</template>
68+
69+
<script setup lang="ts">
70+
import { BannerStates } from '@src/components/BannerConductor/StateMachine/BannerStates';
71+
import SoftClose from '@src/components/SoftClose/SoftClose.vue';
72+
import { computed, inject, ref, watch } from 'vue';
73+
import FullPageBanner from './FullPageBanner.vue';
74+
import MiniBanner from './MiniBanner.vue';
75+
import FundsModal from '@src/components/UseOfFunds/FundsModal.vue';
76+
import { UseOfFundsContent as useOfFundsContentInterface } from '@src/domain/UseOfFunds/UseOfFundsContent';
77+
import { UseOfFundsCloseSources } from '@src/components/UseOfFunds/UseOfFundsCloseSources';
78+
import { PageScroller } from '@src/utils/PageScroller/PageScroller';
79+
import MainDonationForm from '@src/components/DonationForm/Forms/MainDonationForm.vue';
80+
import MultiStepDonation from '@src/components/DonationForm/MultiStepDonation.vue';
81+
import BannerText from '../content/BannerText.vue';
82+
import UpgradeToYearlyButtonForm from '@src/components/DonationForm/Forms/UpgradeToYearlyButtonForm.vue';
83+
import BannerSlides from '../content/BannerSlides.vue';
84+
import BannerFooter from '@src/components/Footer/BannerFooter.vue';
85+
import ChevronLeftIcon from '@src/components/Icons/ChevronLeftIcon.vue';
86+
import KeenSlider from '@src/components/Slider/KeenSlider.vue';
87+
import { Tracker } from '@src/tracking/Tracker';
88+
import { MobileMiniBannerExpandedEvent } from '@src/tracking/events/MobileMiniBannerExpandedEvent';
89+
import { useFormModel } from '@src/components/composables/useFormModel';
90+
import {
91+
createSubmittableMainDonationForm
92+
} from '@src/components/DonationForm/StepControllers/SubmittableMainDonationForm';
93+
import {
94+
createSubmittableUpgradeToYearly
95+
} from '@src/components/DonationForm/StepControllers/SubmittableUpgradeToYearly';
96+
import { CloseChoices } from '@src/domain/CloseChoices';
97+
import { CloseEvent } from '@src/tracking/events/CloseEvent';
98+
import { TrackingFeatureName } from '@src/tracking/TrackingEvent';
99+
import ProgressBar from '@src/components/ProgressBar/ProgressBar.vue';
100+
101+
enum ContentStates {
102+
Mini = 'wmde-banner-wrapper--mini',
103+
FullPage = 'wmde-banner-wrapper--full-page',
104+
SoftClosing = 'wmde-banner-wrapper--soft-closing'
105+
}
106+
107+
enum FormStepNames {
108+
MainDonationFormStep = 'MainDonationForm',
109+
UpgradeToYearlyFormStep = 'UpgradeToYearlyForm'
110+
}
111+
112+
interface Props {
113+
bannerState: BannerStates;
114+
useOfFundsContent: useOfFundsContentInterface;
115+
pageScroller: PageScroller;
116+
remainingImpressions: number;
117+
}
118+
119+
const props = defineProps<Props>();
120+
const emit = defineEmits( [ 'bannerClosed', 'bannerContentChanged' ] );
121+
122+
const tracker = inject<Tracker>( 'tracker' );
123+
124+
const isFundsModalVisible = ref<boolean>( false );
125+
const slideShowStopped = ref<boolean>( false );
126+
const slideshowShouldPlay = computed( () => props.bannerState === BannerStates.Visible && !slideShowStopped.value );
127+
const contentState = ref<ContentStates>( ContentStates.Mini );
128+
const formModel = useFormModel();
129+
const stepControllers = [
130+
createSubmittableMainDonationForm( formModel, FormStepNames.UpgradeToYearlyFormStep ),
131+
createSubmittableUpgradeToYearly( formModel, FormStepNames.MainDonationFormStep, FormStepNames.MainDonationFormStep )
132+
];
133+
134+
watch( contentState, async () => {
135+
emit( 'bannerContentChanged' );
136+
} );
137+
138+
function onCloseMiniBanner(): void {
139+
if ( props.remainingImpressions > 0 ) {
140+
contentState.value = ContentStates.SoftClosing;
141+
} else {
142+
onClose( 'MainBanner', CloseChoices.Close );
143+
}
144+
}
145+
146+
function onClose( feature: TrackingFeatureName, userChoice: CloseChoices ): void {
147+
emit( 'bannerClosed', new CloseEvent( feature, userChoice ) );
148+
}
149+
150+
function onshowFullPageBanner(): void {
151+
slideShowStopped.value = true;
152+
contentState.value = ContentStates.FullPage;
153+
tracker.trackEvent( new MobileMiniBannerExpandedEvent() );
154+
}
155+
156+
function onshowFullPageBannerPreselected(): void {
157+
slideShowStopped.value = true;
158+
formModel.selectedAmount.value = '10';
159+
contentState.value = ContentStates.FullPage;
160+
tracker.trackEvent( new MobileMiniBannerExpandedEvent( 'preselected' ) );
161+
}
162+
163+
const onHideFundsModal = ( payload: { source: UseOfFundsCloseSources } ): void => {
164+
props.pageScroller.scrollIntoView( payload.source === UseOfFundsCloseSources.callToAction ?
165+
'.wmde-banner-form' :
166+
'.wmde-banner-full-small-print .wmde-banner-footer-usage-link'
167+
);
168+
isFundsModalVisible.value = false;
169+
};
170+
171+
</script>

0 commit comments

Comments
 (0)