Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Agenda URL #hash scrolls to 'now' or specific day #7772

Merged
merged 14 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/agenda/Agenda.vue
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ function reconnectScrollObservers () {
scrollObserver.disconnect()
visibleDays.length = 0
for (const mDay of agendaStore.meetingDays) {
const el = document.getElementById(`agenda-day-${mDay.slug}`)
const el = document.getElementById(mDay.slug)
el.dataset.dayId = mDay.slug.toString()
el.dataset.dayTs = mDay.ts
scrollObserver.observe(el)
Expand Down
57 changes: 49 additions & 8 deletions client/agenda/AgendaMobileBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,48 @@ const siteStore = useSiteStore()

// Meeting Days

function optionToLink(opts){
const { key, label, icon } = opts

return {
...opts,
type: 'render',
render: () => h(
'a',
{
class: 'dropdown-link',
'data-testid': 'mobile-link',
href: `#${key}`
},
[
h(
'span',
icon()
),
h(
'span',
label
)
]
)
}
}

const jumpToDayOptions = computed(() => {
const days = []
if (agendaStore.isMeetingLive) {
days.push({
days.push(optionToLink({
label: 'Jump to Now',
key: 'now',
icon: () => h('i', { class: 'bi bi-arrow-down-right-square text-red' })
})
icon: () => h('i', { class: 'bi bi-arrow-down-right-square text-red' }),
}))
}
for (const day of agendaStore.meetingDays) {
days.push({
days.push(optionToLink({
label: `Jump to ${day.label}`,
key: day.slug,
icon: () => h('i', { class: 'bi bi-arrow-down-right-square' })
})
icon: () => h('i', { class: 'bi bi-arrow-down-right-square' }),
}))
}
return days
})
Expand All @@ -90,14 +117,13 @@ const downloadIcsOptions = [
function jumpToDay (dayId) {
if (dayId === 'now') {
const lastEventId = agendaStore.findCurrentEventId()

if (lastEventId) {
document.getElementById(`agenda-rowid-${lastEventId}`)?.scrollIntoView(true)
} else {
message.warning('There is no event happening right now.')
}
} else {
document.getElementById(`agenda-day-${dayId}`)?.scrollIntoView(true)
document.getElementById(dayId)?.scrollIntoView(true)
}
}

Expand Down Expand Up @@ -162,4 +188,19 @@ function downloadIcs (key) {
}
}
}

.dropdown-link {
display: flex;
text-decoration:none;
gap: 0.2rem 0.5rem;
padding: 0.5em;
color: var(--bs-body-color);

&:hover,
&:focus {
background-color: var(--bs-dark-bg-subtle);
text-decoration: underline;
}
}

</style>
10 changes: 3 additions & 7 deletions client/agenda/AgendaQuickAccess.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
li.nav-item(v-for='day of agendaStore.meetingDays')
a.nav-link(
:class='agendaStore.dayIntersectId === day.slug ? `active` : ``'
:href='`#slot-` + day.slug'
:href='`#${day.slug}`'
@click='scrollToDay(day.slug, $event)'
)
i.bi.bi-arrow-right-short.d-none.d-xxl-inline.me-2
Expand All @@ -109,7 +109,6 @@
<script setup>
import { computed, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { DateTime } from 'luxon'
import {
NAffix,
NBadge,
Expand Down Expand Up @@ -200,14 +199,11 @@ function pickerDiscard () {
}
}

function scrollToDay (dayId, ev) {
ev.preventDefault()
document.getElementById(`agenda-day-${dayId}`)?.scrollIntoView(true)
function scrollToDay (daySlug, ev) {
document.getElementById(daySlug)?.scrollIntoView(true)
}

function scrollToNow (ev) {
ev.preventDefault()
Copy link
Contributor Author

@holloway holloway Jul 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed so that when clicking 'Now' <a href="#now">Now</a>
Screenshot from 2024-08-01 10-52-50

...it will update the browser location with #now so that future reloads will have a hashstate that expresses 'Now'


const lastEventId = agendaStore.findCurrentEventId()

if (lastEventId) {
Expand Down
29 changes: 27 additions & 2 deletions client/agenda/AgendaScheduleList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
)
//- ROW - DAY HEADING -----------------------
template(v-if='item.displayType === `day`')
td(:id='`agenda-day-` + item.id', :colspan='pickerModeActive ? 6 : 5') {{item.date}}
td(:id='item.slug', :colspan='pickerModeActive ? 6 : 5') {{item.date}}
//- ROW - SESSION HEADING -------------------
template(v-else-if='item.displayType === `session-head`')
td.agenda-table-cell-check(v-if='pickerModeActive') &nbsp;
Expand Down Expand Up @@ -200,7 +200,7 @@ import {

import AgendaDetailsModal from './AgendaDetailsModal.vue'

import { useAgendaStore } from './store'
import { useAgendaStore, daySlugPrefix, daySlug } from './store'
import { useSiteStore } from '../shared/store'
import { getUrl } from '../shared/urls'

Expand Down Expand Up @@ -248,6 +248,7 @@ const meetingEvents = computed(() => {
if (itemDate.toISODate() !== acc.lastDate) {
acc.result.push({
id: item.id,
slug: daySlug(item),
key: `day-${itemDate.toISODate()}`,
displayType: 'day',
date: itemDate.toLocaleString(DateTime.DATE_HUGE),
Expand Down Expand Up @@ -575,6 +576,30 @@ function recalculateRedLine () {
}
}

/**
* On page load when browser location hash contains '#now' or '#agenda-day-*' then scroll accordingly
*/
(function scrollToHashInit() {
if (!window.location.hash) {
return
}
if (!(window.location.hash === "#now" || window.location.hash.startsWith(`#${daySlugPrefix}`))) {
return
}
const unsubscribe = agendaStore.$subscribe((_mutation, agendaStoreState) => {
if (agendaStoreState.schedule.length === 0) {
return
}
unsubscribe() // we only need to scroll once, so unsubscribe from future updates
if(window.location.hash === "#now") {
const lastEventId = agendaStore.findCurrentEventId()
document.getElementById(`agenda-rowid-${lastEventId}`)?.scrollIntoView(true)
} else if(window.location.hash.startsWith(`#${daySlugPrefix}`)) {
document.getElementById(window.location.hash.substring(1))?.scrollIntoView(true)
}
})
})()

// MOUNTED

onMounted(() => {
Expand Down
30 changes: 29 additions & 1 deletion client/agenda/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const useAgendaStore = defineStore('agenda', {
meetingDays () {
const siteStore = useSiteStore()
return uniqBy(this.scheduleAdjusted, 'adjustedStartDate').sort().map(s => ({
slug: s.id.toString(),
slug: daySlug(s),
ts: s.adjustedStartDate,
label: siteStore.viewport < 1350 ? DateTime.fromISO(s.adjustedStartDate).toFormat('ccc LLL d') : DateTime.fromISO(s.adjustedStartDate).toLocaleString(DateTime.DATE_HUGE)
}))
Expand Down Expand Up @@ -154,6 +154,29 @@ export const useAgendaStore = defineStore('agenda', {
this.timezone = agendaData.meeting.timezone
}

// The following is for developers to modify `agendaData` dates to match
// your current system time so that features depending on an overlap of times
// such as the 'Now' button and red divider are available.
// This should be commented out when not in
// (function(){
holloway marked this conversation as resolved.
Show resolved Hide resolved
// if (location.hostname !== "localhost") {
// console.log(`Not modifying \`agendaData\` because we're not on localhost: ${location.hostname}`)
// return
// }
// const originalStartDateMs = new Date(agendaData.meeting.startDate).getTime()
// const oneDayInMilliseconds = 24 * 60 * 60 * 1000
// const startDate = new Date(Date.now() - (2 * oneDayInMilliseconds))
// agendaData.meeting.startDate = `${startDate.getFullYear()}-${("00" + (startDate.getMonth() + 1)).slice (-2)}-${startDate.getDate()}`
// const startDateDifferenceMs = new Date(agendaData.meeting.startDate).getTime() - originalStartDateMs
// const endDate = new Date(Date.now() + (5 * oneDayInMilliseconds))
// agendaData.meeting.endDate = `${endDate.getFullYear()}-${("00" + (endDate.getMonth() + 1)).slice (-2)}-${endDate.getDate()}`
// agendaData.schedule = agendaData.schedule.map(schedule => ({
// ...schedule,
// startDateTime: new Date(new Date(schedule.startDateTime).getTime() + startDateDifferenceMs).toISOString()
// }))
// console.log("Server `agendaData` has dates modified for your system time. This should not be done in prod.", agendaData)
// }())

// -> Load meeting data
this.categories = agendaData.categories
this.floors = agendaData.floors
Expand Down Expand Up @@ -292,3 +315,8 @@ function findFirstConferenceUrl (txt) {
} catch (err) { }
return null
}

export const daySlugPrefix = 'agenda-day-'
export function daySlug(s) {
return `${daySlugPrefix}${s.adjustedStartDate}` // eg 'agenda-day-2024-08-13'
}
2 changes: 1 addition & 1 deletion playwright/tests/meeting/agenda.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1431,7 +1431,7 @@ test.describe('past - small screens', () => {

// can open the jump to day dropdown
await barBtnLocator.first().click()
const jumpDayDdnLocator = page.locator('.n-dropdown-menu > .n-dropdown-option')
const jumpDayDdnLocator = page.locator('.n-dropdown-menu [data-testid=mobile-link]')
await expect(jumpDayDdnLocator).toHaveCount(7)
for (let idx = 0; idx < 7; idx++) {
const localDateTime = DateTime.fromISO(meetingData.meeting.startDate, { zone: meetingData.meeting.timezone })
Expand Down
Loading