diff --git a/src/pretix/static/pretixpresale/widget/src/components/AvailBox.vue b/src/pretix/static/pretixpresale/widget/src/components/AvailBox.vue
index 6a81fb0d99..f8cff7104a 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/AvailBox.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/AvailBox.vue
@@ -61,11 +61,8 @@ const waitingListUrl = computed(() => {
if (store.subevent) {
u += `&subevent=${store.subevent}`
}
- const widgetDataJson = JSON.stringify(store.widgetData)
- u += `&widget_data=${encodeURIComponent(widgetDataJson)}`
- if (store.widgetData.consent) {
- u += `&consent=${encodeURIComponent(store.widgetData.consent)}`
- }
+ u += `&widget_data=${encodeURIComponent(store.widgetDataJson)}`
+ u += store.consentParameter
return u
})
diff --git a/src/pretix/static/pretixpresale/widget/src/components/Button.vue b/src/pretix/static/pretixpresale/widget/src/components/Button.vue
index c5774a4640..00140c0385 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/Button.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/Button.vue
@@ -16,32 +16,6 @@ const formMethod = computed(() => {
return 'post'
})
-const formAction = computed(() => store.getFormAction())
-
-const formTarget = computed(() => {
- const isFirefox = navigator.userAgent.toLowerCase().includes('firefox')
- const isAndroid = navigator.userAgent.toLowerCase().includes('android')
- if (isAndroid && isFirefox) {
- return '_top'
- }
- return '_blank'
-})
-
-const consentParameterValue = computed(() => {
- if (store.widgetData.consent) {
- return encodeURIComponent(store.widgetData.consent)
- }
- return ''
-})
-
-const widgetDataJson = computed(() => {
- const clonedData = { ...store.widgetData }
- if (clonedData.consent) {
- delete clonedData.consent
- }
- return JSON.stringify(clonedData)
-})
-
function handleBuy (event: Event) {
if (form.value) {
const formData = new FormData(form.value)
@@ -57,13 +31,13 @@ defineExpose({
.pretix-widget-wrapper
.pretix-widget-button-container
- form(ref="form", :method="formMethod", :action="formAction", :target="formTarget")
+ form(ref="form", :method="formMethod", :action="store.formAction", :target="store.formTarget")
input(v-if="store.voucherCode", type="hidden", name="_voucher_code", :value="store.voucherCode")
input(v-if="store.voucherCode", type="hidden", name="voucher", :value="store.voucherCode")
input(type="hidden", name="subevent", :value="store.subevent")
input(type="hidden", name="locale", :value="lang")
- input(type="hidden", name="widget_data", :value="widgetDataJson")
- input(v-if="consentParameterValue", type="hidden", name="consent", :value="consentParameterValue")
+ input(type="hidden", name="widget_data", :value="store.widgetDataJson")
+ input(v-if="store.consentParameterValue", type="hidden", name="consent", :value="store.consentParameterValue")
input(
v-for="item in store.items",
:key="item.item",
@@ -76,6 +50,5 @@ defineExpose({
Overlay
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/Category.vue b/src/pretix/static/pretixpresale/widget/src/components/Category.vue
index deb99c1cbd..70b4c78be8 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/Category.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/Category.vue
@@ -19,6 +19,5 @@ defineProps<{
:category="category"
)
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventCalendar.vue b/src/pretix/static/pretixpresale/widget/src/components/EventCalendar.vue
index adfc05223b..b1ed41aa95 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventCalendar.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventCalendar.vue
@@ -16,6 +16,7 @@ const calendar = ref()
const displayEventInfo = computed(() => store.displayEventInfo || (store.displayEventInfo === null && store.parentStack.length > 0))
const monthname = computed(() => {
+ // TODO proper date formatting?
if (!store.date) return ''
const monthNum = store.date.substr(5, 2)
const year = store.date.substr(0, 4)
@@ -26,9 +27,8 @@ const id = computed(() => `${store.htmlId}-event-calendar-table`)
const ariaLabelledby = computed(() => `${store.htmlId}-event-calendar-table-label`)
-const showFilters = computed(() => !store.disableFilters && store.metaFilterFields.length > 0)
-
function backToList () {
+ // TODO should be in store
store.weeks = null
store.view = 'events'
store.name = null
@@ -63,7 +63,6 @@ function nextmonth () {
store.reload({ focus: `#${id.value}` })
}
-
.pretix-widget-event-calendar(ref="calendar")
//- Back navigation
@@ -80,7 +79,7 @@ function nextmonth () {
)
//- Filter
- EventListFilterForm(v-if="showFilters")
+ EventListFilterForm(v-if="!store.disableFilters && store.metaFilterFields.length > 0")
//- Calendar navigation
.pretix-widget-event-calendar-head
@@ -92,7 +91,7 @@ function nextmonth () {
a.pretix-widget-event-calendar-next-month(href="#", @click.prevent.stop="nextmonth")
| {{ STRINGS.next_month }} »
- //- Calendar table
+ //- Calendar
table.pretix-widget-event-calendar-table(
:id="id",
tabindex="0",
@@ -115,6 +114,5 @@ function nextmonth () {
:mobile="mobile"
)
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventCalendarCell.vue b/src/pretix/static/pretixpresale/widget/src/components/EventCalendarCell.vue
index cba2c3ac43..76bc4db1e1 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventCalendarCell.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventCalendarCell.vue
@@ -26,9 +26,10 @@ const tabindex = computed(() => role.value === 'button' ? '0' : '-1')
const classObject = computed(() => {
const o: Record = {}
if (props.day && props.day.events.length > 0) {
- o['pretix-widget-has-events'] = true
+ o['pretix-widget-has-events'] = true // TODO static
let best = 'red'
let allLow = true
+ // TODO decopypasta
for (const ev of props.day.events) {
if (ev.availability.color === 'green') {
best = 'green'
@@ -47,11 +48,12 @@ const classObject = computed(() => {
return o
})
-function selectDay(e: Event) {
+function selectDay (e: Event) {
if (!props.day || !props.day.events.length || !props.mobile) return
e.preventDefault()
e.stopPropagation()
+ // TODO decopypasta
if (props.day.events.length === 1) {
const ev = props.day.events[0]
store.parentStack.push(store.targetUrl)
@@ -66,22 +68,23 @@ function selectDay(e: Event) {
}
}
-function onKeyDown(e: KeyboardEvent) {
+function onKeyDown (e: KeyboardEvent) {
const keyDown = e.key ?? e.keyCode
if (keyDown === 'Enter' || keyDown === 13 || ['Spacebar', ' '].includes(keyDown as string) || keyDown === 32) {
+ // (prevent default so the page doesn't scroll when pressing space)
e.preventDefault()
selectDay(e)
}
}
-function attachListeners() {
+function attachListeners () {
if (role.value === 'button' && cellEl.value) {
cellEl.value.addEventListener('click', selectDay)
cellEl.value.addEventListener('keydown', onKeyDown)
}
}
-function detachListeners() {
+function detachListeners () {
if (cellEl.value) {
cellEl.value.removeEventListener('click', selectDay)
cellEl.value.removeEventListener('keydown', onKeyDown)
@@ -92,6 +95,7 @@ onMounted(() => {
attachListeners()
})
+// TODO why different from old version?
watch(role, (newValue, oldValue) => {
if (newValue === 'button' && oldValue !== 'button') {
attachListeners()
@@ -100,7 +104,6 @@ watch(role, (newValue, oldValue) => {
}
})
-
td(
ref="cellEl",
@@ -113,6 +116,5 @@ td(
.pretix-widget-event-calendar-events(v-if="day")
EventCalendarEvent(v-for="e in day.events", :key="e.event_url", :event="e")
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventCalendarEvent.vue b/src/pretix/static/pretixpresale/widget/src/components/EventCalendarEvent.vue
index 6b978bebb5..e86ea140ba 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventCalendarEvent.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventCalendarEvent.vue
@@ -21,7 +21,7 @@ const classObject = computed(() => {
return o
})
-function select() {
+function select () {
store.parentStack.push(store.targetUrl)
store.targetUrl = props.event.event_url
store.error = null
@@ -30,18 +30,16 @@ function select() {
store.reload()
}
-
a.pretix-widget-event-calendar-event(
href="#",
:class="classObject",
- @click.prevent.stop="select",
- :aria-describedby="describedby"
+ :aria-describedby="describedby",
+ @click.prevent.stop="select"
)
strong.pretix-widget-event-calendar-event-name {{ event.name }}
.pretix-widget-event-calendar-event-date(v-if="!event.continued && event.time") {{ event.time }}
.pretix-widget-event-calendar-event-availability(v-if="!event.continued && event.availability.text") {{ event.availability.text }}
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventCalendarRow.vue b/src/pretix/static/pretixpresale/widget/src/components/EventCalendarRow.vue
index 850f0d8b16..fc175d781d 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventCalendarRow.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventCalendarRow.vue
@@ -7,11 +7,9 @@ defineProps<{
mobile: boolean
}>()
-
tr
EventCalendarCell(v-for="(d, idx) in week", :key="idx", :day="d", :mobile="mobile")
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventForm.vue b/src/pretix/static/pretixpresale/widget/src/components/EventForm.vue
index 5779a5e683..5b8a8f1b10 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventForm.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventForm.vue
@@ -11,12 +11,9 @@ const voucherinput = ref()
const isItemsSelected = ref(false)
const localVoucher = ref('')
-const displayEventInfo = computed(() => store.displayEventInfo || (store.displayEventInfo === null && (store.events || store.weeks || store.days)))
-
const idVoucherInput = computed(() => `${store.htmlId}-voucher-input`)
-
const ariaLabelledby = computed(() => `${store.htmlId}-voucher-headline`)
-
+const displayEventInfo = computed(() => store.displayEventInfo || (store.displayEventInfo === null && (store.events || store.weeks || store.days)))
const idCartExistsMsg = computed(() => `${store.htmlId}-cart-exists`)
const buyLabel = computed(() => {
@@ -43,78 +40,16 @@ const hiddenParams = computed(() => {
const params = new URL(store.getVoucherFormTarget()).searchParams
params.delete('iframe')
params.delete('take_cart_id')
- return Array.from(params.entries())
+ return [...params.entries()]
})
const showVoucherForm = computed(() => store.vouchersExist && !store.disableVouchers && !store.voucherCode)
-const consentParameterValue = computed(() => {
- if (store.widgetData.consent) {
- return encodeURIComponent(store.widgetData.consent)
- }
- return ''
-})
-
-const widgetDataJson = computed(() => {
- const clonedData = { ...store.widgetData }
- if (clonedData.consent) {
- delete clonedData.consent
- }
- return JSON.stringify(clonedData)
-})
-
-const formAction = computed(() => {
- const additionalParams = getAdditionalURLParams()
- let checkoutUrl = `/${store.targetUrl.replace(/^[^\/]+:\/\/([^\/]+)\//, '')}w/${store.widgetId.replace('pretix-widget-', '')}/`
- if (!store.cartExists) {
- checkoutUrl += 'checkout/start'
- }
- if (additionalParams) {
- checkoutUrl += `?${additionalParams}`
- }
-
- const cookieName = `pretix_widget_${store.targetUrl.replace(/[^a-zA-Z0-9]+/g, '_')}`
- const cartIdCookie = document.cookie
- .split('; ')
- .find((row) => row.startsWith(`${cookieName}=`))
- ?.split('=')[1] || null
-
- let formTarget = `${store.targetUrl}w/${store.widgetId.replace('pretix-widget-', '')}/cart/add?iframe=1&next=${encodeURIComponent(checkoutUrl)}`
- if (cartIdCookie) {
- formTarget += `&take_cart_id=${cartIdCookie}`
- }
- if (store.widgetData.consent) {
- formTarget += `&consent=${encodeURIComponent(store.widgetData.consent)}`
- }
- return formTarget
-})
-
-const formTarget = computed(() => {
- const isFirefox = navigator.userAgent.toLowerCase().includes('firefox')
- const isAndroid = navigator.userAgent.toLowerCase().includes('android')
- if (isAndroid && isFirefox) {
- return '_top'
- }
- return '_blank'
-})
-
-function getAdditionalURLParams (): string {
- if (!window.location.search.includes('utm_')) {
- return ''
- }
- const params = new URLSearchParams(window.location.search)
- for (const [key] of params.entries()) {
- if (!key.startsWith('utm_')) {
- params.delete(key)
- }
- }
- return params.toString()
-}
-
function backToList () {
store.targetUrl = store.parentStack.pop() || store.targetUrl
store.error = null
if (!store.subevent) {
+ // reset if we are not in a series
store.name = null
store.frontpageText = null
}
@@ -123,13 +58,20 @@ function backToList () {
store.appendEvents = false
store.triggerLoadCallback()
- if (store.events !== null) {
+ if (store.events !== undefined && store.events !== null) {
store.view = 'events'
- } else if (store.days !== null) {
+ } else if (store.days !== undefined && store.days !== null) {
store.view = 'days'
} else {
store.view = 'weeks'
}
+
+ // TODO
+ // let $el = this.$root.$el
+ // this.$root.$nextTick(function () {
+ // // wait for redraw, then focus content element for better a11y
+ // $el.focus()
+ // })
}
function calcItemsSelected () {
@@ -208,14 +150,14 @@ watch(() => store.overlay?.frameShown, (newValue) => {
form(
ref="form",
method="post",
- :action="formAction",
- :target="formTarget",
+ :action="store.formAction",
+ :target="store.formTarget",
@submit="handleBuy"
)
input(v-if="store.voucherCode", type="hidden", name="_voucher_code", :value="store.voucherCode")
input(type="hidden", name="subevent", :value="store.subevent")
- input(type="hidden", name="widget_data", :value="widgetDataJson")
- input(v-if="consentParameterValue", type="hidden", name="consent", :value="consentParameterValue")
+ input(type="hidden", name="widget_data", :value="store.widgetDataJson")
+ input(v-if="store.consentParameterValue", type="hidden", name="consent", :value="store.consentParameterValue")
//- Error message
.pretix-widget-error-message(v-if="store.error") {{ store.error }}
@@ -242,7 +184,7 @@ watch(() => store.overlay?.frameShown, (newValue) => {
| {{ STRINGS.waiting_list }}
.pretix-widget-clear
- //- Product list
+ //- Actual Product list
Category(v-for="category in store.categories", :key="category.id", :category="category")
//- Buy button
@@ -293,6 +235,5 @@ watch(() => store.overlay?.frameShown, (newValue) => {
button(@click="handleRedeem") {{ STRINGS.redeem }}
.pretix-widget-clear
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventList.vue b/src/pretix/static/pretixpresale/widget/src/components/EventList.vue
index 991c056327..1f632f087b 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventList.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventList.vue
@@ -9,11 +9,12 @@ const store = inject(StoreKey)!
const displayEventInfo = computed(() => store.displayEventInfo || (store.displayEventInfo === null && store.parentStack.length > 0))
-const showBackButton = computed(() => store.weeks || store.parentStack.length > 0)
-
-const showFilters = computed(() => !store.disableFilters && store.metaFilterFields.length > 0)
-
-function backToCalendar() {
+function backToCalendar () {
+ // TODO
+ // make sure to always focus content element
+ // this.$nextTick(function () {
+ // this.$root.$el.focus()
+ // })
store.offset = 0
store.appendEvents = false
@@ -30,17 +31,16 @@ function backToCalendar() {
}
}
-function loadMore() {
+function loadMore () {
store.appendEvents = true
store.offset += 50
store.loading++
store.reload()
}
-
.pretix-widget-event-list
- .pretix-widget-back(v-if="showBackButton")
+ .pretix-widget-back(v-if="store.weeks || store.parentStack.length > 0")
a(href="#", rel="prev", @click.prevent.stop="backToCalendar")
| ‹ {{ STRINGS.back }}
@@ -52,7 +52,7 @@ function loadMore() {
v-html="store.frontpageText"
)
- EventListFilterForm(v-if="showFilters")
+ EventListFilterForm(v-if="!store.disableFilters && store.metaFilterFields.length > 0")
EventListEntry(
v-for="event in store.events",
@@ -63,6 +63,5 @@ function loadMore() {
p.pretix-widget-event-list-load-more(v-if="store.hasMoreEvents")
button(@click.prevent.stop="loadMore") {{ STRINGS.load_more }}
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventListEntry.vue b/src/pretix/static/pretixpresale/widget/src/components/EventListEntry.vue
index d99279c3ec..d200749177 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventListEntry.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventListEntry.vue
@@ -22,7 +22,7 @@ const classObject = computed(() => {
const location = computed(() => props.event.location.replace(/\s*\n\s*/g, ', '))
-function select() {
+function select () {
store.parentStack.push(store.targetUrl)
store.targetUrl = props.event.event_url
store.error = null
@@ -31,15 +31,14 @@ function select() {
store.reload()
}
-
a.pretix-widget-event-list-entry(href="#", :class="classObject", @click.prevent.stop="select")
.pretix-widget-event-list-entry-name {{ event.name }}
.pretix-widget-event-list-entry-date {{ event.date_range }}
+ //- hidden by css for now, but used by a few people
.pretix-widget-event-list-entry-location {{ location }}
.pretix-widget-event-list-entry-availability
span {{ event.availability.text }}
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventListFilterField.vue b/src/pretix/static/pretixpresale/widget/src/components/EventListFilterField.vue
index b366eb8dfb..767c418c8a 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventListFilterField.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventListFilterField.vue
@@ -16,13 +16,11 @@ const currentValue = computed(() => {
return filterParams.get(props.field.key) || ''
})
-
.pretix-widget-event-list-filter-field
label(:for="id") {{ field.label }}
select(:id="id", :name="field.key", :value="currentValue")
option(v-for="choice in field.choices", :key="choice[0]", :value="choice[0]") {{ choice[1] }}
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventListFilterForm.vue b/src/pretix/static/pretixpresale/widget/src/components/EventListFilterForm.vue
index 5bc4987735..63068f3629 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventListFilterForm.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventListFilterForm.vue
@@ -7,7 +7,7 @@ import EventListFilterField from './EventListFilterField.vue'
const store = inject(StoreKey)!
const filterform = ref()
-function onSubmit(e: Event) {
+function onSubmit (e: Event) {
e.preventDefault()
if (!filterform.value) return
@@ -25,7 +25,6 @@ function onSubmit(e: Event) {
store.reload()
}
-
form.pretix-widget-event-list-filter-form(ref="filterform", @submit="onSubmit")
fieldset.pretix-widget-event-list-filter-fieldset
@@ -37,6 +36,5 @@ form.pretix-widget-event-list-filter-form(ref="filterform", @submit="onSubmit")
)
button {{ STRINGS.filter }}
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventWeekCalendar.vue b/src/pretix/static/pretixpresale/widget/src/components/EventWeekCalendar.vue
index 28e5ca11d4..ad0a2cdeb8 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventWeekCalendar.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventWeekCalendar.vue
@@ -24,16 +24,14 @@ const weekname = computed(() => {
const id = computed(() => `${store.htmlId}-event-week-table`)
-const showFilters = computed(() => !store.disableFilters && store.metaFilterFields.length > 0)
-
-function backToList() {
+function backToList () {
store.weeks = null
store.name = null
store.frontpageText = null
store.view = 'events'
}
-function prevweek() {
+function prevweek () {
if (!store.week) return
let curWeek = store.week[1]
let curYear = store.week[0]
@@ -47,7 +45,7 @@ function prevweek() {
store.reload({ focus: `#${id.value}` })
}
-function nextweek() {
+function nextweek () {
if (!store.week) return
let curWeek = store.week[1]
let curYear = store.week[0]
@@ -61,12 +59,11 @@ function nextweek() {
store.reload({ focus: `#${id.value}` })
}
-
.pretix-widget-event-calendar.pretix-widget-event-week-calendar(ref="weekcalendar")
//- Back navigation
.pretix-widget-back(v-if="store.events !== null")
- a(href="#", @click.prevent.stop="backToList", role="button")
+ a(href="#", role="button", @click.prevent.stop="backToList")
| ‹ {{ STRINGS.back }}
//- Event header
@@ -74,7 +71,7 @@ function nextweek() {
strong {{ store.name }}
//- Filter
- EventListFilterForm(v-if="showFilters")
+ EventListFilterForm(v-if="!store.disableFilters && store.metaFilterFields.length > 0")
//- Calendar navigation
.pretix-widget-event-description(
@@ -82,12 +79,12 @@ function nextweek() {
v-html="store.frontpageText"
)
.pretix-widget-event-calendar-head
- a.pretix-widget-event-calendar-previous-month(href="#", @click.prevent.stop="prevweek", role="button")
+ a.pretix-widget-event-calendar-previous-month(href="#", role="button", @click.prevent.stop="prevweek")
| « {{ STRINGS.previous_week }}
|
strong {{ weekname }}
|
- a.pretix-widget-event-calendar-next-month(href="#", @click.prevent.stop="nextweek", role="button")
+ a.pretix-widget-event-calendar-next-month(href="#", role="button", @click.prevent.stop="nextweek")
| {{ STRINGS.next_week }} »
//- Actual calendar
@@ -95,6 +92,5 @@ function nextweek() {
.pretix-widget-event-week-col(v-for="d in store.days", :key="d?.date || ''")
EventWeekCell(:day="d", :mobile="mobile")
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/EventWeekCell.vue b/src/pretix/static/pretixpresale/widget/src/components/EventWeekCell.vue
index 4da1e86151..241f654712 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/EventWeekCell.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/EventWeekCell.vue
@@ -6,7 +6,7 @@ import EventCalendarEvent from './EventCalendarEvent.vue'
const props = defineProps<{
day: DayEntry | null
- mobile: boolean
+ mobile: boolean // TODO inject?
}>()
const store = inject(StoreKey)!
@@ -47,6 +47,7 @@ function selectDay () {
if (props.day.events.length === 1) {
const ev = props.day.events[0]
+ // TODO store mutation bad
store.parentStack.push(store.targetUrl)
store.targetUrl = ev.event_url
store.error = null
@@ -59,7 +60,6 @@ function selectDay () {
}
}
-
div(:class="classObject", @click.prevent.stop="selectDay")
.pretix-widget-event-calendar-day(v-if="day", :id="id") {{ dayhead }}
@@ -71,6 +71,5 @@ div(:class="classObject", @click.prevent.stop="selectDay")
:describedby="id"
)
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue b/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue
index f619c939fb..4f139fce74 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/Overlay.vue
@@ -69,8 +69,11 @@ function errorClose (e: Event) {
store.overlay.errorUrlAfterNewTab = false
}
-function close () {
+function close (e) {
if (store.overlay.frameLoading) {
+ // Chrome does not allow blocking dialog.cancel event more than once
+ // => wiggle the loading-element and re-open the modal
+ cancel(e)
frameDialog.value?.showModal()
return
}
@@ -82,6 +85,7 @@ function close () {
}
function cancel (e: Event) {
+ // do not allow to cancel while frame is loading as we cannot abort the operation
if (store.overlay.frameLoading) {
e.preventDefault()
const target = e.target as HTMLElement
@@ -111,14 +115,14 @@ function triggerCloseCallback () {
})
}
+// TODO check if watchEffect is better for the following watchers
watch(() => store.overlay.lightbox, (newValue, oldValue) => {
- if (newValue) {
- if (newValue.image !== oldValue?.image) {
- newValue.loading = true
- }
- if (!oldValue) {
- lightboxDialog.value?.showModal()
- }
+ if (!newValue) return
+ if (newValue.image !== oldValue?.image) {
+ newValue.loading = true
+ }
+ if (!oldValue) {
+ lightboxDialog.value?.showModal()
}
})
@@ -128,19 +132,19 @@ watch(() => store.overlay.errorMessage, (newValue, oldValue) => {
}
})
-watch(() => store.overlay.frameShown, (newValue) => {
- if (newValue) {
- nextTick(() => {
- closeButton.value?.focus()
- })
- }
+watch(() => store.overlay.frameShown, async (newValue) => {
+ if (!newValue) return
+ await nextTick()
+ closeButton.value?.focus()
})
watch(() => store.overlay.frameSrc, (newValue, oldValue) => {
+ // show loading spinner only when previously no frame_src was set
if (newValue && !oldValue) {
store.overlay.frameLoading = true
}
if (iframe.value) {
+ // to close and unload the iframe, frame_src can be empty -> make it valid HTML with about:blank
iframe.value.src = newValue || 'about:blank'
}
})
@@ -151,7 +155,7 @@ watch(() => store.overlay.frameLoading, (newValue) => {
frameDialog.value.showModal()
}
} else {
- if (!store.overlay.frameSrc && frameDialog.value?.open) {
+ if (!store.overlay.frameSrc && frameDialog.value?.open) { // finished loading, but no iframe to display => close
frameDialog.value.close()
}
}
@@ -169,7 +173,6 @@ onUnmounted(() => {
Teleport(to="body")
.pretix-widget-overlay
- //- Iframe dialog
dialog(ref="frameDialog", :class="frameClasses", :aria-label="STRINGS.checkout", @close="close", @cancel="cancel")
.pretix-widget-frame-loading(v-show="store.overlay.frameLoading")
svg(width="256", height="256", viewBox="0 0 1792 1792", xmlns="http://www.w3.org/2000/svg")
@@ -193,8 +196,6 @@ Teleport(to="body")
referrerpolicy="origin",
@load="iframeLoaded"
) Please enable frames in your browser!
-
- //- Alert dialog
dialog(ref="alertDialog", :class="alertClasses", role="alertdialog", :aria-labelledby="errorMessageId", @close="errorClose")
form.pretix-widget-alert-box(method="dialog")
p(:id="errorMessageId") {{ store.overlay.errorMessage }}
@@ -207,7 +208,6 @@ Teleport(to="body")
path(style="fill:#ffffff;", d="M 599.86438,303.72882 H 1203.5254 V 1503.4576 H 599.86438 Z")
path.pretix-widget-primary-color(d="M896 128q209 0 385.5 103t279.5 279.5 103 385.5-103 385.5-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103zm128 1247v-190q0-14-9-23.5t-22-9.5h-192q-13 0-23 10t-10 23v190q0 13 10 23t23 10h192q13 0 22-9.5t9-23.5zm-2-344l18-621q0-12-10-18-10-8-24-8h-220q-14 0-24 8-10 6-10 18l17 621q0 10 10 17.5t24 7.5h185q14 0 23.5-7.5t10.5-17.5z")
- //- Lightbox dialog
dialog(ref="lightboxDialog", :class="lightboxClasses", role="alertdialog", @close="lightboxClose")
.pretix-widget-lightbox-loading(v-if="store.overlay.lightbox?.loading")
svg(width="256", height="256", viewBox="0 0 1792 1792", xmlns="http://www.w3.org/2000/svg")
@@ -227,6 +227,5 @@ Teleport(to="body")
)
figcaption(v-if="store.overlay.lightbox.description") {{ store.overlay.lightbox.description }}
-
diff --git a/src/pretix/static/pretixpresale/widget/src/components/Widget.vue b/src/pretix/static/pretixpresale/widget/src/components/Widget.vue
index 9751d41d6e..f3af0c817a 100644
--- a/src/pretix/static/pretixpresale/widget/src/components/Widget.vue
+++ b/src/pretix/static/pretixpresale/widget/src/components/Widget.vue
@@ -36,12 +36,14 @@ onMounted(() => {
resizeObserver.observe(wrapper.value)
}
- store.reload()
- emit('mounted')
+ store.reload() // TODO call earlier?
+ emit('mounted') // TODO where does this go?
})
watch(() => store.view, (newValue, oldValue) => {
if (oldValue && wrapper.value) {
+ // always make sure the widget is scrolled to the top
+ // as we only check top, we do not need to wait for a redraw
const rect = wrapper.value.getBoundingClientRect()
if (rect.top < 0) {
wrapper.value.scrollIntoView()
diff --git a/src/pretix/static/pretixpresale/widget/src/sharedStore.ts b/src/pretix/static/pretixpresale/widget/src/sharedStore.ts
index 6d38ad32d8..8eb69466d5 100644
--- a/src/pretix/static/pretixpresale/widget/src/sharedStore.ts
+++ b/src/pretix/static/pretixpresale/widget/src/sharedStore.ts
@@ -125,22 +125,23 @@ export function createWidgetStore (config: {
if ((window as any).crossOriginIsolated === true) return false
return !this.disableIframe && (this.skipSsl || /https.*/.test(document.location.protocol))
},
-
cookieName (): string {
return `pretix_widget_${this.targetUrl.replace(/[^a-zA-Z0-9]+/g, '_')}`
},
-
cartIdFromCookie (): string | null {
return getCookie(this.cookieName) ?? null
},
-
+ widgetDataJson (): string {
+ const cloned = { ...this.widgetData }
+ delete cloned.consent
+ return JSON.stringify(cloned)
+ },
consentParameter (): string {
if (this.widgetData.consent) {
return `&consent=${encodeURIComponent(this.widgetData.consent)}`
}
return ''
},
-
additionalURLParams (): string {
if (!window.location.search.includes('utm_')) {
return ''
@@ -153,12 +154,46 @@ export function createWidgetStore (config: {
}
return params.toString()
},
-
newTabTarget (): string {
return this.subevent ? `${this.targetUrl}${this.subevent}/` : this.targetUrl
},
- },
+ formTarget (): string {
+ const isFirefox = navigator.userAgent.toLowerCase().includes('firefox')
+ const isAndroid = navigator.userAgent.toLowerCase().includes('android')
+ if (isAndroid && isFirefox) {
+ return '_top'
+ }
+ return '_blank'
+ },
+ consentParameterValue (): string {
+ if (this.widgetData.consent) {
+ return encodeURIComponent(this.widgetData.consent)
+ }
+ return ''
+ },
+ formAction (): string {
+ if (!this.useIframe && this.isButton && this.items.length === 0) {
+ if (this.voucherCode) return `${this.targetUrl}redeem`
+ if (this.subevent) return `${this.targetUrl}${this.subevent}/`
+ return this.targetUrl
+ }
+ let checkoutUrl = `/${this.targetUrl.replace(/^[^/]+:\/\/([^/]+)\//, '')}w/${globalWidgetId}/`
+ if (!this.cartExists) {
+ checkoutUrl += 'checkout/start'
+ }
+ if (this.additionalURLParams) {
+ checkoutUrl += `?${this.additionalURLParams}`
+ }
+
+ let formTarget = `${this.targetUrl}w/${globalWidgetId}/cart/add?iframe=1&next=${encodeURIComponent(checkoutUrl)}`
+ if (this.cartIdFromCookie) {
+ formTarget += `&take_cart_id=${this.cartIdFromCookie}`
+ }
+ formTarget += this.consentParameter
+ return formTarget
+ },
+ },
actions: {
triggerLoadCallback () {
nextTick(() => {
@@ -167,7 +202,6 @@ export function createWidgetStore (config: {
}
})
},
-
async reload (opt: { focus?: string } = {}) {
if (this.isButton) return
@@ -303,28 +337,6 @@ export function createWidgetStore (config: {
}
}
},
- getFormAction (): string {
- if (!this.useIframe && this.isButton && this.items.length === 0) {
- if (this.voucherCode) return `${this.targetUrl}redeem`
- if (this.subevent) return `${this.targetUrl}${this.subevent}/`
- return this.targetUrl
- }
-
- let checkoutUrl = `/${this.targetUrl.replace(/^[^/]+:\/\/([^/]+)\//, '')}w/${globalWidgetId}/`
- if (!this.cartExists) {
- checkoutUrl += 'checkout/start'
- }
- if (this.additionalURLParams) {
- checkoutUrl += `?${this.additionalURLParams}`
- }
-
- let formTarget = `${this.targetUrl}w/${globalWidgetId}/cart/add?iframe=1&next=${encodeURIComponent(checkoutUrl)}`
- if (this.cartIdFromCookie) {
- formTarget += `&take_cart_id=${this.cartIdFromCookie}`
- }
- formTarget += this.consentParameter
- return formTarget
- },
getVoucherFormTarget (): string {
let formTarget = `${this.targetUrl}w/${globalWidgetId}/redeem?iframe=1&locale=${LANG}`
if (this.cartIdFromCookie) {
@@ -334,7 +346,7 @@ export function createWidgetStore (config: {
formTarget += `&subevent=${this.subevent}`
}
if (this.widgetData) {
- formTarget += `&widget_data=${encodeURIComponent(JSON.stringify(this.widgetData))}`
+ formTarget += `&widget_data=${encodeURIComponent(this.widgetDataJson)}`
}
formTarget += this.consentParameter
if (this.additionalURLParams) {
@@ -349,12 +361,11 @@ export function createWidgetStore (config: {
setCookie(this.cookieName, data.cart_id, 30)
}
- let redirectUrl = data.redirect
- if (redirectUrl.substring(0, 1) === '/') {
- redirectUrl = `${this.targetUrl.replace(/^([^/]+:\/\/[^/]+)\/.*$/, '$1')}${redirectUrl}`
+ let url = data.redirect
+ if (url.substring(0, 1) === '/') {
+ url = `${this.targetUrl.replace(/^([^/]+:\/\/[^/]+)\/.*$/, '$1')}${url}`
}
- let url = redirectUrl
if (url.includes('?')) {
url = `${url}&iframe=1&locale=${LANG}&take_cart_id=${this.cartId}`
} else {
@@ -375,9 +386,11 @@ export function createWidgetStore (config: {
} else {
this.overlay.frameSrc = url
}
- } else if (data.async_id && data.check_url) {
+ } else {
this.asyncTaskId = data.async_id
- this.asyncTaskCheckUrl = `${this.targetUrl.replace(/^([^/]+:\/\/[^/]+)\/.*$/, '$1')}${data.check_url}`
+ if (data.check_url) {
+ this.asyncTaskCheckUrl = `${this.targetUrl.replace(/^([^/]+:\/\/[^/]+)\/.*$/, '$1')}${data.check_url}`
+ }
this.asyncTaskTimeout = window.setTimeout(() => this.pollAsyncTask(), this.asyncTaskInterval)
this.asyncTaskInterval = 250
}
@@ -397,11 +410,8 @@ export function createWidgetStore (config: {
}
},
async buy (formData: FormData, event?: Event) {
- if (this.useIframe) {
- if (event) event.preventDefault()
- } else {
- return
- }
+ if (!this.useIframe) return
+ if (event) event.preventDefault()
if (this.isButton && this.items.length === 0) {
if (this.voucherCode) {
@@ -412,7 +422,7 @@ export function createWidgetStore (config: {
return
}
- const url = `${this.getFormAction()}&locale=${LANG}&ajax=1`
+ const url = `${this.formAction}&locale=${LANG}&ajax=1`
this.overlay.frameLoading = true
this.asyncTaskInterval = 100
@@ -427,25 +437,24 @@ export function createWidgetStore (config: {
this.overlay.errorUrlAfter = this.newTabTarget
this.overlay.errorUrlAfterNewTab = true
} else if (e.status === 405) {
+ // Likely a redirect!
this.targetUrl = e.responseUrl.substring(0, e.responseUrl.indexOf('/cart/add') - 18)
this.overlay.frameLoading = false
- } else {
- this.overlay.errorMessage = STRINGS.cart_error
- this.overlay.frameLoading = false
+ this.buy(formData)
+ return
}
- } else {
- this.overlay.errorMessage = STRINGS.cart_error
- this.overlay.frameLoading = false
}
+ this.overlay.errorMessage = STRINGS.cart_error
+ this.overlay.frameLoading = false
}
},
redeem (voucherCode: string, event?: Event) {
- if (this.useIframe) {
- if (event) event.preventDefault()
- this.voucherOpen(voucherCode)
- }
+ if (!this.useIframe) return
+ if (event) event.preventDefault()
+ this.voucherOpen(voucherCode)
},
voucherOpen (voucherCode: string) {
+ // TODO just use https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
const redirectUrl = `${this.getVoucherFormTarget()}&voucher=${encodeURIComponent(voucherCode)}`
if (this.useIframe) {
this.overlay.frameSrc = redirectUrl
@@ -456,6 +465,7 @@ export function createWidgetStore (config: {
resume () {
let redirectUrl = `${this.targetUrl}w/${globalWidgetId}/`
if (this.subevent && !this.cartId) {
+ // button with subevent but no items
redirectUrl += `${this.subevent}/`
}
redirectUrl += `?iframe=1&locale=${LANG}`
@@ -463,7 +473,7 @@ export function createWidgetStore (config: {
redirectUrl += `&take_cart_id=${this.cartId}`
}
if (this.widgetData) {
- redirectUrl += `&widget_data=${encodeURIComponent(JSON.stringify(this.widgetData))}`
+ redirectUrl += `&widget_data=${encodeURIComponent(this.widgetDataJson)}`
}
redirectUrl += this.consentParameter
if (this.additionalURLParams) {
@@ -502,7 +512,7 @@ export function createWidgetStore (config: {
redirectUrl += `&take_cart_id=${this.cartId}`
}
if (this.widgetData) {
- redirectUrl += `&widget_data=${encodeURIComponent(JSON.stringify(this.widgetData))}`
+ redirectUrl += `&widget_data=${encodeURIComponent(this.widgetDataJson)}`
}
redirectUrl += this.consentParameter
if (this.additionalURLParams) {