diff --git a/components/Calendar.vue b/components/Calendar.vue index 3b6eb920..9f9f0c92 100644 --- a/components/Calendar.vue +++ b/components/Calendar.vue @@ -19,6 +19,9 @@ import { take, get } from 'lodash' export default { name: 'Calendar', + props: { + events: { type: Array, default: [] } + }, data () { const month = dayjs().month() + 1 const year = dayjs().year() @@ -27,7 +30,6 @@ export default { } }, computed: { - ...mapGetters(['filteredEventsWithPast']), ...mapState(['tags', 'filters', 'in_past', 'settings']), // TODO: could be better @@ -48,7 +50,7 @@ export default { return color } - attributes = attributes.concat(this.filteredEventsWithPast + attributes = attributes.concat(this.events .filter(e => !e.multidate) .map(e => { return { @@ -58,7 +60,7 @@ export default { } })) - attributes = attributes.concat(this.filteredEventsWithPast + attributes = attributes.concat(this.events .filter(e => e.multidate) .map(e => ({ key: e.id, @@ -72,11 +74,12 @@ export default { methods: { ...mapActions(['updateEvents', 'showPastEvents']), updatePage (page) { - this.updateEvents(page) + this.$emit('monthchange', page) }, click (day) { - const element = document.getElementById(day.day) - if (element) { element.scrollIntoView() } // Even IE6 supports this + this.$emit('dayclick', day) + // const element = document.getElementById(day.day) + // if (element) { element.scrollIntoView() } // Even IE6 supports this } } } diff --git a/components/Event.vue b/components/Event.vue index 05df2d84..9be5bdb2 100644 --- a/components/Event.vue +++ b/components/Event.vue @@ -9,11 +9,11 @@ v-card-text time.text-h6(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")') mdi-event {{ event|when }} - v-btn.d-block.text-h6(text color='primary' big) mdi-map-marker {{event.place.name}} + v-btn.d-block.text-h6(text color='primary' big @click="$emit('placeclick', event.place.id)") mdi-map-marker {{event.place.name}} v-card-actions v-chip.ml-1(v-for='tag in event.tags' link - :key='tag' outlined color='primary' @click='addTag(tag)') {{tag}} + :key='tag' outlined color='primary' @click="$emit('tagclick',tag)") {{tag}} v-spacer v-menu(offset-y) @@ -31,20 +31,9 @@ import { mapState, mapActions } from 'vuex' export default { props: { event: { type: Object, default: () => ({}) }, - showTags: { - type: Boolean, - default: true - }, - showImage: { - type: Boolean, - default: true - } }, computed: { - ...mapState(['settings', 'filters']), - description () { - return this.event.description.replace(/(
)+/g, '
') - }, + ...mapState(['settings']), show_footer () { return (this.event.tags.length || this.event.resources.length) } diff --git a/components/Home.vue b/components/Home.vue index eb75a73f..21129339 100644 --- a/components/Home.vue +++ b/components/Home.vue @@ -10,18 +10,26 @@ //- this is needed as v-calendar does not support SSR //- https://github.com/nathanreyes/v-calendar/issues/336 client-only - Calendar + Calendar(@dayclick='dayClick' + @monthchange='monthChange' :events='events') .col - Search + Search( + :filters='filters' + @update='updateFilters' + ) + .text-h3.text-center(v-if='selectedDay') {{selectedDay|day}} #events - Event(v-for='event in events' :key='event.id' :event='event') + Event(v-for='event in events' + :key='event.id' :event='event' + @tagclick='tagClick' @placeclick='placeClick') diff --git a/locales/esm.js b/locales/esm.js index 106796f1..d3b9a6bb 100644 --- a/locales/esm.js +++ b/locales/esm.js @@ -4,5 +4,7 @@ export default { es: 'Español', ca: 'Català', pl: 'Polski', - eu: 'Euskara' + eu: 'Euskara', + nb_NO: 'Norwegian Bokmål', + fr: 'Francais' } diff --git a/nuxt.config.js b/nuxt.config.js index 771c1756..8da3e506 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -41,6 +41,7 @@ module.exports = { '@/plugins/vue-clipboard', // vuetify '@/plugins/axios', // axios baseurl configuration '@/plugins/validators', // inject validators + '@/plugins/api', // api helpers { src: '@/plugins/v-calendar', ssr: false } // calendar, fix ssr ], diff --git a/pages/add/ImportDialog.vue b/pages/add/ImportDialog.vue index 59724434..b1ced6c7 100644 --- a/pages/add/ImportDialog.vue +++ b/pages/add/ImportDialog.vue @@ -56,9 +56,7 @@ export default { reader.readAsText(this.file) reader.onload = () => { const data = reader.result - console.error(data) const event = ical.parse(data) - console.error(event) this.event = { title: event.name } @@ -79,7 +77,6 @@ export default { this.event = ret // check if contain an h-event this.$emit('imported', ret) - console.error(ret) } catch (e) { console.error(e) this.error = true diff --git a/pages/add/_edit.vue b/pages/add/_edit.vue index 29705321..0e95fb01 100644 --- a/pages/add/_edit.vue +++ b/pages/add/_edit.vue @@ -281,7 +281,6 @@ export default { } if (this.event.image) { - console.error(this.event.image) formData.append('image', this.event.image) } formData.append('title', this.event.title) @@ -307,7 +306,6 @@ export default { this.loading = false this.$root.$message(this.$auth.loggedIn ? 'event.added' : 'event.added_anon', { color: 'success' }) } catch (e) { - console.error(e.response) switch (e.request.status) { case 413: this.$root.$message('event.image_too_big', { color: 'error' }) diff --git a/pages/announcement/_id.vue b/pages/announcement/_id.vue index 4c5f1957..18f5ccfb 100644 --- a/pages/announcement/_id.vue +++ b/pages/announcement/_id.vue @@ -17,7 +17,6 @@ export default { try { const id = Number(params.id) const announcement = store.state.announcements.find(a => a.id === id) - console.error(announcement) return { announcement } } catch (e) { error({ statusCode: 404, message: 'Announcement not found' }) diff --git a/pages/export.vue b/pages/export.vue index ec639dde..55c9a5c3 100644 --- a/pages/export.vue +++ b/pages/export.vue @@ -85,8 +85,7 @@ export default { } }, computed: { - ...mapState(['filters', 'events', 'settings']), - ...mapGetters(['filteredEvents']), + ...mapState(['filters', 'settings']), domain () { const URL = url.parse(this.settings.baseurl) return URL.hostname diff --git a/plugins/api.js b/plugins/api.js new file mode 100644 index 00000000..77e664a7 --- /dev/null +++ b/plugins/api.js @@ -0,0 +1,34 @@ + +export default ({ $axios, store }, inject) => { + + const api = { + + /** + * Get events + * + * filter: { + * start_datetime: unix_timestamp (default now) + * end_datetime: unix_timestamp + * tags: [tag, list], + * places: [place_id], + * limit: (default ∞) + * } + * + */ + async getEvents (params) { + try { + const events = await $axios.$get(`/events`, { params: { + start: params.start, + end: params.end, + places: params.places && params.places.join(','), + tags: params.tags && params.tags.join(',') + }} ) + return events + } catch (e) { + console.error(e) + return [] + } + } + } + inject('api', api) +} \ No newline at end of file diff --git a/server/api/controller/event.js b/server/api/controller/event.js index 45246bf7..38e5f58a 100644 --- a/server/api/controller/event.js +++ b/server/api/controller/event.js @@ -417,17 +417,30 @@ const eventController = { } }, - async _select (start = moment().unix(), limit = 100) { + async _select ({ start, end, tags, places}) { + const where = { // confirmed event only recurrent: null, is_visible: true, - start_datetime: { [Op.gt]: start } + start_datetime: { [Op.gt]: start }, + } + + if (end) { + where['end_datetime'] = { [Op.lt]: end } + } + + if (places) { + where.placeId = places.split(',') + } + + let where_tags = {} + if (tags) { + where_tags = { where: { tag: tags.split(',') } } } const events = await Event.findAll({ where, - limit, attributes: { exclude: ['slug', 'likes', 'boost', 'userId', 'is_visible', 'createdAt', 'updatedAt', 'placeId'] // include: [[Sequelize.fn('COUNT', Sequelize.col('activitypub_id')), 'ressources']] @@ -435,7 +448,7 @@ const eventController = { order: ['start_datetime', [Tag, 'weigth', 'DESC']], include: [ { model: Resource, required: false, attributes: ['id'] }, - { model: Tag, attributes: ['tag'], required: false, through: { attributes: [] } }, + { model: Tag, attributes: ['tag'], required: tags ? true : false, ...where_tags, through: { attributes: [] } }, { model: Place, required: false, attributes: ['id', 'name', 'address'] } ] }) @@ -451,9 +464,14 @@ const eventController = { * Select events based on params */ async select (req, res) { - const start = req.query.start || moment().unix() - const limit = req.query.limit || 100 - res.json(await eventController._select(start, limit)) + const start = req.query.start + const end = req.query.end + const tags = req.query.tags + const places = req.query.places + + res.json(await eventController._select({ + start, end, places, tags + })) }, /** diff --git a/server/api/controller/settings.js b/server/api/controller/settings.js index 2490450f..85b565df 100644 --- a/server/api/controller/settings.js +++ b/server/api/controller/settings.js @@ -125,7 +125,6 @@ const settingsController = { .resize(400) .png({ quality: 90 }) .toFile(baseImgPath + '.png', async (err, info) => { - console.error(err) const image = await readFile(baseImgPath + '.png') const favicon = await toIco([image], { sizes: [64], resize: true }) writeFile(baseImgPath + '.ico', favicon) diff --git a/server/api/index.js b/server/api/index.js index 0cff1eb1..83140cbc 100644 --- a/server/api/index.js +++ b/server/api/index.js @@ -121,7 +121,7 @@ api.get('/export/:type', cors, exportController.export) // get events in this range // api.get('/event/:month/:year', cors, eventController.getAll) -api.get('/event', cors, eventController.select) +api.get('/events', cors, eventController.select) api.get('/instances', isAdmin, instanceController.getAll) api.get('/instances/:instance_domain', isAdmin, instanceController.get) diff --git a/server/helpers.js b/server/helpers.js index 4c784632..ec04b5e3 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -115,7 +115,6 @@ module.exports = { Microformats.get({ html: response.data, filter: ['h-event'] }, (err, data) => { if (!data.items.length || !data.items[0].properties) return res.sendStatus(404) const event = data.items[0].properties - console.error(event) return res.json({ title: get(event, 'name[0]', ''), description: get(event, 'content[0]', ''), diff --git a/server/routes.js b/server/routes.js index e7e83d5c..c28c312e 100644 --- a/server/routes.js +++ b/server/routes.js @@ -66,8 +66,8 @@ app.use((error, req, res, next) => { // first nuxt component is ./pages/index.vue (with ./layouts/default.vue) // prefill current events, tags, places and announcements (used in every path) app.use(async (req, res, next) => { - const start_datetime = getUnixTime(startOfWeek(startOfMonth(new Date()))) - req.events = await eventController._select(start_datetime, 100) + // const start_datetime = getUnixTime(startOfWeek(startOfMonth(new Date()))) + // req.events = await eventController._select(start_datetime, 100) req.meta = await eventController._getMeta() req.announcements = await announceController._getVisible() next() diff --git a/store/index.js b/store/index.js index bd4b6b10..abd14bfb 100644 --- a/store/index.js +++ b/store/index.js @@ -1,10 +1,10 @@ -import moment from 'moment-timezone' +import dayjs from 'dayjs' import intersection from 'lodash/intersection' export const state = () => ({ locale: '', user_locale: {}, - events: [], + filters: { tags: [], places: [] }, tags: [], places: [], settings: { @@ -20,113 +20,27 @@ export const state = () => ({ enable_trusted_instances: true, trusted_instances: [] }, - in_past: false, - filters: { - tags: [], - places: [], - show_past_events: false, - show_recurrent_events: false, - show_pinned_event: false - }, announcements: [] }) -export const getters = { - - // filter matches search tag/place - filteredEvents: state => { - const search_for_tags = !!state.filters.tags.length - const search_for_places = !!state.filters.places.length - - return state.events.filter(e => { - // filter past events - if (!state.filters.show_past_events && e.past) { return false } - - // filter recurrent events - if (!state.filters.show_recurrent_events && e.parentId) { return false } - - if (search_for_places && !state.filters.places.includes(e.place.id)) { - return false - } - - if (search_for_tags) { - const common_tags = intersection(e.tags, state.filters.tags) - if (common_tags.length === 0) { return false } - } - - if (!search_for_places && !search_for_tags) { return true } - - return true - }) - }, - - // filter matches search tag/place including past events - filteredEventsWithPast: state => { - const search_for_tags = !!state.filters.tags.length - const search_for_places = !!state.filters.places.length - - return state.events.filter(e => { - // filter recurrent events - if (!state.filters.show_recurrent_events && e.parentId) { return false } - - if (search_for_places && !state.filters.places.includes(e.place.id)) { - return false - } - - if (search_for_tags) { - const common_tags = intersection(e.tags, state.filters.tags) - if (common_tags.length === 0) { return false } - } - - return true - }) - } -} - export const mutations = { - setEvents (state, events) { - // set`past` and `newDay` flags to event - let lastDay = null - state.events = events.map(e => { - const currentDay = moment.unix(e.start_datetime).date() - e.newDay = (!lastDay || lastDay !== currentDay) && currentDay - lastDay = currentDay - const end_datetime = e.end_datetime || e.start_datetime + 3600 * 2 - const past = ((moment().unix()) - end_datetime) > 0 - e.past = !!past - return e - }) - }, - addEvent (state, event) { - state.events.push(event) - }, - updateEvent (state, event) { - state.events = state.events.map((e) => { - if (e.id !== event.id) { return e } - return event - }) - }, - delEvent (state, eventId) { - state.events = state.events.filter(ev => { - return ev.id !== eventId - }) - }, + // setEvents (state, events) { + // // set`past` and `newDay` flags to event + // let lastDay = null + // state.events = events.map(e => { + // const currentDay = dayjs.unix(e.start_datetime).date() + // e.newDay = (!lastDay || lastDay !== currentDay) && currentDay + // lastDay = currentDay + // const end_datetime = e.end_datetime || e.start_datetime + 3600 * 2 + // const past = ((dayjs().unix()) - end_datetime) > 0 + // e.past = !!past + // return e + // }) + // }, update (state, { tags, places }) { state.tags = tags state.places = places }, - setSearchTags (state, tags) { - state.filters.tags = tags - }, - setSearchPlaces (state, places) { - state.filters.places = places - }, - showPastEvents (state, show) { - state.filters.show_past_events = show - }, - showRecurrentEvents (state, show) { - state.filters.show_recurrent_events = show - }, setSettings (state, settings) { state.settings = settings }, @@ -136,8 +50,9 @@ export const mutations = { setLocale (state, locale) { state.locale = locale }, - setPast (state, in_past) { - state.in_past = in_past + setFilters (state, filters) { + state.filters.tags = [...filters.tags] + state.filters.places = [...filters.places] }, setAnnouncements (state, announcements) { state.announcements = announcements @@ -149,23 +64,8 @@ export const actions = { // we use it to get configuration from db, set locale, etc... nuxtServerInit ({ commit }, { req }) { commit('setSettings', req.settings) - - commit('setEvents', req.events) commit('setAnnouncements', req.announcements) commit('update', req.meta) - - // apply settings - commit('showRecurrentEvents', req.settings.allow_recurrent_event && req.settings.recurrent_event_visible) - }, - async updateEvents ({ commit }, page) { - const [month, year] = [moment().month(), moment().year()] - const in_past = page.year < year || (page.year === year && page.month <= month) - // commit('setPast', in_past) - const start_datetime = moment().year(page.year).month(page.month - 1).startOf('month').startOf('week').unix() - const query = `start=${start_datetime}` - const events = await this.$axios.$get(`/event?${query}`) - commit('setEvents', events) - commit('showPastEvents', in_past) }, async updateAnnouncements ({ commit }) { const announcements = await this.$axios.$get('/announcements') @@ -176,16 +76,13 @@ export const actions = { commit('update', { tags, places }) }, async addEvent ({ commit }, formData) { - const event = await this.$axios.$post('/event', formData) - if (event.user) { - commit('addEvent', event) - } + await this.$axios.$post('/event', formData) }, async updateEvent ({ commit }, formData) { - const event = await this.$axios.$put('/event', formData) - if (event.user) { - commit('updateEvent', event) - } + await this.$axios.$put('/event', formData) + }, + setFilters({ commit }, filters) { + commit('setFilters', filters) }, setAnnouncements ({ commit }, announcements) { commit('setAnnouncements', announcements) @@ -193,18 +90,6 @@ export const actions = { delEvent ({ commit }, eventId) { commit('delEvent', eventId) }, - setSearchTags ({ commit }, tags) { - commit('setSearchTags', tags) - }, - setSearchPlaces ({ commit }, places) { - commit('setSearchPlaces', places) - }, - showPastEvents ({ commit }, show) { - commit('showPastEvents', show) - }, - showRecurrentEvents ({ commit }, show) { - commit('showRecurrentEvents', show) - }, async setSetting ({ commit }, setting) { await this.$axios.$post('/settings', setting) commit('setSetting', setting)