Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 .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