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

Implement sort most popular sessions view #12

Merged
merged 13 commits into from
Sep 10, 2024
286 changes: 194 additions & 92 deletions src/App.vue

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions src/components/AppDropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div v-on-clickaway="away" class="app-drop-down">
<bunt-button @click="toggle">
<slot name="toggler"> Toggle </slot>
<svg
v-if="this.sharedState.active"
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
>
<path fill="currentColor" d="m7 15l5-5l5 5z" />
</svg>
<svg
v-else
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 24 24"
>
<path fill="currentColor" d="m7 10l5 5l5-5z" />
</svg>
</bunt-button>
<slot />
</div>
</template>
<script>
import { mixin as clickaway } from 'vue-clickaway'

export default {
name: 'AppDropdown',
mixins: [clickaway],
provide () {
return {
sharedState: this.sharedState
}
},
data () {
return {
sharedState: {
active: false,
},
}
},
methods: {
toggle () {
this.sharedState.active = !this.sharedState.active
},
away () {
this.sharedState.active = false
}
}
}
</script>
<style>
.app-drop-down {
margin: 0px 4px;
border-radius: 5px;
border: 2px solid rgba(0, 0, 0, 0.38);
}

.app-drop-down .bunt-button {
background-color: white;
}

.app-drop-down .bunt-button .bunt-button-content {
text-transform: capitalize;
}
</style>
55 changes: 55 additions & 0 deletions src/components/AppDropdownContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<transition name="dropdown-content">
<div v-if="active" class="dropdown-content">
<slot />
</div>
</transition>
</template>

<script>
export default {
name: 'AppDropdownContent',
inject: ['sharedState'],
computed: {
active () {
return this.sharedState.active
},
},
}
</script>

<style>
.dropdown-content {
position: absolute;
background: #ffffff;
margin-top: 4px;
border: 1px solid rgba(0, 0, 0, 0.38);
border-radius: 5px;
max-width: calc(100% - 40px);
max-height: 350px;
overflow-y: auto;
}
.dropdown-content .checkbox-line {
margin: 8px;
}
.dropdown-content .checkbox-line .bunt-checkbox .bunt-checkbox-box {
min-width: 20px;
}
.dropdown-content-enter-active,
.dropdown-content-leave-active {
transition: all 0.2s;
}
.dropdown-content-enter,
.dropdown-content-leave-to {
opacity: 0;
transform: translateY(-5px);
}

@media screen and (max-width: 480px) {
.dropdown-content {
/* left: 5px; */
max-width: 50%;
z-index: 10;
}
}
</style>
11 changes: 11 additions & 0 deletions src/components/AppDropdownItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<div>
<slot/>
</div>
</template>

<script>
export default {
name: "AppDropdownItem"
};
</script>
hongquan marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 4 additions & 2 deletions src/components/GridSchedule.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export default {
}
})
// remove gap at the end of the schedule
if (compactedSlices[compactedSlices.length - 1].gap) compactedSlices.pop()
if (compactedSlices && compactedSlices[compactedSlices.length - 1]?.gap) compactedSlices.pop()
return compactedSlices
},
visibleTimeslices () {
Expand Down Expand Up @@ -330,6 +330,8 @@ export default {
.c-grid-schedule
flex: auto
background-color: $clr-grey-50
max-width: 100vw
overflow: auto
.grid
display: grid
grid-template-columns: 78px repeat(var(--total-rooms), 1fr) auto
Expand All @@ -338,7 +340,7 @@ export default {
min-width: min-content
> .room
position: sticky
top: calc(var(--pretalx-sticky-date-offset) + var(--pretalx-sticky-top-offset, 0px))
// top: calc(var(--pretalx-sticky-date-offset) + var(--pretalx-sticky-top-offset, 0px))
hongquan marked this conversation as resolved.
Show resolved Hide resolved
display: flex
justify-content: center
align-items: center
Expand Down
93 changes: 48 additions & 45 deletions src/components/LinearSchedule.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template lang="pug">
.c-linear-schedule(v-scrollbar.y="")
.bucket(v-for="({date, sessions}, index) of sessionBuckets")
.bucket(v-if="sortBy == 'time'", v-for="({date, sessions}, index) of sessionBuckets")
hongquan marked this conversation as resolved.
Show resolved Hide resolved
.bucket-label(:ref="getBucketName(date)", :data-date="date.format()")
.day(v-if="index === 0 || date.clone().startOf('day').diff(sessionBuckets[index - 1].date.clone().startOf('day'), 'day') > 0") {{ date.format('dddd DD. MMMM') }}
.time {{ date.format('LT') }}
Expand All @@ -15,8 +15,21 @@
)
.break(v-else)
.title {{ getLocalizedString(session.title) }}
.bucket(v-if="sortBy == 'popularity' || sortBy == 'title'", v-for="session of sessionBuckets")
.sorted-session
session(
v-if="isProperSession(session)",
:session="session",
:faved="session.id && favs.includes(session.id)",
@fav="$emit('fav', session.id)",
@unfav="$emit('unfav', session.id)",
isLinearSchedule=true
)
.break(v-else)
.title {{ getLocalizedString(session.title) }}
</template>
<script>
import _ from 'lodash'
import moment from 'moment-timezone'
import { getLocalizedString } from 'utils'
import Session from './Session'
Expand Down Expand Up @@ -46,52 +59,40 @@ export default {
},
computed: {
sessionBuckets () {
const buckets = {}
for (const session of this.sessions) {
const key = session.start.format()
if (!buckets[key]) {
buckets[key] = []
}
if (!session.id) {
// Remove duplicate breaks, meaning same start, end and text
session.break_id = `${session.start}${session.end}${session.title}`
if (buckets[key].filter(s => s.break_id === session.break_id).length === 0) buckets[key].push(session)
} else {
buckets[key].push(session)
}
}

return Object.entries(buckets).map(([date, sessions]) => {
let sessionBucket = {}
switch (this.sortBy) {
case 'title':
sessionBucket = {
date: sessions[0].start,
// sort by room for stable sort across time buckets
sessions: sessions.sort((a, b) => {
return a.title.localeCompare(b.title)
})
}
break
case 'popularity':
sessionBucket = {
date: sessions[0].start,
// sort by room for stable sort across time buckets
sessions: sessions.sort((a, b) => {
return b.fav_count - a.fav_count
})
}
break
default:
sessionBucket = {
date: sessions[0].start,
// sort by room for stable sort across time buckets
sessions: sessions.sort((a, b) => this.rooms.findIndex(room => room.id === a.room.id) - this.rooms.findIndex(room => room.id === b.room.id))
}
if (this.sortBy === 'time') {
const buckets = {}
for (const session of this.sessions) {
const key = session.start.format()
if (!buckets[key]) {
buckets[key] = []
}
if (!session.id) {
// Remove duplicate breaks, meaning same start, end and text
session.break_id = `${session.start}${session.end}${session.title}`
if (!buckets[key].some(s => s.break_id === session.break_id)) buckets[key].push(session)
} else {
buckets[key].push(session)
}
}

return sessionBucket
})
return Object.entries(buckets).map(([date, sessions]) => ({
date: sessions[0].start,
// sort by room for stable sort across time buckets
sessions: _.sortBy(sessions, session => this.rooms.findIndex(room => room.id === session.room.id))
}))
} else {
const sortedSessions = this.sessions.slice().sort((a, b) => {
switch (this.sortBy) {
case 'title':
return a.title.localeCompare(b.title)
case 'popularity':
return b.fav_count - a.fav_count
default:
return _.sortBy(this.rooms, room => room.id === a.room.id) - _.sortBy(this.rooms, room => room.id === b.room.id)
hongquan marked this conversation as resolved.
Show resolved Hide resolved
}
})
return sortedSessions
}
}
},
watch: {
Expand Down Expand Up @@ -189,6 +190,8 @@ export default {
padding-left: 16px
.day
font-weight: 600
.sorted-session
padding-left: 16px
.break
z-index: 10
margin: 8px
Expand Down
17 changes: 14 additions & 3 deletions src/components/Session.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ a.c-linear-schedule-session(:class="{faved}", :style="style", :href="link", @cli
.time {{ startTime.time }}
.ampm(v-if="startTime.ampm") {{ startTime.ampm }}
.duration {{ getPrettyDuration(session.start, session.end) }}
.session-date {{ getPrettyDate(session.start) }}
.buffer
.is-live(v-if="isLive") live
.info
Expand Down Expand Up @@ -36,7 +37,7 @@ a.c-linear-schedule-session(:class="{faved}", :style="style", :href="link", @cli
<script>
import moment from 'moment-timezone'
import MarkdownIt from 'markdown-it'
import { getLocalizedString, getPrettyDuration } from 'utils'
import { getLocalizedString, getPrettyDuration, getPrettyDate } from 'utils'

const markdownIt = MarkdownIt({
linkify: true,
Expand Down Expand Up @@ -81,7 +82,8 @@ export default {
data () {
return {
getPrettyDuration,
getLocalizedString
getLocalizedString,
getPrettyDate,
}
},
computed: {
Expand Down Expand Up @@ -151,7 +153,8 @@ export default {
color: $clr-primary-text-light
position: relative
.time-box
width: 69px
min-width: 75px
max-width: 75px
box-sizing: border-box
background-color: var(--track-color)
padding: 12px 16px 8px 12px
Expand All @@ -174,6 +177,14 @@ export default {
font-size: 13px
.duration
color: $clr-secondary-text-dark
margin-bottom: 8px
.session-date
color: $clr-primary-text-dark
font-size: 13px
font-weight: 600
display: flex
flex-direction: column
align-items: flex-end
.buffer
flex: auto
.is-live
Expand Down
5 changes: 5 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// import i18n from 'i18n'
import moment from 'moment';

export function getLocalizedString (string) {
if (!string) return ''
Expand Down Expand Up @@ -26,3 +27,7 @@ export function getPrettyDuration (start, end) {
}
return `${hours}h`
}
export function getPrettyDate (start) {
const date = moment(start);
return date.format('ddd DD. MMM');
}