Skip to content

Commit

Permalink
feat(agenda): various agenda improvements and fixes (ietf-tools#4613)
Browse files Browse the repository at this point in the history
* chore: update dependencies + add jsconfig

* fix(agenda): handle localStorage being disabled

* feat: agenda share modal

* feat: agenda tour

* feat: agenda share filters + picked sessions + fixes

* test: fix agenda tests

* test: add agenda share dialog test

* test: remove agenda only flag
  • Loading branch information
NGPixel authored Oct 21, 2022
1 parent d08815d commit 395f110
Show file tree
Hide file tree
Showing 93 changed files with 1,291 additions and 792 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ indent_size = 2
[dev/**.js]
indent_size = 2

[{package.json,.eslintrc.js,.yarnrc.yml,vite.config.js,cypress.config.js}]
[{package.json,.eslintrc.js,.yarnrc.yml,vite.config.js,jsconfig.json}]
indent_size = 2

# Settings for cypress tests
Expand Down
849 changes: 443 additions & 406 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
96 changes: 73 additions & 23 deletions client/agenda/Agenda.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,28 @@

.agenda-topnav.my-3
meeting-navigation
n-button.d-none.d-sm-flex(
quaternary
@click='toggleSettings'
)
template(#icon)
i.bi.bi-gear
span Settings
.agenda-topnav-right.d-none.d-md-flex
n-button(
quaternary
@click='startTour'
)
template(#icon)
i.bi.bi-question-square
span Help
n-button(
quaternary
@click='toggleShare'
)
template(#icon)
i.bi.bi-share
span Share
n-button(
quaternary
@click='toggleSettings'
)
template(#icon)
i.bi.bi-gear
span Settings

.row
.col
Expand Down Expand Up @@ -137,6 +152,7 @@
agenda-quick-access

agenda-mobile-bar
agenda-share-modal(v-model:shown='state.shareModalShown')
</template>

<script setup>
Expand All @@ -159,10 +175,12 @@ import AgendaScheduleList from './AgendaScheduleList.vue'
import AgendaScheduleCalendar from './AgendaScheduleCalendar.vue'
import AgendaQuickAccess from './AgendaQuickAccess.vue'
import AgendaSettings from './AgendaSettings.vue'
import AgendaShareModal from './AgendaShareModal.vue'
import AgendaMobileBar from './AgendaMobileBar.vue'
import MeetingNavigation from './MeetingNavigation.vue'
import timezones from '../shared/timezones'
import { initTour } from './tour'
import { useAgendaStore } from './store'
import { useSiteStore } from '../shared/store'
Expand All @@ -187,6 +205,7 @@ const route = useRoute()
const state = reactive({
searchText: '',
shareModalShown: false
})
// REFS
Expand Down Expand Up @@ -219,8 +238,19 @@ watch(() => agendaStore.meetingDays, () => {
})
watch(() => agendaStore.isLoaded, () => {
let resetQuery = false
if (route.query.filters) {
// Handle ?filters= parameter
const keywords = route.query.filters.split(',').map(k => k.trim()).filter(k => !!k)
if (keywords?.length > 0) {
agendaStore.$patch({
selectedCatSubs: keywords
})
}
resetQuery = true
}
if (route.query.show) {
// Handle legacy ?show= parameter
// Handle ?show= parameter
const keywords = route.query.show.split(',').map(k => k.trim()).filter(k => !!k)
if (keywords?.length > 0) {
const pickedIds = []
Expand All @@ -235,13 +265,23 @@ watch(() => agendaStore.isLoaded, () => {
pickerModeView: true,
pickedEvents: pickedIds
})
agendaStore.persistMeetingPreferences()
}
}
resetQuery = true
}
if (route.query.pick) {
// Handle legacy /personalize path (open picker mode)
agendaStore.$patch({ pickerMode: true })
resetQuery = true
}
if (route.query.tz) {
// Handle tz param
agendaStore.$patch({ timezone: route.query.tz })
resetQuery = true
}
if (resetQuery) {
agendaStore.persistMeetingPreferences()
router.replace({ query: null })
}
Expand Down Expand Up @@ -313,6 +353,18 @@ function toggleSettings () {
})
}
function toggleShare () {
state.shareModalShown = !state.shareModalShown
}
function startTour () {
const tour = initTour({
mobileMode: siteStore.viewport < 990,
pickerMode: agendaStore.pickerMode
})
tour.start()
}
// -> Go to current meeting if not provided
function handleCurrentMeetingRedirect () {
if (!route.params.meetingNumber && agendaStore.meeting.number) {
Expand Down Expand Up @@ -394,15 +446,6 @@ onMounted(() => {
}
})
// CREATED
// -> Handle loading tab directly based on URL
if (window.location.pathname.indexOf('-utc') >= 0) {
agendaStore.$patch({ timezone: 'UTC' })
} else if (window.location.pathname.indexOf('personalize') >= 0) {
// state.currentTab = 'personalize'
}
</script>
<style lang="scss">
Expand All @@ -421,18 +464,25 @@ if (window.location.pathname.indexOf('-utc') >= 0) {
&-topnav {
position: relative;
> button {
&-right {
position: absolute;
top: 5px;
right: 0;
display: flex;
.bi {
transition: transform 1s ease;
button + button {
margin-left: 5px;
}
&:hover {
> button:last-child {
.bi {
transform: rotate(180deg);
transition: transform 1s ease;
}
&:hover {
.bi {
transform: rotate(180deg);
}
}
}
}
Expand Down
13 changes: 10 additions & 3 deletions client/agenda/AgendaFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ n-drawer(v-model:show='state.isShown', placement='bottom', :height='state.drawer
</template>

<script setup>
import { reactive, ref, unref, watch } from 'vue'
import { nextTick, reactive, ref, unref, watch } from 'vue'
import intersection from 'lodash/intersection'
import difference from 'lodash/difference'
import union from 'lodash/union'
Expand Down Expand Up @@ -113,8 +113,15 @@ function cancelFilter () {
}
function saveFilter () {
agendaStore.$patch({ selectedCatSubs: state.pendingSelection })
state.isShown = false
const applyLoadingMsg = message.create('Applying filters...', { type: 'loading', duration: 0 })
setTimeout(() => {
agendaStore.$patch({ selectedCatSubs: state.pendingSelection })
agendaStore.persistMeetingPreferences()
state.isShown = false
nextTick(() => {
applyLoadingMsg.destroy()
})
}, 500)
}
function clearFilter () {
Expand Down
173 changes: 173 additions & 0 deletions client/agenda/AgendaShareModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<template lang="pug">
n-modal(v-model:show='modalShown')
n-card.agenda-share(
:bordered='false'
segmented
role='dialog'
aria-modal='true'
)
template(#header-extra)
.agenda-share-header
n-button.ms-4.agenda-share-close(
ghost
color='gray'
strong
@click='modalShown = false'
)
i.bi.bi-x
template(#header)
.agenda-share-header
i.bi.bi-share
span Share this view
.agenda-share-content
.text-muted.pb-2 Use the following URL for sharing the current view #[em (including any active filters)] with other users:
n-input-group
n-input(
ref='filteredUrlIpt'
size='large'
readonly
v-model:value='state.filteredUrl'
)
n-button(
type='primary'
primary
strong
size='large'
@click='copyFilteredUrl'
)
template(#icon)
i.bi.bi-clipboard-check.me-1
span Copy
</template>

<script setup>
import { computed, reactive, ref, watch } from 'vue'
import { find } from 'lodash-es'
import {
NButton,
NCard,
NModal,
NInputGroup,
NInput,
useMessage
} from 'naive-ui'
import { useAgendaStore } from './store'
// PROPS
const props = defineProps({
shown: {
type: Boolean,
required: true,
default: false
}
})
// MESSAGE PROVIDER
const message = useMessage()
// STORES
const agendaStore = useAgendaStore()
// EMIT
const emit = defineEmits(['update:shown'])
// STATE
const state = reactive({
isLoading: false,
filteredUrl: window.location.href
})
const filteredUrlIpt = ref(null)
// COMPUTED
const modalShown = computed({
get () {
return props.shown
},
set(value) {
emit('update:shown', value)
}
})
// WATCHERS
watch(() => props.shown, (newValue) => {
if (newValue) {
generateUrl()
}
})
// METHODS
function generateUrl () {
const newUrl = new URL(window.location.href)
const queryParams = []
if (agendaStore.selectedCatSubs.length > 0 ) {
queryParams.push(`filters=${agendaStore.selectedCatSubs.join(',')}`)
}
if (agendaStore.pickerMode && agendaStore.pickedEvents.length > 0 ) {
const kwds = []
for (const id of agendaStore.pickedEvents) {
const session = find(agendaStore.scheduleAdjusted, ['id', id])
if (session) {
const suffix = session.sessionToken ? `-${session.sessionToken}` : ''
kwds.push(`${session.acronym}${suffix}`)
}
}
queryParams.push(`show=${kwds.join(',')}`)
}
newUrl.search = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''
state.filteredUrl = newUrl.toString()
}
async function copyFilteredUrl () {
filteredUrlIpt.value?.select()
try {
if (navigator.clipboard) {
await navigator.clipboard.writeText(state.filteredUrl)
} else {
if (!document.execCommand('copy')) {
throw new Error('Copy failed')
}
}
message.success('URL copied to clipboard successfully.')
} catch (err) {
message.error('Failed to copy URL to clipboard.')
}
}
</script>
<style lang="scss">
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
.agenda-share {
width: 90vw;
max-width: 1000px;
&-header {
font-size: 20px;
display: flex;
align-items: center;
> .bi {
margin-right: 12px;
font-size: 20px;
color: $indigo;
}
}
&-close .bi {
font-size: 20px;
color: inherit;
}
}
</style>
Loading

0 comments on commit 395f110

Please sign in to comment.