Merge branch 'master' into gh

This commit is contained in:
lesion
2022-09-28 12:26:08 +02:00
47 changed files with 4964 additions and 6891 deletions

1
.gitignore vendored
View File

@@ -8,7 +8,6 @@
_*.js _*.js
config*.json config*.json
tests/seeds/testdb.sqlite tests/seeds/testdb.sqlite
preso.md
gancio.sqlite gancio.sqlite
db.sqlite db.sqlite
releases releases

View File

@@ -1,6 +1,38 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
### 1.5.0 - UNRELEASED ### 1.5.6 - 22 set '22
- update linkifyjs, sequelizem, nuxt deps
- improve homepage loading time
- restore removed icons
### 1.5.5 - 21 set '22
- fix #185 - wrong res.download api usage
- fix some dialog background on light theme
- update sequelize and remove live patch
- improve events filtering on selected day
- allow tags complete removals
- improve homepage performance
- docs: add scheme to nginx proxy configuration
### 1.5.4 - 6 set '22
- Update webcomponent deps
- Refactor datime display in webcomponent
- Force flyer download
- Restore range events on calendar
- Fix limit/max events for mariadb #183
- Fix endtime selection
- Fix microdata address
### 1.5.3 - 30 aug '22
- Fix end time selection when it's in the next day
### 1.5.2 - 26 aug '22
- fix Editor background color
### 1.5.1 - 14 aug '22
- fix regression with hidden recurrent events
### 1.5.0 - 8 aug '22
##### :warning: **BREAKING CHANGES**: ##### :warning: **BREAKING CHANGES**:
- supported nodejs version >=14 <=16 (nodejs 12 reached End-of-Life on 30 April 2022) - supported nodejs version >=14 <=16 (nodejs 12 reached End-of-Life on 30 April 2022)
- minimum mariadb supported version >= 10.5.2 - minimum mariadb supported version >= 10.5.2

View File

@@ -1,19 +1,31 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
export function attributesFromEvents(_events) { export function attributesFromEvents(_events) {
// const colors = ['teal', 'green', 'yellow', 'teal', 'indigo', 'green', 'red', 'purple', 'pink', 'gray']
// merge events with same date // merge events with same date
let attributes = [] let attributes = []
const now = dayjs().unix() const now = dayjs().unix()
for (let e of _events) { for (let e of _events) {
const key = dayjs.unix(e.start_datetime).format('YYYYMMDD') const key = dayjs.unix(e.start_datetime).tz().format('MMDD') // Math.floor(e.start_datetime/(3600*24)) // dayjs.unix(e.start_datetime).tz().format('YYYYMMDD')
const c = e.start_datetime < now ? 'vc-past' : '' const c = (e.end_datetime || e.start_datetime) < now ? 'vc-past' : ''
if (e.multidate) {
attributes.push({
dates: { start: new Date(e.start_datetime * 1000), end: new Date(e.end_datetime * 1000) },
highlight: {
start: { fillMode: 'outline' },
base: { fillMode: 'light' },
end: { fillMode: 'outline' },
}
})
continue
}
const i = attributes.find(a => a.day === key) const i = attributes.find(a => a.day === key)
if (!i) { if (!i) {
attributes.push({ day: key, key: e.id, n: 1, dates: new Date(e.start_datetime * 1000), attributes.push({
dot: { color: 'teal', class: c } }) day: key, key: e.id, n: 1, dates: new Date(e.start_datetime * 1000),
dot: { color: 'teal', class: c }
})
continue continue
} }

View File

@@ -57,11 +57,11 @@ li {
padding: 15px; padding: 15px;
} }
.editor .content { .editor .ProseMirror {
background-color: #f7f7f7; background-color: #f7f7f7;
} }
.theme--dark .editor .content { .theme--dark .editor .ProseMirror {
background-color: #292929; background-color: #292929;
} }

View File

@@ -9,7 +9,6 @@
@update:from-page='updatePage' @update:from-page='updatePage'
:locale='$i18n.locale' :locale='$i18n.locale'
:attributes='attributes' :attributes='attributes'
:timezone='settings.instance_timezone'
transition='fade' transition='fade'
aria-label='Calendar' aria-label='Calendar'
is-expanded is-expanded

View File

@@ -13,15 +13,14 @@ v-col(cols=12)
client-only client-only
.datePicker.mt-3 .datePicker.mt-3
v-input(:value='fromDate' v-input(:value='fromDate' :rules="[$validators.required('common.when')]")
:rules="[$validators.required('common.when')]")
vc-date-picker( vc-date-picker(
:value='fromDate' v-model='fromDate'
@input="date => change('date', date)"
:is-range='type === "multidate"' :is-range='type === "multidate"'
@input="date => change('date', fromDate)"
:timezone='settings.instance_timezone'
:attributes='attributes' :attributes='attributes'
:locale='$i18n.locale' :locale='$i18n.locale'
:from-page.sync='page'
:is-dark="settings['theme.is_dark']" :is-dark="settings['theme.is_dark']"
is-inline is-inline
is-expanded is-expanded
@@ -42,6 +41,9 @@ v-col(cols=12)
transition="scale-transition") transition="scale-transition")
template(v-slot:activator="{ on, attrs }") template(v-slot:activator="{ on, attrs }")
v-text-field( v-text-field(
clearable
:clear-icon='mdiClose'
@click:clear='() => change("fromHour")'
:label="$t('event.from')" :label="$t('event.from')"
:value="fromHour" :value="fromHour"
:disabled='!value.from' :disabled='!value.from'
@@ -52,7 +54,7 @@ v-col(cols=12)
v-on="on") v-on="on")
v-time-picker( v-time-picker(
v-if="menuFromHour" v-if="menuFromHour"
:value="fromHour" v-model="fromHour"
:allowedMinutes='allowedMinutes' :allowedMinutes='allowedMinutes'
format='24hr' format='24hr'
@click:minute='menuFromHour = false' @click:minute='menuFromHour = false'
@@ -68,6 +70,9 @@ v-col(cols=12)
transition="scale-transition") transition="scale-transition")
template(v-slot:activator="{ on, attrs }") template(v-slot:activator="{ on, attrs }")
v-text-field( v-text-field(
clearable
:clear-icon='mdiClose'
@click:clear='() => change("dueHour")'
:label="$t('event.due')" :label="$t('event.due')"
:value="dueHour" :value="dueHour"
:disabled='!fromHour' :disabled='!fromHour'
@@ -77,7 +82,7 @@ v-col(cols=12)
v-on="on") v-on="on")
v-time-picker( v-time-picker(
v-if="menuDueHour" v-if="menuDueHour"
:value="dueHour" v-model="dueHour"
:allowedMinutes='allowedMinutes' :allowedMinutes='allowedMinutes'
format='24hr' format='24hr'
@click:minute='menuDueHour = false' @click:minute='menuDueHour = false'
@@ -91,7 +96,7 @@ import dayjs from 'dayjs'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import List from '@/components/List' import List from '@/components/List'
import { attributesFromEvents } from '../assets/helper' import { attributesFromEvents } from '../assets/helper'
import { mdiClockTimeFourOutline, mdiClockTimeEightOutline } from '@mdi/js' import { mdiClockTimeFourOutline, mdiClockTimeEightOutline, mdiClose } from '@mdi/js'
export default { export default {
name: 'DateInput', name: 'DateInput',
@@ -101,13 +106,23 @@ export default {
event: { type: Object, default: () => null } event: { type: Object, default: () => null }
}, },
data() { data() {
let fromDate
if (this.value.from) {
if (this.value.multidate) {
fromDate = ({ start: dayjs(this.value.from).toDate(), end: dayjs(this.value.due).toDate() })
} else {
fromDate = new Date(this.value.from)
}
}
return { return {
mdiClockTimeFourOutline, mdiClockTimeEightOutline, mdiClockTimeFourOutline, mdiClockTimeEightOutline, mdiClose,
allowedMinutes: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55], allowedMinutes: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55],
menuFromHour: false, menuFromHour: false,
fromDate,
fromHour: this.value.from && dayjs.tz(this.value.from).format('HH:mm'),
dueHour: this.value.due && dayjs.tz(this.value.due).format('HH:mm'),
menuDueHour: false, menuDueHour: false,
type: 'normal', type: this.value.type || 'normal',
page: null,
events: [], events: [],
frequencies: [ frequencies: [
{ value: '1w', text: this.$t('event.each_week') }, { value: '1w', text: this.$t('event.each_week') },
@@ -119,26 +134,13 @@ export default {
computed: { computed: {
...mapState(['settings']), ...mapState(['settings']),
todayEvents() { todayEvents() {
const start = dayjs(this.value.from).startOf('day').unix() const start = dayjs.tz(this.value.from).startOf('day').unix()
const end = dayjs(this.value.from).endOf('day').unix() const end = dayjs.tz(this.value.from).endOf('day').unix()
return this.events.filter(e => e.start_datetime >= start && e.start_datetime <= end) return this.events.filter(e => e.start_datetime >= start && e.start_datetime <= end)
}, },
attributes() { attributes() {
return attributesFromEvents(this.events) return attributesFromEvents(this.events)
}, },
fromDate () {
if (this.value.multidate) {
return ({ start: dayjs(this.value.from).toDate(), end: dayjs(this.value.due).toDate() })
}
return this.value.from ? dayjs(this.value.from).toDate() : null
},
fromHour () {
return this.value.from && this.value.fromHour ? dayjs.tz(this.value.from).format('HH:mm') : null
},
dueHour () {
return this.value.due && this.value.dueHour ? dayjs.tz(this.value.due).format('HH:mm') : null
},
whenPatterns() { whenPatterns() {
if (!this.value.from) { return } if (!this.value.from) { return }
const date = dayjs(this.value.from) const date = dayjs(this.value.from)
@@ -225,52 +227,59 @@ export default {
} else if (what === 'recurrentType') { } else if (what === 'recurrentType') {
this.$emit('input', { ...this.value, recurrent: { ...this.value.recurrent, type: value } }) this.$emit('input', { ...this.value, recurrent: { ...this.value.recurrent, type: value } })
} else if (what === 'fromHour') { } else if (what === 'fromHour') {
if (value) { // if (value) {
const [hour, minute] = value.split(':') const [hour, minute] = value ? value.split(':') : [0, 0]
const from = dayjs.tz(this.value.from).hour(hour).minute(minute).second(0) let from = dayjs.tz(this.value.from).hour(hour).minute(minute).second(0).toDate()
this.$emit('input', { ...this.value, from, fromHour: true }) this.$emit('input', { ...this.value, from })
} else { if (!value) {
this.$emit('input', { ...this.value, fromHour: false }) this.fromHour = null
} }
// } else {
// this.$emit('input', { ...this.value })
// }
} else if (what === 'dueHour') { } else if (what === 'dueHour') {
if (value) { if (value) {
const [hour, minute] = value.split(':') const [hour, minute] = value.split(':')
const fromHour = dayjs.tz(this.value.from).hour() let due = dayjs.tz(this.value.due || this.value.from).hour(Number(hour)).minute(Number(minute)).second(0)
// add a day // add a day
let due = dayjs(this.value.from) if (dayjs(this.value.from).hour() > Number(hour) && !this.value.multidate) {
if (fromHour > Number(hour) && !this.value.multidate) {
due = due.add(1, 'day') due = due.add(1, 'day')
} }
due = due.hour(hour).minute(minute).second(0) due = due.hour(hour).minute(minute).second(0)
this.$emit('input', { ...this.value, due, dueHour: true }) this.$emit('input', { ...this.value, due: due.toDate() })
} else { } else {
this.$emit('input', { ...this.value, due: null, dueHour: false }) this.$emit('input', { ...this.value, due: null })
this.dueHour = null
} }
// change date in calendar (could be a range or a recurrent event...) // change date in calendar (could be a range or a recurrent event...)
} else if (what === 'date') { } else if (what === 'date') {
if (value === null) { if (value === null) {
this.$emit('input', { ...this.value, from: null, fromHour: false }) this.$emit('input', { ...this.value, from: null, due: null })
return return
} }
if (this.value.multidate) { if (this.value.multidate) {
let from = value.start let from = value.start
let due = value.end let due = value.end
if (this.value.fromHour) { if (this.fromHour) {
from = dayjs.tz(value.start).hour(dayjs.tz(this.value.from).hour()) const [hour, minute] = this.fromHour.split(':')
from = dayjs.tz(from).hour(hour).minute(minute).second(0).toDate()
} }
if (this.value.dueHour) { if (this.dueHour) {
due = dayjs.tz(value.end).hour(dayjs.tz(this.value.due).hour()) const [hour, minute] = this.dueHour.split(':')
due = dayjs.tz(due).hour(hour).minute(minute).second(0).toDate()
} }
this.$emit('input', { ...this.value, from, due }) this.$emit('input', { ...this.value, from, due })
} else { } else {
let from = value let from = value
let due = this.value.due let due = this.value.due
if (this.value.fromHour) { if (this.fromHour) {
from = dayjs.tz(value).hour(dayjs.tz(this.value.from).hour()) const [hour, minute] = this.fromHour.split(':')
from = dayjs.tz(value).hour(hour).minute(minute).second(0).toDate()
} }
if (this.value.dueHour && this.value.due) { if (this.dueHour) {
due = dayjs.tz(value).hour(dayjs.tz(this.value.due).hour()) const [hour, minute] = this.dueHour.split(':')
due = dayjs.tz(value).hour(hour).minute(minute).second(0).toDate()
} }
this.$emit('input', { ...this.value, from, due }) this.$emit('input', { ...this.value, from, due })
} }

View File

@@ -182,10 +182,6 @@ export default {
margin-top: 4px; margin-top: 4px;
padding-top: 12px; padding-top: 12px;
padding-bottom: 22px; padding-bottom: 22px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #FF4500 transparent;
scroll-behavior: smooth;
font-family: sans-serif; font-family: sans-serif;
font-size: 1.1em; font-size: 1.1em;
@@ -231,38 +227,19 @@ export default {
// position: absolute; // position: absolute;
} }
.focused .ProseMirror::after { // .focused .::after {
width: 100%; // width: 100%;
} // }
.ProseMirror {
padding: 15px;
outline: 0;
&::before {
bottom: 0px;
content: "";
left: 0;
position: absolute;
transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
width: 100%;
border-width: thin 0 0 0;
border-style: solid;
height: 0px;
border-color: rgba(255, 255, 255, 0.7);
}
&::after { .ProseMirror {
bottom: 0px; padding: 5px 15px 0px 15px;
content: ""; outline: 0;
left: 0; min-height: 100px;
position: absolute; max-height: 350px;
height: 0px; overflow-y: auto;
transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1); scrollbar-width: thin;
width: 100%; scrollbar-color: #FF4500 transparent;
border-width: 2px 0 0 0; overflow-y: auto;
border-style: solid;
border-color: #FF4500;
transform: scaleX(0);
}
} }
} }

View File

@@ -8,56 +8,23 @@ v-card.h-event.event.d-flex(itemscope itemtype="https://schema.org/Event")
v-card-text.body.pt-0.pb-0 v-card-text.body.pt-0.pb-0
time.dt-start.subtitle-1(:datetime='event.start_datetime | unixFormat("YYYY-MM-DD HH:mm")' itemprop="startDate" :content="event.start_datetime | unixFormat('YYYY-MM-DDTHH:mm')") <v-icon v-text='mdiCalendar'></v-icon> {{ event | when }} time.dt-start.subtitle-1(:datetime='event.start_datetime | unixFormat("YYYY-MM-DD HH:mm")' itemprop="startDate" :content="event.start_datetime | unixFormat('YYYY-MM-DDTHH:mm')") <v-icon v-text='mdiCalendar'></v-icon> {{ event | when }}
.d-none.dt-end(itemprop="endDate" :content="event.end_datetime | unixFormat('YYYY-MM-DDTHH:mm')") {{ event.end_datetime | unixFormat('YYYY-MM-DD HH:mm') }} .d-none.dt-end(itemprop="endDate" :content="event.end_datetime | unixFormat('YYYY-MM-DDTHH:mm')") {{ event.end_datetime | unixFormat('YYYY-MM-DD HH:mm') }}
nuxt-link.place.d-block.p-location.pl-0(text color='primary' :to='`/place/${event.place.name}`' itemprop="location" :content="event.place.name") <v-icon v-text='mdiMapMarker'></v-icon> {{event.place.name}} nuxt-link.place.d-block.p-location.pl-0(text color='primary' :to='`/place/${event.place.name}`' itemprop="location" itemscope itemtype="https://schema.org/Place") <v-icon v-text='mdiMapMarker'></v-icon> <span itemprop='name'>{{ event.place.name }}</span>
.d-none(itemprop='location.address') {{event.place.address}} .d-none(itemprop='address') {{ event.place.address }}
v-card-actions.pt-0.actions.justify-space-between v-card-actions.pt-0.actions.justify-space-between
.tags .tags
v-chip.ml-1.mt-1(v-for='tag in event.tags.slice(0, 6)' small :to='`/tag/${tag}`' v-chip.ml-1.mt-1(v-for='tag in event.tags.slice(0, 6)' small :to='`/tag/${tag}`'
:key='tag' outlined color='primary') {{ tag }} :key='tag' outlined color='primary') {{ tag }}
client-only
v-menu(offset-y eager)
template(v-slot:activator="{on}")
v-btn.align-self-end(icon v-on='on' color='primary' title='more' aria-label='more')
v-icon(v-text='mdiDotsVertical')
v-list(dense)
v-list-item-group
v-list-item(@click='clipboard(`${settings.baseurl}/event/${event.slug || event.id}`)')
v-list-item-icon
v-icon(v-text='mdiContentCopy')
v-list-item-content
v-list-item-title {{$t('common.copy_link')}}
v-list-item(:href='`/api/event/${event.slug || event.id}.ics`')
v-list-item-icon
v-icon(v-text='mdiCalendarExport')
v-list-item-content
v-list-item-title {{$t('common.add_to_calendar')}}
v-list-item(v-if='is_mine' :to='`/add/${event.id}`')
v-list-item-icon
v-icon(v-text='mdiPencil')
v-list-item-content
v-list-item-title {{$t('common.edit')}}
v-list-item(v-if='is_mine' @click='remove(false)')
v-list-item-icon
v-icon(color='error' v-text='mdiDeleteForever')
v-list-item-content
v-list-item-title {{$t('common.remove')}}
template(#placeholder)
v-btn.align-self-end(icon color='primary' aria-label='more')
v-icon(v-text='mdiDotsVertical')
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState } from 'vuex'
import clipboard from '../assets/clipboard'
import MyPicture from '~/components/MyPicture' import MyPicture from '~/components/MyPicture'
import { mdiRepeat, mdiPencil, mdiDotsVertical, mdiContentCopy, import { mdiRepeat, mdiCalendar, mdiMapMarker } from '@mdi/js'
mdiCalendarExport, mdiDeleteForever, mdiCalendar, mdiMapMarker } from '@mdi/js'
export default { export default {
data() { data() {
return { mdiRepeat, mdiPencil, mdiDotsVertical, mdiContentCopy, mdiCalendarExport, return { mdiRepeat, mdiMapMarker, mdiCalendar }
mdiDeleteForever, mdiMapMarker, mdiCalendar }
}, },
components: { components: {
MyPicture MyPicture
@@ -66,27 +33,6 @@ export default {
event: { type: Object, default: () => ({}) }, event: { type: Object, default: () => ({}) },
lazy: Boolean lazy: Boolean
}, },
mixins: [clipboard], computed: mapState(['settings'])
computed: {
...mapState(['settings']),
is_mine () {
if (!this.$auth.user) {
return false
}
return (
this.event.userId === this.$auth.user.id || this.$auth.user.is_admin
)
}
},
methods: {
async remove () {
const ret = await this.$root.$confirm('event.remove_confirmation')
if (!ret) { return }
await this.$axios.delete(`/event/${this.event.id}`)
this.$emit('destroy', this.event.id)
this.$root.$message('admin.event_remove_ok')
}
}
} }
</script> </script>

View File

@@ -14,8 +14,8 @@ v-container
border no-save max-height='400px' :placeholder="$t('common.description')") border no-save max-height='400px' :placeholder="$t('common.description')")
v-card-actions v-card-actions
v-spacer v-spacer
v-btn(@click='dialog=false' color='error') {{$t('common.cancel')}} v-btn(@click='dialog = false' color='error' outlined) {{ $t('common.cancel') }}
v-btn(@click='save' color='primary' :disabled='!valid || loading' :loading='loading') {{$t(`common.${editing?'save':'send'}`)}} v-btn(@click='save' color='primary' :disabled='!valid || loading' :loading='loading' outlined) {{ $t(`common.${editing ? 'save' : 'send'}`) }}
v-btn(@click='openDialog' text color='primary') <v-icon v-text='mdiPlus'></v-icon> {{ $t('common.add') }} v-btn(@click='openDialog' text color='primary') <v-icon v-text='mdiPlus'></v-icon> {{ $t('common.add') }}
v-card-text v-card-text

View File

@@ -11,7 +11,7 @@ v-container
v-btn(color='primary' text @click='newCollection') <v-icon v-text='mdiPlus'></v-icon> {{ $t('admin.new_collection') }} v-btn(color='primary' text @click='newCollection') <v-icon v-text='mdiPlus'></v-icon> {{ $t('admin.new_collection') }}
v-dialog(v-model='dialog' width='800' destroy-on-close :fullscreen='$vuetify.breakpoint.xsOnly') v-dialog(v-model='dialog' width='800' destroy-on-close :fullscreen='$vuetify.breakpoint.xsOnly')
v-card(color='secondary') v-card
v-card-title {{ $t('admin.edit_collection') }} v-card-title {{ $t('admin.edit_collection') }}
v-card-text v-card-text
v-form(v-model='valid' ref='form' @submit.prevent.native='saveCollection') v-form(v-model='valid' ref='form' @submit.prevent.native='saveCollection')
@@ -87,7 +87,7 @@ v-container
v-card-actions v-card-actions
v-spacer v-spacer
v-btn(text @click='dialog=false' color='warning') {{$t('common.close')}} v-btn(@click='dialog = false' outlined color='warning') {{ $t('common.close') }}
v-card-text v-card-text
v-data-table( v-data-table(

View File

@@ -9,7 +9,7 @@ v-container
v-card-subtitle(v-html="$t('admin.place_description')") v-card-subtitle(v-html="$t('admin.place_description')")
v-dialog(v-model='dialog' width='600' :fullscreen='$vuetify.breakpoint.xsOnly') v-dialog(v-model='dialog' width='600' :fullscreen='$vuetify.breakpoint.xsOnly')
v-card(color='secondary') v-card
v-card-title {{ $t('admin.edit_place') }} v-card-title {{ $t('admin.edit_place') }}
v-card-text v-card-text
v-form(v-model='valid' ref='form' lazy-validation) v-form(v-model='valid' ref='form' lazy-validation)
@@ -27,8 +27,8 @@ v-container
v-card-actions v-card-actions
v-spacer v-spacer
v-btn(@click='dialog=false' color='warning') {{$t('common.cancel')}} v-btn(@click='dialog = false' outlined color='warning') {{ $t('common.cancel') }}
v-btn(@click='savePlace' color='primary' :loading='loading' v-btn(@click='savePlace' color='primary' outlined :loading='loading'
:disable='!valid || loading') {{ $t('common.save') }} :disable='!valid || loading') {{ $t('common.save') }}
v-card-text v-card-text

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,40 @@ nav_order: 10
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
### 1.5.0 - UNRELEASED ### 1.5.6 - 22 set '22
- update linkifyjs, sequelizem, nuxt deps
- improve homepage loading time
- restore removed icons
### 1.5.5 - 21 set '22
- fix #185 - wrong res.download api usage
- fix some dialog background on light theme
- update sequelize and remove live patch
- improve events filtering on selected day
- allow tags complete removals
- improve homepage performance
- docs: add scheme to nginx proxy configuration
### 1.5.4 - 6 set '22
- Update webcomponent deps
- Refactor datime display in webcomponent
- Force flyer download
- Restore range events on calendar
- Fix limit/max events for mariadb #183
- Fix endtime selection
- Fix microdata address
### 1.5.3 - 30 aug '22
- Fix end time selection when it's in the next day
### 1.5.2 - 26 aug '22
- fix Editor background color
### 1.5.1 - 14 aug '22
- fix regression with hidden recurrent events
### 1.5.0 - 8 aug '22
##### :warning: **BREAKING CHANGES**: ##### :warning: **BREAKING CHANGES**:
- supported nodejs version >=14 <=16 (nodejs 12 reached End-of-Life on 30 April 2022) - supported nodejs version >=14 <=16 (nodejs 12 reached End-of-Life on 30 April 2022)
- minimum mariadb supported version >= 10.5.2 - minimum mariadb supported version >= 10.5.2

View File

@@ -31,11 +31,6 @@ DB=sqlite
wget {{site.url}}/docker/$DB/docker-compose.yml wget {{site.url}}/docker/$DB/docker-compose.yml
``` ```
Build docker image
```bash
docker-compose build
```
## Start gancio ## Start gancio
Start your container: Start your container:
@@ -60,6 +55,5 @@ You'll need to [setup nginx as a proxy]({% link install/nginx.md %}) then you ca
```bash ```bash
cd /opt/gancio # or where your installation is cd /opt/gancio # or where your installation is
wget https://gancio.org/docker/Dockerfile -O Dockerfile docker-compose pull && docker-compose restart
docker-compose up -d --no-deps --build
``` ```

View File

@@ -29,6 +29,8 @@ server {
location @proxy { location @proxy {
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:13120; proxy_pass http://127.0.0.1:13120;
} }
} }

View File

@@ -19,8 +19,8 @@
"activate_user": "Bestätigt", "activate_user": "Bestätigt",
"password_updated": "Passwort geändert.", "password_updated": "Passwort geändert.",
"me": "Du", "me": "Du",
"disable": "Deaktiviere", "disable": "Ausschalten",
"enable": "Aktiviere", "enable": "Einschalten",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"ok": "OK", "ok": "OK",
"new_user": "Neue nutzende Person", "new_user": "Neue nutzende Person",
@@ -118,7 +118,7 @@
"new_collection": "Bubble anlegen", "new_collection": "Bubble anlegen",
"edit_collection": "Bubble bearbeiten", "edit_collection": "Bubble bearbeiten",
"wrong_domain_warning": "Die \"baseurl\" die in config.json konfiguriert ist <b>({baseurl})</b> unterscheidet sich von derjenigen <b>({url})</b> die du besuchst", "wrong_domain_warning": "Die \"baseurl\" die in config.json konfiguriert ist <b>({baseurl})</b> unterscheidet sich von derjenigen <b>({url})</b> die du besuchst",
"instance_place_help": "Diese Textzeile wird im Menü der anderen freundlichen Instanzen angezeigt", "instance_place_help": "Diese Textzeile wird im Menü der anderen befreundeten Instanzen angezeigt",
"place_description": "Falls ein Ort falsch ist oder sich die Adresse ändert, kannst du ihn ändern.<br/>Bitte beachte, dass alle Veranstaltungen, die mit diesem Ort verbunden sind, die Adresse ändern (auch zurückliegende).", "place_description": "Falls ein Ort falsch ist oder sich die Adresse ändert, kannst du ihn ändern.<br/>Bitte beachte, dass alle Veranstaltungen, die mit diesem Ort verbunden sind, die Adresse ändern (auch zurückliegende).",
"enable_admin_user_confirm": "Achte darauf, dass du der nutzenden Person {user} Admin-Rechte hinzufügst", "enable_admin_user_confirm": "Achte darauf, dass du der nutzenden Person {user} Admin-Rechte hinzufügst",
"trusted_instances_help": "Befreundete Instanzen werden in der Navigationsleiste oben auf der Seite angezeigt", "trusted_instances_help": "Befreundete Instanzen werden in der Navigationsleiste oben auf der Seite angezeigt",
@@ -148,7 +148,7 @@
"announcement_remove_ok": "Hinweis entfernt", "announcement_remove_ok": "Hinweis entfernt",
"instance_block_confirm": "Bestätige, dass du die Instanz {instance} blockieren möchtest?", "instance_block_confirm": "Bestätige, dass du die Instanz {instance} blockieren möchtest?",
"favicon": "Logo", "favicon": "Logo",
"title_description": "Es wird im Seitentitel, in der Betreffzeile von E-Mails, beim Export von RSS-Feeds und ICS verwendet.", "title_description": "Dieses wird im Seitentitel, in der Betreffzeile von E-Mails, beim Export von RSS-Feeds und ICS angezeigt.",
"description_description": "Erscheint in der Kopfzeile neben dem Titel", "description_description": "Erscheint in der Kopfzeile neben dem Titel",
"allow_recurrent_event": "Wiederkehrende Veranstaltungen erlauben", "allow_recurrent_event": "Wiederkehrende Veranstaltungen erlauben",
"recurrent_event_visible": "Wiederkehrende Veranstaltungen sind standardmäßig sichtbar", "recurrent_event_visible": "Wiederkehrende Veranstaltungen sind standardmäßig sichtbar",
@@ -156,7 +156,7 @@
"allow_anon_event": "Kann man auch anonyme Veranstaltungen (vorausgesetzt, diese werden genehmigt) eintragen?", "allow_anon_event": "Kann man auch anonyme Veranstaltungen (vorausgesetzt, diese werden genehmigt) eintragen?",
"allow_registration_description": "Möchtest du die Registrierung aktivieren?", "allow_registration_description": "Möchtest du die Registrierung aktivieren?",
"federation": "Föderation / ActivityPub", "federation": "Föderation / ActivityPub",
"enable_federation_help": "Es wird möglich sein, diese Instanz vom Fediverse aus zu verfolgen", "enable_federation_help": "Bei Aktivierung kann diese Instanz vom Fediverse aus verfolgt werden",
"add_instance": "Instanz hinzufügen", "add_instance": "Instanz hinzufügen",
"block": "Sperre", "block": "Sperre",
"show_resource": "Ressource anzeigen", "show_resource": "Ressource anzeigen",
@@ -278,7 +278,7 @@
"login": { "login": {
"ok": "Angemeldet", "ok": "Angemeldet",
"insert_email": "Gib deine E-Mail-Adresse ein", "insert_email": "Gib deine E-Mail-Adresse ein",
"error": "Eine Anmeldung ist nicht möglich. Bitte überprüfe deine Informationen zur Anmeldung.", "error": "Anmeldung nicht möglich. Bitte überprüfe deine Informationen zur Anmeldung.",
"forgot_password": "Passwort vergessen?", "forgot_password": "Passwort vergessen?",
"not_registered": "Nicht registriert?", "not_registered": "Nicht registriert?",
"check_email": "Überprüfe deinen E-Mail-Posteingang und Spam.", "check_email": "Überprüfe deinen E-Mail-Posteingang und Spam.",
@@ -291,5 +291,5 @@
"copy_password_dialog": "Ja, du musst das Passwort kopieren!", "copy_password_dialog": "Ja, du musst das Passwort kopieren!",
"start": "Start" "start": "Start"
}, },
"about": "\n <p><a href='https://gancio.org'>Gancio</a> ist eine gemeinsamer Veranstaltungsalender für lokale und regionale Gemeinschaften.</p>\n " "about": "\n <p><a href='https://gancio.org'>Gancio</a> ist ein gemeinsamer Veranstaltungsalender für lokale und regionale Gemeinschaften.</p>\n "
} }

1
locales/email/fi.json Normal file
View File

@@ -0,0 +1 @@
{}

1
locales/email/nl.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -88,7 +88,8 @@
"theme": "Itxura", "theme": "Itxura",
"tags": "Etiketak", "tags": "Etiketak",
"close": "Itxi", "close": "Itxi",
"blobs": "Mordoak" "blobs": "Mordoak",
"collections": "Bildumak"
}, },
"login": { "login": {
"description": "Saioa hasten baduzu ekitaldi berriak sortu ahal izango dituzu.", "description": "Saioa hasten baduzu ekitaldi berriak sortu ahal izango dituzu.",
@@ -129,8 +130,8 @@
"where_description": "Non da ekitaldia? Agertzen ez bada zuk sor dezakezu.", "where_description": "Non da ekitaldia? Agertzen ez bada zuk sor dezakezu.",
"confirmed": "Ekitaldia egiaztatu da", "confirmed": "Ekitaldia egiaztatu da",
"not_found": "Ezin da ekitaldia aurkitu", "not_found": "Ezin da ekitaldia aurkitu",
"remove_confirmation": "Ziur zaude ekitaldi hau ezabatu nahi duzula?", "remove_confirmation": "Ziur al zaude ekitaldi hau ezabatu nahi duzula?",
"remove_recurrent_confirmation": "Ziur zaude ekitaldi errepikari hau ezabatu nahi duzula?\nIragandako ekitaldiak mantenduko dira, baina ez da ekitaldi berririk sortuko.", "remove_recurrent_confirmation": "Ziur al zaude ekitaldi errepikari hau ezabatu nahi duzula?\nIragandako ekitaldiak mantenduko dira, baina ez da ekitaldi berririk sortuko.",
"recurrent": "Errepikaria", "recurrent": "Errepikaria",
"show_recurrent": "ekitaldi errepikariak", "show_recurrent": "ekitaldi errepikariak",
"show_past": "iragandako ekitaldiak ere", "show_past": "iragandako ekitaldiak ere",
@@ -168,14 +169,14 @@
"download_flyer": "Deskargatu eskuorria" "download_flyer": "Deskargatu eskuorria"
}, },
"admin": { "admin": {
"place_description": "Helbidea oker badago, alda dezakezu.<br/>Leku honekin lotutako iraganeko eta etorkizuneko ekitaldien helbidea aldatuko da.", "place_description": "Lekua edo helbidea oker badago, alda dezakezu.<br/>Leku honekin lotutako iraganeko eta etorkizuneko ekitaldien helbidea aldatuko da.",
"event_confirm_description": "Erabiltzaile anonimoek sortutako ekitaldiak hemen baieztatu ditzakezu", "event_confirm_description": "Erabiltzaile anonimoek sortutako ekitaldiak hemen baieztatu ditzakezu",
"delete_user": "Ezabatu", "delete_user": "Ezabatu",
"remove_admin": "Kendu administratzaile baimena", "remove_admin": "Kendu administratzaile baimena",
"delete_user_confirm": "Ziur al zaude {user} ezabatu nahi duzula?", "delete_user_confirm": "Ziur al zaude {user} ezabatu nahi duzula?",
"user_remove_ok": "Erabiltzailea ezabatu da", "user_remove_ok": "Erabiltzailea ezabatu da",
"user_create_ok": "Erabiltzailea sortu da", "user_create_ok": "Erabiltzailea sortu da",
"allow_registration_description": "Izen-emateak ahalbidetu nahi dituzu?", "allow_registration_description": "Izen-emateak ahalbidetu?",
"allow_anon_event": "Anonimoek ekitaldiak sortzea ahalbidetu (baieztatu ondoren)?", "allow_anon_event": "Anonimoek ekitaldiak sortzea ahalbidetu (baieztatu ondoren)?",
"allow_recurrent_event": "Ekitaldi errepikariak ahalbidetu", "allow_recurrent_event": "Ekitaldi errepikariak ahalbidetu",
"recurrent_event_visible": "Erakutsi ekitaldi errepikariak modu lehenetsian", "recurrent_event_visible": "Erakutsi ekitaldi errepikariak modu lehenetsian",
@@ -195,14 +196,14 @@
"hide_resource": "Ezkutatu baliabidea", "hide_resource": "Ezkutatu baliabidea",
"show_resource": "Erakutsi baliabidea", "show_resource": "Erakutsi baliabidea",
"delete_resource": "Ezabatu baliabidea", "delete_resource": "Ezabatu baliabidea",
"delete_resource_confirm": "Ziur zaude baliabide hau ezabatu nahi duzula?", "delete_resource_confirm": "Ziur al zaude baliabide hau ezabatu nahi duzula?",
"block_user": "Blokeatu erabiltzailea", "block_user": "Blokeatu erabiltzailea",
"user_blocked": "{user} erabiltzailea blokeatuta dago", "user_blocked": "{user} erabiltzailea blokeatuta dago",
"filter_instances": "Iragazi instantziak", "filter_instances": "Iragazi instantziak",
"filter_users": "Iragazi erabiltzaileak", "filter_users": "Iragazi erabiltzaileak",
"instance_name": "Instantziaren izena", "instance_name": "Instantziaren izena",
"favicon": "Logoa", "favicon": "Logoa",
"user_block_confirm": "Ziur al zaude {user} blokeatu nahi duzula?", "user_block_confirm": "Ziur al zaude {user} erabiltzailea blokeatu nahi duzula?",
"delete_announcement_confirm": "Ziur al zaude iragarpena ezabatu nahi duzula?", "delete_announcement_confirm": "Ziur al zaude iragarpena ezabatu nahi duzula?",
"announcement_remove_ok": "Iragarpena ezabatu da", "announcement_remove_ok": "Iragarpena ezabatu da",
"announcement_description": "Atal honetan iragarpenak txertatu ditzakezu hasiera-orrialdean ager daitezen", "announcement_description": "Atal honetan iragarpenak txertatu ditzakezu hasiera-orrialdean ager daitezen",
@@ -225,7 +226,7 @@
"is_dark": "Itxura iluna", "is_dark": "Itxura iluna",
"instance_block_confirm": "Ziur al zaude {instance} instantzia blokeatu nahi duzula?", "instance_block_confirm": "Ziur al zaude {instance} instantzia blokeatu nahi duzula?",
"add_instance": "Gehitu instantzia", "add_instance": "Gehitu instantzia",
"disable_user_confirm": "Ziur zaude {user} deskonektatu nahi duzula?", "disable_user_confirm": "Ziur al zaude {user} desgaitu nahi duzula?",
"show_smtp_setup": "Eposta ezarpenak", "show_smtp_setup": "Eposta ezarpenak",
"smtp_test_button": "Bidali probako eposta bat", "smtp_test_button": "Bidali probako eposta bat",
"smtp_test_success": "Probako eposta bidali da {admin_email}-(e)ra, begiratu zure sarrera-ontzia", "smtp_test_success": "Probako eposta bidali da {admin_email}-(e)ra, begiratu zure sarrera-ontzia",
@@ -237,7 +238,16 @@
"wrong_domain_warning": "config.json-en konfiguratuta dagoen baseurl <b>({baseurl})</b> ez da bisitatzen ari zaren berbera <b>({url})</b>", "wrong_domain_warning": "config.json-en konfiguratuta dagoen baseurl <b>({baseurl})</b> ez da bisitatzen ari zaren berbera <b>({url})</b>",
"new_blob": "Mordo berria", "new_blob": "Mordo berria",
"blobs_description": "Mordoak etiketen eta lekuen arabera multzokatutako ekitaldiak dira. Hasiera-orrialdean bistaratzen dira", "blobs_description": "Mordoak etiketen eta lekuen arabera multzokatutako ekitaldiak dira. Hasiera-orrialdean bistaratzen dira",
"edit_blob": "Editatu mordoa" "edit_blob": "Editatu mordoa",
"smtp_port": "SMTP ataka",
"smtp_secure": "SMTP segurua (TLS edo STARTTLS)",
"smtp_use_sendmail": "Erabili sendmail",
"sender_email": "Igorlearen eposta",
"edit_collection": "Editatu bilduma",
"disable_admin_user_confirm": "Ziur al zaude {user}(r)i administratzaile baimenak kendu nahi dizkiozula?",
"collections_description": "Bildumak, etiketen eta lekuen arabera multzokatutako ekitaldiak dira. Hasierako orrialdean bistaratuko dira",
"enable_admin_user_confirm": "Ziur zaude {user}-(r)i administratzaile baimenak gehitu nahi dizkiozula",
"new_collection": "Bilduma berria"
}, },
"auth": { "auth": {
"not_confirmed": "Oraindik baieztatu gabe dago…", "not_confirmed": "Oraindik baieztatu gabe dago…",

1
locales/fi.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -1,6 +1,6 @@
{ {
"common": { "common": {
"add_event": "Nuovo evento", "add_event": "Aggiungi evento",
"next": "Continua", "next": "Continua",
"export": "Esporta", "export": "Esporta",
"send": "Invia", "send": "Invia",

1
locales/nl.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -134,6 +134,18 @@ module.exports = {
defaultAssets: false defaultAssets: false
}, },
build: { build: {
extend(config, { isDev, isClient }) {
// ..
config.module.rules.push({
test: /\.mjs$/,
include: /node_modules/,
type: "javascript/auto"
})
// Sets webpack's mode to development if `isDev` is true.
if (isDev) {
config.mode = 'development'
}
},
corejs: 3, corejs: 3,
cache: true, cache: true,
hardSource: !isDev, hardSource: !isDev,

View File

@@ -1,6 +1,6 @@
{ {
"name": "gancio", "name": "gancio",
"version": "1.5.0", "version": "1.5.6",
"description": "A shared agenda for local communities", "description": "A shared agenda for local communities",
"author": "lesion", "author": "lesion",
"scripts": { "scripts": {
@@ -43,19 +43,19 @@
"body-parser": "^1.20.0", "body-parser": "^1.20.0",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "^2.8.5", "cors": "^2.8.5",
"dayjs": "^1.11.4", "dayjs": "^1.11.5",
"dompurify": "^2.3.10", "dompurify": "^2.3.10",
"email-templates": "^8.0.9", "email-templates": "^10.0.1",
"express": "^4.18.1", "express": "^4.18.1",
"express-oauth-server": "lesion/express-oauth-server#master", "express-oauth-server": "lesion/express-oauth-server#master",
"http-signature": "^1.3.6", "http-signature": "^1.3.6",
"https-proxy-agent": "^5.0.1", "https-proxy-agent": "^5.0.1",
"ical.js": "^1.5.0", "ical.js": "^1.5.0",
"ics": "^2.37.0", "ics": "^2.40.0",
"jsdom": "^20.0.0", "jsdom": "^20.0.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"linkify-html": "^3.0.4", "linkify-html": "^4.0.0",
"linkifyjs": "3.0.5", "linkifyjs": "4.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mariadb": "^3.0.1", "mariadb": "^3.0.1",
"microformat-node": "^2.0.1", "microformat-node": "^2.0.1",
@@ -63,27 +63,25 @@
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"nuxt-edge": "2.16.0-27616340.013f051b", "nuxt-edge": "2.16.0-27720022.54e852f",
"pg": "^8.6.0", "pg": "^8.8.0",
"sequelize": "^6.21.3", "sequelize": "^6.23.0",
"sequelize-slugify": "^1.6.1", "sequelize-slugify": "^1.6.2",
"sharp": "^0.27.2", "sharp": "^0.27.2",
"sqlite3": "^5.0.11", "sqlite3": "^5.0.11",
"tiptap": "^1.32.0", "tiptap": "^1.32.0",
"tiptap-extensions": "^1.35.0", "tiptap-extensions": "^1.35.0",
"umzug": "^2.3.0", "umzug": "^2.3.0",
"v-calendar": "^2.4.1", "v-calendar": "^2.4.1",
"vue": "2.7.8",
"vue-i18n": "^8.26.7", "vue-i18n": "^8.26.7",
"vue-template-compiler": "2.7.8", "vuetify": "2.6.10",
"vuetify": "2.6.8", "winston": "^3.8.2",
"winston": "^3.8.1",
"winston-daily-rotate-file": "^4.7.1", "winston-daily-rotate-file": "^4.7.1",
"yargs": "^17.5.0" "yargs": "^17.5.0"
}, },
"devDependencies": { "devDependencies": {
"@nuxtjs/vuetify": "^1.12.3", "@nuxtjs/vuetify": "^1.12.3",
"jest": "^28.1.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"pug": "^3.0.2", "pug": "^3.0.2",
"pug-plain-loader": "^1.1.0", "pug-plain-loader": "^1.1.0",

View File

@@ -217,7 +217,7 @@ export default {
formData.append('description', this.event.description) formData.append('description', this.event.description)
formData.append('multidate', !!this.date.multidate) formData.append('multidate', !!this.date.multidate)
formData.append('start_datetime', dayjs(this.date.from).unix()) formData.append('start_datetime', dayjs(this.date.from).unix())
formData.append('end_datetime', this.date.due ? dayjs(this.date.due).unix() : this.date.from.add(2, 'hour').unix()) formData.append('end_datetime', this.date.due ? dayjs(this.date.due).unix() : dayjs(this.date.from).add(2, 'hour').unix())
if (this.edit) { if (this.edit) {
formData.append('id', this.event.id) formData.append('id', this.event.id)

View File

@@ -49,7 +49,7 @@ v-container#event.pa-0.pa-sm-2
:href='`/api/event/${event.slug || event.id}.ics`') :href='`/api/event/${event.slug || event.id}.ics`')
v-icon(v-text='mdiCalendarExport') v-icon(v-text='mdiCalendarExport')
v-btn.ml-2(v-if='hasMedia' large icon :title="$t('event.download_flyer')" color='primary' :aria-label="$t('event.download_flyer')" v-btn.ml-2(v-if='hasMedia' large icon :title="$t('event.download_flyer')" color='primary' :aria-label="$t('event.download_flyer')"
:href='event | mediaURL') :href='event | mediaURL("download")')
v-icon(v-text='mdiFileDownloadOutline') v-icon(v-text='mdiFileDownloadOutline')
.p-description.text-body-1.pa-3.rounded(v-if='hasMedia && event.description' itemprop='description' v-html='event.description') .p-description.text-body-1.pa-3.rounded(v-if='hasMedia && event.description' itemprop='description' v-html='event.description')

View File

@@ -113,7 +113,7 @@ export default {
theme: $store.state.settings['theme.is_dark'] ? 'dark' : 'light', theme: $store.state.settings['theme.is_dark'] ? 'dark' : 'light',
sidebar: 'true' sidebar: 'true'
}, },
filters: { tags: [], places: [], show_recurrent: false }, filters: { tags: [], places: [], show_recurrent: $store.state.settings.recurrent_event_visible },
events: [] events: []
} }
}, },

View File

@@ -16,7 +16,17 @@ v-container.pa-0
.col.pt-0.pt-md-2.mt-4.ma-md-0.pb-0 .col.pt-0.pt-md-2.mt-4.ma-md-0.pb-0
//- v-btn(to='/search' color='primary' ) {{$t('common.search')}} //- v-btn(to='/search' color='primary' ) {{$t('common.search')}}
v-form(to='/search' action='/search' method='GET') v-form(to='/search' action='/search' method='GET')
v-col(cols=12)
v-switch(
v-if='settings.allow_recurrent_event'
v-model='show_recurrent'
inset color='primary'
hide-details
:label="$t('event.show_recurrent')")
v-col.mb-4(cols=12)
v-text-field(name='search' :label='$t("common.search")' outlined rounded hide-details :append-icon='mdiMagnify') v-text-field(name='search' :label='$t("common.search")' outlined rounded hide-details :append-icon='mdiMagnify')
v-chip(v-if='selectedDay' close :close-icon='mdiCloseCircle' @click:close='dayChange()') {{selectedDay}}
//- Events //- Events
#events.mb-2.mt-1.pl-1.pl-sm-2 #events.mb-2.mt-1.pl-1.pl-sm-2
@@ -29,7 +39,7 @@ import dayjs from 'dayjs'
import Event from '@/components/Event' import Event from '@/components/Event'
import Announcement from '@/components/Announcement' import Announcement from '@/components/Announcement'
import Calendar from '@/components/Calendar' import Calendar from '@/components/Calendar'
import { mdiMagnify } from '@mdi/js' import { mdiMagnify, mdiCloseCircle } from '@mdi/js'
export default { export default {
name: 'Index', name: 'Index',
@@ -43,9 +53,9 @@ export default {
}) })
return { events } return { events }
}, },
data () { data ({ $store }) {
return { return {
mdiMagnify, mdiMagnify, mdiCloseCircle,
first: true, first: true,
isCurrentMonth: true, isCurrentMonth: true,
now: dayjs().unix(), now: dayjs().unix(),
@@ -53,7 +63,8 @@ export default {
events: [], events: [],
start: dayjs().startOf('month').unix(), start: dayjs().startOf('month').unix(),
end: null, end: null,
selectedDay: null selectedDay: null,
show_recurrent: $store.state.settings.recurrent_event_visible
} }
}, },
head () { head () {
@@ -80,11 +91,11 @@ export default {
if (this.selectedDay) { if (this.selectedDay) {
const min = dayjs.tz(this.selectedDay).startOf('day').unix() const min = dayjs.tz(this.selectedDay).startOf('day').unix()
const max = dayjs.tz(this.selectedDay).endOf('day').unix() const max = dayjs.tz(this.selectedDay).endOf('day').unix()
return this.events.filter(e => (e.start_datetime <= max && e.start_datetime >= min)) return this.events.filter(e => (e.start_datetime <= max && (e.end_datetime || e.start_datetime) >= min) && (this.show_recurrent || !e.parentId))
} else if (this.isCurrentMonth) { } else if (this.isCurrentMonth) {
return this.events.filter(e => e.end_datetime ? e.end_datetime > now : e.start_datetime + 2 * 60 * 60 > now) return this.events.filter(e => ((e.end_datetime ? e.end_datetime > now : e.start_datetime + 2 * 60 * 60 > now) && (this.show_recurrent || !e.parentId)))
} else { } else {
return this.events return this.events.filter(e => this.show_recurrent || !e.parentId)
} }
} }
}, },

View File

@@ -1,6 +1,8 @@
<template> <template>
<v-container class='px-0' fluid> <v-container class='px-0' fluid>
<h1 class='d-block text-h4 font-weight-black text-center text-uppercase mt-10 mx-auto w-100 text-underline'><u>{{place.name}}</u></h1> <h1 class='d-block text-h4 font-weight-black text-center text-uppercase mt-10 mx-auto w-100 text-underline'>
<u>{{ place.name }}</u>
</h1>
<span class="d-block text-subtitle text-center w-100 mb-14">{{ place.address }}</span> <span class="d-block text-subtitle text-center w-100 mb-14">{{ place.address }}</span>
<!-- Events --> <!-- Events -->

View File

@@ -42,14 +42,15 @@ export default ({ app, store }) => {
// shown in mobile homepage // shown in mobile homepage
Vue.filter('day', value => dayjs.unix(value).tz().locale(store.state.locale).format('dddd, D MMM')) Vue.filter('day', value => dayjs.unix(value).tz().locale(store.state.locale).format('dddd, D MMM'))
Vue.filter('mediaURL', (event, type, format = '.jpg') => { Vue.filter('mediaURL', (event, type, format = '.jpg') => {
const mediaPath = type === 'download' ? '/download/' : '/media/'
if (event.media && event.media.length) { if (event.media && event.media.length) {
if (type === 'alt') { if (type === 'alt') {
return event.media[0].name return event.media[0].name
} else { } else {
return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + event.media[0].url.replace(/.jpg$/, format) return store.state.settings.baseurl + mediaPath + (type === 'thumb' ? 'thumb/' : '') + event.media[0].url.replace(/.jpg$/, format)
} }
} else if (type !== 'alt') { } else if (type !== 'alt') {
return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + 'logo.svg' return store.state.settings.baseurl + mediaPath + (type === 'thumb' ? 'thumb/' : '') + 'logo.svg'
} }
return '' return ''
}) })
@@ -78,27 +79,12 @@ export default ({ app, store }) => {
const start = dayjs.unix(event.start_datetime).tz() const start = dayjs.unix(event.start_datetime).tz()
const end = dayjs.unix(event.end_datetime).tz() const end = dayjs.unix(event.end_datetime).tz()
// const normal = `${start.format('dddd, D MMMM (HH:mm-')}${end.format('HH:mm) ')}`
// // recurrent event
// if (event.parent && where !== 'home') {
// const { frequency, days, type } = event.parent.recurrent
// if (frequency === '1w' || frequency === '2w') {
// const recurrent = app.i18n.tc(`event.recurrent_${frequency}_days`, days.length, { days: days.map(d => dayjs().day(d - 1).format('dddd')) })
// return `${normal} - ${recurrent}`
// } else if (frequency === '1m' || frequency === '2m') {
// const d = type === 'ordinal' ? days : days.map(d => dayjs().day(d - 1).format('dddd'))
// const recurrent = app.i18n.tc(`event.recurrent_${frequency}_${type}`, days.length, { days: d })
// return `${normal} - ${recurrent}`
// }
// return 'recurrent '
// }
// multidate // multidate
if (event.multidate) { if (event.multidate) {
return `${start.format('ddd, D MMM, HH:mm')} - ${end.format('ddd, D MMM, HH:mm')}` return `${start.format('dddd D MMMM HH:mm')} - ${end.format('dddd D MMMM HH:mm')}`
} }
// normal event // normal event
return `${start.format('dddd, D MMMM, HH:mm')}-${end.format('HH:mm')}` return `${start.format('dddd D MMMM HH:mm')}-${end.format('HH:mm')}`
}) })
} }

View File

@@ -4,6 +4,6 @@ export default () => {
Vue.use(VCalendar, { Vue.use(VCalendar, {
componentPrefix: 'vc', componentPrefix: 'vc',
// why is that ?! // why is that ?!
firstDayOfWeek: 2 // firstDayOfWeek: 2
}) })
} }

252
preso.md Normal file
View File

@@ -0,0 +1,252 @@
# Gancio
_a shared agenda for local communities_
<small>
lesion / underscore hacklab / hackmeeting 0x19
</small>
--
- a brief history, where we come from
- where are we at
- where we are going
note: se qualcuno si sta chiedendo giustamente "ma ancora?"
--
## Intro
- is technology neutral? (hint: nope)
- there are choices based on values...
- ...and consequences
note: essendo uno dei primi talk rimarchero' un concetto che proprio la
comunita' di hackmeeting mi ha spiegato e non vorrei darlo per assodato in
questo contesto, ovvero che la tecnologia non e' neutrale ma facilita dei casi
d'uso, modifica l'ambito del possibile, quello che facilita e quello che
complica. gli strumenti sono formati dalla visione di chi li ha pensati,
progettati e costruiti e ne propagano i valori. nello sviluppo di strumenti ci
sono quindi scelte progettuali e ci sono delle conseguenze sulle scelte che
vengono fatte, questa e' la teoria. in pratica parliamo di quali sono le
impostazioni di default, quali sono le funzionalita' che scegliamo di
implementare o meno, quali sono i casi d'uso che vogliamo agevolare o meno.
sono domande importanti da farsi quando si sviluppa e quando si usa uno
strumento e cerchero' di spiegare un po' le scelte che sono state fatte su
gancio e perche'. ovviamente queste scelte sono ridiscutibili, siamo qui anche
per questo.
--
<blockquote><small>
... choices many of us in the social movements/left/activist scene make to be present in certain social networks, or to use certain technologies due to pragmatism - <strong>everybody is there</strong>, we need to reach 'common people', and so on. This is totally ok, but I feel we lack spaces to imagine which tools we need, which tech we would want to have if anything was possible? Do we want a FLOSS version of Instagram? Or do we want something completely different? Perhaps pragmatism allows the big tech tools to shape us and how we do our activism? What if we could shape the tools?
</small>
<span>absorto @ hackit_desiderata pad</span></blockquote>
note: tra le idee di tavole rotonde di quest'anno nel pad c'era questa serie di domande centrali.
questo non lo dico perche' penso che gancio sia chissa' che strumento
rivoluzionario, anzi. lo dico invece piu' che altro per spronarci tutti a farci
di questi ragionamenti e non solo per quanto riguarda gli strumenti tecnici.
dobbiamo chiederci cosa ci serve e perche'! non servono competenze per sognare e
desiderare, serve immaginarci dei modi altri, dei mondi altri. e questo sognare
lo ribadisco non si puo' lasciare ai nerd e basta.
---
### where we come from
- born from needs
note: carta canta, sgombero asilo 2019
--
## small & Local
- size matters
- small tech does not scale and it's ok
- local (no timezone)
note: progettando strumenti che devono scalare verso l'alto
costruiamo fondamentalmente centri di potere.
non e' solo una questione di software libero o della proprieta' del software...
se fb fosse nostro sarebbe comunque un problema, se il parlamento
fosse nostro sarebbe comunque un problema.
gancio non e' pensato per scalare, anzi, il caso d'uso facilitato
e' quello di un nodo legato ad un territorio e questa scelta
ha poi conseguenze sulla progettazione del sw e sulle conseguenze
nel suo utilizzo. ad es. una delle cons. di questa idea e' il fatto che
il fuso orario degli eventi e' uno per nodo, non per evento.
il caso d'uso poteva essere tematico ad esempio, nazionale, per posto....
conseguenze sull'uso > gli utenti sanno dove trovarti nella vita vera,
c'e' un rapporto, se domani gancio ha problemi c'e' un canale privilegiato
per comunicare.
--
## focus on content
nowhere on gancio appears the identity of who published the event, not even under a nickname, not even to administrators (except in the db).
This is not an ego-friendly platform, gamification is not aided.
note: altre scelte, non c'e' scritto da nessuna parte chi ha postato l'evento.
gamification non e' agevolata.
--
## random people first
We do not want logged user to get more features than random visitor.
People don't have to register to use it, not even to publish events.
note: eventi anonimi, gli eventi vanno confermati, possibilita' di modificare gli eventi?
--
## fuck walled garden
We are not interested in making hits, monitor user activities, sell data or ads: we export events in many ways, via RSS feeds, via global or individual ics calendar, embedding lists of events or single event via iframe or webcomponents on other websites, via h-event (microformat), via microdata, via ActivityPub, via API.
---
### 3 years later...
note: questo e' da dove siamo partiti...
--
### Status & Last Updates
- 25 known instances
- 11 languages
--
- https://gancio.cisti.org - Torino
- https://lapunta.org - Firenze
- https://sapratza.in - Sardegna
- https://ponente.rocks - Ponente Ligure
- https://bcn.convoca.la/ - Barcellona
- https://lubakiagenda.net/ - Bilbao
- https://bonn.jetzt/ - Bonn
- https://impending.events - Minneapolis
ma anche istanze tematiche:
- https://quest.livellosegreto.it - livello segreto
- https://events.osm.lat - OSM latino america
--
### Maintainance
> Another flaw in the human character
is that everybody wants to build
and nobody wants to do maintenance.<br/>
- Kurt Vonnegut
note: cosa ho fatto in questo tempo? principalmente c'e' un debito tecnico, la roba viene aggiornata, si scoprono bugs frequentemente,
aggiornando di scoprono altri bug! ogni feature si porta gatte da pelare notevoli.
--
Flyer download
note: non si poteva scaricare l'immagine associata ad un evento.
anche qui sono conseguenze indirette, [il componente](https://vuetifyjs.com/en/components/images/) della libreria che sto usando ha fatto altre scelte.
--
New time selection widget
--
Improve Recurrent events
--
Tag page
--
restrict new tag entropy
note: su questo c'e' ancora da fare per i po', debito tecnico
--
Place page
--
Redirect based on content-type
note: content-type cos'e' feed rss, ics, AP
--
Collection page
--
Add microdata support
--
sitemap
---
CLI
--
Add MariaDB supports
--
Improve SMTP configuration
--
footer links reordering
--
Unit Testing
--
Lot of fixes....
--
[API](https://gancio.org/dev/api)
--
### Webcomponent
<gancio-events baseurl='https://gancio.cisti.org' title='eventi' maxlength=4 theme='dark'/>
--
<gancio-events baseurl='https://gancio.cisti.org' title='eventi' maxlength=2 theme='dark' sidebar="false"/>
--
### WPGancio
---
## where are we going
--
### Plugins!
--
OSM integration
--
Generate
---
### Wanna help?
- let's think about what serves the community we want to build
- let's maintain the tools we already have
---
### References
- SITE: https://gancio.org
- DEMO: https://demo.gancio.org

View File

@@ -1,12 +1,15 @@
RELEASE=v$(cat package.json | jq ".version" | sed -e 's/"//g') RELEASE=v$(cat package.json | jq ".version" | sed -e 's/"//g')
echo "Releasing $RELEASE" echo "Releasing $RELEASE"
rm -fr node_modules
yarn
yarn build yarn build
yarn pack yarn pack
yarn publish # yarn publish
yarn doc gpg --pinentry-mode loopback --passphrase `pass underscore/pgp` --detach-sign --local-user 5DAC477D5441B7A15ACBF680BBEB4DD39AC6CCA9 gancio-$RELEASE.tgz
gpg --detach-sign --local-user 5DAC477D5441B7A15ACBF680BBEB4DD39AC6CCA9 gancio-$RELEASE.tgz
cp gancio-$RELEASE.tgz releases/ cp gancio-$RELEASE.tgz releases/
mv gancio-$RELEASE.tgz releases/latest.tgz mv gancio-$RELEASE.tgz releases/latest.tgz
cp gancio-$RELEASE.tgz.sig releases/ cp gancio-$RELEASE.tgz.sig releases/
mv gancio-$RELEASE.tgz.sig releases/latest.tgz.sig mv gancio-$RELEASE.tgz.sig releases/latest.tgz.sig
rsync -a docs/_site/ gancio.org:/var/www/gancio/ yarn doc
rsync -a docs/_site/ --chown=www-data:www-data cisti.web:/var/www/gancio/
cd docs

4
reveal-md.json Normal file
View File

@@ -0,0 +1,4 @@
{
"separator": "^---",
"verticalSeparator": "^--"
}

5
reveal.json Normal file
View File

@@ -0,0 +1,5 @@
{
"controls": true,
"progress": true,
"transition": "slide"
}

View File

@@ -570,8 +570,8 @@ const eventController = {
let tags = [] let tags = []
if (body.tags) { if (body.tags) {
tags = await tagController._findOrCreate(body.tags) tags = await tagController._findOrCreate(body.tags)
await event.setTags(tags)
} }
await event.setTags(tags)
let newEvent = await Event.findByPk(event.id, { include: [Tag, Place] }) let newEvent = await Event.findByPk(event.id, { include: [Tag, Place] })
newEvent = newEvent.get() newEvent = newEvent.get()
@@ -688,7 +688,7 @@ const eventController = {
const events = await Event.findAll({ const events = await Event.findAll({
where, where,
attributes: { attributes: {
exclude: ['likes', 'boost', 'userId', 'is_visible', 'createdAt', 'description', 'resources', 'recurrent', 'placeId', 'parentId'] exclude: ['likes', 'boost', 'userId', 'is_visible', 'createdAt', 'description', 'resources', 'recurrent', 'placeId', 'image_path']
}, },
order: [['start_datetime', older ? 'DESC' : 'ASC']], order: [['start_datetime', older ? 'DESC' : 'ASC']],
include: [ include: [
@@ -723,8 +723,8 @@ const eventController = {
const end = req.query.end const end = req.query.end
const tags = req.query.tags const tags = req.query.tags
const places = req.query.places const places = req.query.places
const limit = req.query.max const limit = Number(req.query.max) || 0
const page = req.query.page = 0 const page = Number(req.query.page) || 0
const older = req.query.older || false const older = req.query.older || false
const show_recurrent = settings.allow_recurrent_event && const show_recurrent = settings.allow_recurrent_event &&

View File

@@ -1,9 +1,5 @@
const Sequelize = require('sequelize') const Sequelize = require('sequelize')
// this is an hack: https://github.com/sequelize/sequelize/pull/14800
const livePatchMariaDBDialect = require('sequelize/lib/dialects/mariadb/query')
livePatchMariaDBDialect.prototype.handleJsonSelectQuery = () => null
const Umzug = require('umzug') const Umzug = require('umzug')
const path = require('path') const path = require('path')
const config = require('../../config') const config = require('../../config')

View File

@@ -91,6 +91,13 @@ module.exports = {
const router = express.Router() const router = express.Router()
// serve images/thumb // serve images/thumb
router.use('/media/', express.static(config.upload_path, { immutable: true, maxAge: '1y' }), (_req, res) => res.sendStatus(404)) router.use('/media/', express.static(config.upload_path, { immutable: true, maxAge: '1y' }), (_req, res) => res.sendStatus(404))
router.use('/download/:filename', (req, res) => {
return res.download(req.params.filename, undefined, { root: config.upload_path }, err => {
if (err) {
res.status(404).send('Not found (but nice try 😊)')
}
})
})
router.use('/noimg.svg', express.static('./static/noimg.svg')) router.use('/noimg.svg', express.static('./static/noimg.svg'))
router.use('/logo.png', (req, res, next) => { router.use('/logo.png', (req, res, next) => {

File diff suppressed because it is too large Load Diff

View File

@@ -258,6 +258,17 @@ describe('Tags', () => {
// expect(response.body[0].title).toBe('test tags') // expect(response.body[0].title).toBe('test tags')
expect(response.body[0].tags.length).toBe(3) expect(response.body[0].tags.length).toBe(3)
}) })
test('should return limited events', async () => {
let response = await request(app).get('/api/events?max=1')
.expect(200)
expect(response.body.length).toBe(1)
response = await request(app).get('/api/events?max=2')
.expect(200)
expect(response.body.length).toBe(2)
})
}) })
describe('Place', () => { describe('Place', () => {

View File

@@ -9,8 +9,8 @@
"serve": "vite preview" "serve": "vite preview"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.34", "@sveltejs/vite-plugin-svelte": "^1.0.4",
"svelte": "^3.46.3", "svelte": "^3.50.0",
"vite": "^2.7.11" "vite": "^3.0.9"
} }
} }

View File

@@ -1,5 +1,8 @@
<svelte:options tag="gancio-event" />
<script> <script>
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { when } from './helpers'
export let baseurl = 'https://demo.gancio.org' export let baseurl = 'https://demo.gancio.org'
export let id export let id
@@ -9,8 +12,8 @@
function update(id, baseurl) { function update(id, baseurl) {
if (mounted) { if (mounted) {
fetch(`${baseurl}/api/event/${id}`) fetch(`${baseurl}/api/event/${id}`)
.then(res => res.json()) .then((res) => res.json())
.then(e => event = e) .then((e) => (event = e))
} }
} }
@@ -20,18 +23,6 @@
}) })
$: update(id, baseurl) $: update(id, baseurl)
function when (event) {
return new Date(event.start_datetime*1000)
.toLocaleDateString(undefined,
{
weekday: 'long',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}
function thumbnail(event) { function thumbnail(event) {
return `${baseurl}/media/thumb/${event.media[0].url}` return `${baseurl}/media/thumb/${event.media[0].url}`
} }
@@ -43,25 +34,34 @@
} }
return 'center center' return 'center center'
} }
</script> </script>
<svelte:options tag="gancio-event"/>
{#if event} {#if event}
<a href='{baseurl}/event/{event.slug || event.id}' class='card' target='_blank'> <a
href="{baseurl}/event/{event.slug || event.id}"
class="card"
target="_blank"
>
{#if event.media.length} {#if event.media.length}
<img src="{thumbnail(event)}" alt="{event.media[0].name}" style="object-position: {position(event)}; aspect-ratio=1.7778;"> <img
src={thumbnail(event)}
alt={event.media[0].name}
style="object-position: {position(event)}; aspect-ratio=1.7778;"
/>
{/if} {/if}
<div class="container"> <div class="container">
<strong>{event.title}</strong> <strong>{event.title}</strong>
<div>{when(event)}</div> <div>{when(event)}</div>
<div class='place'>@{event.place.name}</div> <div class="place">@{event.place.name}</div>
</div> </div>
</a> </a>
{/if} {/if}
<style> <style>
.card { .card {
display: block; display: block;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS',
sans-serif;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
transition: 0.3s; transition: 0.3s;
border-radius: 5px; /* 5px rounded corners */ border-radius: 5px; /* 5px rounded corners */
@@ -93,7 +93,7 @@ img {
/* Add some padding inside the card container */ /* Add some padding inside the card container */
.container { .container {
transition: padding-left .2s; transition: padding-left 0.2s;
padding: 16px; padding: 16px;
} }

View File

@@ -1,6 +1,8 @@
<script> <svelte:options tag="gancio-events" />
<script>
import { onMount } from 'svelte' import { onMount } from 'svelte'
import { when } from './helpers'
export let baseurl = '' export let baseurl = ''
export let title = '' export let title = ''
export let maxlength = false export let maxlength = false
@@ -33,84 +35,90 @@
params.push(`show_recurrent=${show_recurrent ? 'true' : 'false'}`) params.push(`show_recurrent=${show_recurrent ? 'true' : 'false'}`)
fetch(`${baseurl}/api/events?${params.join('&')}`) fetch(`${baseurl}/api/events?${params.join('&')}`)
.then(res => res.json()) .then((res) => res.json())
.then(e => { .then((e) => {
events = e events = e
}) })
.catch(e => { .catch((e) => {
console.error('Error loading Gancio API -> ', e) console.error('Error loading Gancio API -> ', e)
}) })
} }
function position(event) { function position(event) {
if (event.media[0].focalpoint) { if (event.media && event.media[0].focalpoint) {
const focalpoint = event.media[0].focalpoint const focalpoint = event.media[0].focalpoint
return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%` return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%`
} }
return 'center center' return 'center center'
} }
function when (timestamp) {
return new Date(timestamp*1000)
.toLocaleDateString(undefined,
{
weekday: 'long',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}
onMount(() => { onMount(() => {
mounted = true mounted = true
update() update()
}) })
$: update(maxlength && title && places && tags && theme && show_recurrent && sidebar) $: update(
maxlength && title && places && tags && theme && show_recurrent && sidebar
)
</script> </script>
<svelte:options tag="gancio-events"/>
{#if external_style}<link rel='stylesheet' href='{external_style}' />{/if} {#if external_style}<link rel="stylesheet" href={external_style} />{/if}
{#if events.length} {#if events.length}
<div id='gancioEvents' <div
class:dark="{theme === 'dark'}" class:light="{theme === 'light'}" id="gancioEvents"
class:sidebar="{sidebar === 'true'}" class:nosidebar="{sidebar !== 'true'}"> class:dark={theme === 'dark'}
class:light={theme === 'light'}
class:sidebar={sidebar === 'true'}
class:nosidebar={sidebar !== 'true'}
>
{#if title && sidebar === 'true'} {#if title && sidebar === 'true'}
<a href='{baseurl}' target='_blank' id='header'> <a href={baseurl} target="_blank" id="header">
<div class='content'> <div class="content">
<div class='title'>{title}</div> <div class="title">{title}</div>
<img id='logo' alt='logo' src='{baseurl}/logo.png'/> <img id="logo" alt="logo" src="{baseurl}/logo.png" />
</div> </div>
</a> </a>
{/if} {/if}
{#each events as event} {#each events as event}
<a href='{baseurl}/event/{event.slug || event.id}' class='event' title='{event.title}' target='_blank'> <a
href="{baseurl}/event/{event.slug || event.id}"
class="event"
title={event.title}
target="_blank"
>
{#if sidebar !== 'true'} {#if sidebar !== 'true'}
<div class='img'> <div class="img">
{#if event.media.length} {#if event.media.length}
<img style="object-position: {position(event)}; aspect-ratio=1.7778;" <img
alt="{event.media[0].name}" style="object-position: {position(event)}; aspect-ratio=1.7778;"
src="{baseurl + '/media/thumb/' + event.media[0].url}" loading='lazy'/> alt={event.media[0].name}
src={baseurl + '/media/thumb/' + event.media[0].url}
loading="lazy"
/>
{:else} {:else}
<img style="aspect-ratio=1.7778;" <img
alt="{event.title}" style="aspect-ratio=1.7778;"
src="{baseurl + '/noimg.svg'}" loading='lazy'/> alt={event.title}
src={baseurl + '/noimg.svg'}
loading="lazy"
/>
{/if} {/if}
</div> </div>
{/if} {/if}
<div class='content'> <div class="content">
<div class='subtitle'> <div class="subtitle">
{when(event.start_datetime)} {when(event)}
</div> </div>
<div class='title'> <div class="title">
{event.title} {event.title}
</div> </div>
<span class='place'>@{event.place.name} <span class='subtitle'> {event.place.address}</span></span> <span class="place"
>@{event.place.name}
<span class="subtitle"> {event.place.address}</span></span
>
{#if event.tags.length} {#if event.tags.length}
<div class='tags'> <div class="tags">
{#each event.tags as tag} {#each event.tags as tag}
<span class='tag'>#{tag}</span> <span class="tag">#{tag}</span>
{/each} {/each}
</div> </div>
{/if} {/if}
@@ -119,14 +127,19 @@
{/each} {/each}
</div> </div>
{/if} {/if}
<style> <style>
#gancioEvents { #gancioEvents {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
overflow-x: hidden; overflow-x: hidden;
width: 100%; width: 100%;
box-sizing: content-box; box-sizing: content-box;
margin: 0 auto; margin: 0 auto;
font-size: 1rem; font-size: 1rem;
text-align: left;
} }
.nosidebar { .nosidebar {
@@ -140,7 +153,8 @@
.sidebar { .sidebar {
max-width: 500px; max-width: 500px;
box-shadow: rgba(60, 64, 67, 0.4) 0px 1px 2px 0px, rgba(60, 64, 67, 0.25) 0px 1px 3px 1px; box-shadow: rgba(60, 64, 67, 0.4) 0px 1px 2px 0px,
rgba(60, 64, 67, 0.25) 0px 1px 3px 1px;
border-radius: 5px; border-radius: 5px;
font-size: 1rem; font-size: 1rem;
} }
@@ -167,7 +181,8 @@
border-radius: 15px; border-radius: 15px;
width: 100%; width: 100%;
height: 100%; height: 100%;
box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px,
rgba(0, 0, 0, 0.3) 0px 3px 7px -3px;
} }
.nosidebar .event { .nosidebar .event {
@@ -199,9 +214,10 @@ a {
margin: 0; margin: 0;
line-height: 1.275rem; line-height: 1.275rem;
font-weight: 400; font-weight: 400;
font-size: .875rem; font-size: 0.875rem;
position: relative; position: relative;
transition: background-color .3s cubic-bezier(.25,.8,.5,1), padding .3s; transition: background-color 0.3s cubic-bezier(0.25, 0.8, 0.5, 1),
padding 0.3s;
box-sizing: content-box; box-sizing: content-box;
} }
@@ -222,8 +238,8 @@ a:active .title {
.light { .light {
--bg-odd-color: #f5f5f5; --bg-odd-color: #f5f5f5;
--bg-even-color: #FAFAFA; --bg-even-color: #fafafa;
--bg-hover-color: #EEE; --bg-hover-color: #eee;
--text-color: #222; --text-color: #222;
--title-color: black; --title-color: black;
--line-color: rgba(220, 220, 220, 0.9); --line-color: rgba(220, 220, 220, 0.9);
@@ -271,5 +287,4 @@ a:active .title {
margin-right: 10px; margin-right: 10px;
display: inline-block; display: inline-block;
} }
</style> </style>

View File

@@ -0,0 +1,24 @@
function formatDatetime(timestamp, type = 'long') {
const options =
type === 'long'
? {
weekday: 'long',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
}
: { hour: '2-digit', minute: '2-digit' }
return new Date(timestamp * 1000).toLocaleString(undefined, options)
}
export function when(event) {
if (event.multidate) {
return formatDatetime(event.start_datetime) + ' - ' + formatDatetime(event.end_datetime)
}
return (
formatDatetime(event.start_datetime) +
(event.end_datetime ? '-' + formatDatetime(event.end_datetime, 'short') : '')
)
}

View File

@@ -2,157 +2,169 @@
# yarn lockfile v1 # yarn lockfile v1
"@rollup/pluginutils@^4.1.2": "@esbuild/linux-loong64@0.14.54":
version "4.2.0" version "0.14.54"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.0.tgz#a14bbd058fdbba0a5647143b16ed0d86fb60bd08" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
integrity sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA== integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
"@rollup/pluginutils@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
dependencies: dependencies:
estree-walker "^2.0.1" estree-walker "^2.0.1"
picomatch "^2.2.2" picomatch "^2.2.2"
"@sveltejs/vite-plugin-svelte@^1.0.0-next.34": "@sveltejs/vite-plugin-svelte@^1.0.4":
version "1.0.0-next.39" version "1.0.4"
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.39.tgz#b9437de18d13a475f76cf603511174cdf905b8d4" resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.4.tgz#363a0adeb9221c35abb65197c6db0754b9994a08"
integrity sha512-gnvvcAW2LK+KnUn8lKb2ypcXKwSp2K57mem5C4VNKfjxdRpM6+XwNavWwVf6otnDhz3qPYl/TKKW6/dRr6eeAw== integrity sha512-UZco2fdj0OVuRWC0SUJjEOftITc2IeHLFJNp00ym9MuQ9dShnlO4P29G8KUxRlcS7kSpzHuko6eCR9MOALj7lQ==
dependencies: dependencies:
"@rollup/pluginutils" "^4.1.2" "@rollup/pluginutils" "^4.2.1"
debug "^4.3.3" debug "^4.3.4"
kleur "^4.1.4" deepmerge "^4.2.2"
magic-string "^0.25.7" kleur "^4.1.5"
svelte-hmr "^0.14.9" magic-string "^0.26.2"
svelte-hmr "^0.14.12"
debug@^4.3.3: debug@^4.3.4:
version "4.3.3" version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies: dependencies:
ms "2.1.2" ms "2.1.2"
esbuild-android-64@0.14.25: deepmerge@^4.2.2:
version "0.14.25" version "4.2.2"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz#d532d38cb5fe0ae45167ce35f4bbc784c636be40" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-L5vCUk7TzFbBnoESNoXjU3x9+/+7TDIE/1mTfy/erAfvZAqC+S3sp/Qa9wkypFMcFvN9FzvESkTlpeQDolREtQ== integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
esbuild-android-arm64@0.14.25: esbuild-android-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz#9c5bb3366aabfd14a1c726d36978b79441dfcb6e" resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
integrity sha512-4jv5xPjM/qNm27T5j3ZEck0PvjgQtoMHnz4FzwF5zNP56PvY2CT0WStcAIl6jNlsuDdN63rk2HRBIsO6xFbcFw== integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
esbuild-darwin-64@0.14.25: esbuild-android-arm64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz#05dcdb6d884f427039ffee5e92ff97527e56c26d" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
integrity sha512-TGp8tuudIxOyWd1+8aYPxQmC1ZQyvij/AfNBa35RubixD0zJ1vkKHVAzo0Zao1zcG6pNqiSyzfPto8vmg0s7oA== integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
esbuild-darwin-arm64@0.14.25: esbuild-darwin-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz#28e080da4ea0cfe9498071e7f8060498caee1a95" resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
integrity sha512-oTcDgdm0MDVEmw2DWu8BV68pYuImpFgvWREPErBZmNA4MYKGuBRaCiJqq6jZmBR1x+3y1DWCjez+5uLtuAm6mw== integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
esbuild-freebsd-64@0.14.25: esbuild-darwin-arm64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz#200d3664a3b945bc9fdcba73614b49a11ebd1cfa" resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
integrity sha512-ueAqbnMZ8arnuLH8tHwTCQYeptnHOUV7vA6px6j4zjjQwDx7TdP7kACPf3TLZLdJQ3CAD1XCvQ2sPhX+8tacvQ== integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
esbuild-freebsd-arm64@0.14.25: esbuild-freebsd-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz#624b08c5da6013bdc312aaa23c4ff409580f5c3c" resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
integrity sha512-+ZVWud2HKh+Ob6k/qiJWjBtUg4KmJGGmbvEXXW1SNKS7hW7HU+Zq2ZCcE1akFxOPkVB+EhOty/sSek30tkCYug== integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
esbuild-linux-32@0.14.25: esbuild-freebsd-arm64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz#0238e597eb0b60aa06c7e98fccbbfd6bb9a0d6c5" resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
integrity sha512-3OP/lwV3kCzEz45tobH9nj+uE4ubhGsfx+tn0L26WAGtUbmmcRpqy7XRG/qK7h1mClZ+eguIANcQntYMdYklfw== integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
esbuild-linux-64@0.14.25: esbuild-linux-32@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz#8a8b8cf47dfce127c858e71229d9a385a82c62e8" resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
integrity sha512-+aKHdHZmX9qwVlQmu5xYXh7GsBFf4TWrePgeJTalhXHOG7NNuUwoHmketGiZEoNsWyyqwH9rE5BC+iwcLY30Ug== integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
esbuild-linux-arm64@0.14.25: esbuild-linux-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz#7ac94371418a2640ba413bc1700aaedeb2794e52" resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
integrity sha512-UxfenPx/wSZx55gScCImPtXekvZQLI2GW3qe5dtlmU7luiqhp5GWPzGeQEbD3yN3xg/pHc671m5bma5Ns7lBHw== integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
esbuild-linux-arm@0.14.25: esbuild-linux-arm64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz#034bd18e9310b9f010c89f90ef7f05706689600b" resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
integrity sha512-aTLcE2VBoLydL943REcAcgnDi3bHtmULSXWLbjtBdtykRatJVSxKMjK9YlBXUZC4/YcNQfH7AxwVeQr9fNxPhw== integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
esbuild-linux-mips64le@0.14.25: esbuild-linux-arm@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz#05f98a8cf6b578eab6b4e6b0ab094f37530934f4" resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
integrity sha512-wLWYyqVfYx9Ur6eU5RT92yJVsaBGi5RdkoWqRHOqcJ38Kn60QMlcghsKeWfe9jcYut8LangYZ98xO1LxIoSXrQ== integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
esbuild-linux-ppc64le@0.14.25: esbuild-linux-mips64le@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz#46fd0add8d8535678439d7a9c2876ad20042d952" resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
integrity sha512-0dR6Csl6Zas3g4p9ULckEl8Mo8IInJh33VCJ3eaV1hj9+MHGdmDOakYMN8MZP9/5nl+NU/0ygpd14cWgy8uqRw== integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
esbuild-linux-riscv64@0.14.25: esbuild-linux-ppc64le@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz#ea2e986f0f3e5df73c635135dd778051734fc605" resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
integrity sha512-J4d20HDmTrgvhR0bdkDhvvJGaikH3LzXQnNaseo8rcw9Yqby9A90gKUmWpfwqLVNRILvNnAmKLfBjCKU9ajg8w== integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
esbuild-linux-s390x@0.14.25: esbuild-linux-riscv64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz#efe89486e9a1b1508925048076e3f3a6698aa6a3" resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
integrity sha512-YI2d5V6nTE73ZnhEKQD7MtsPs1EtUZJ3obS21oxQxGbbRw1G+PtJKjNyur+3t6nzHP9oTg6GHQ3S3hOLLmbDIQ== integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
esbuild-netbsd-64@0.14.25: esbuild-linux-s390x@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz#439fe27d8ee3b5887501ee63988e85f920107db6" resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
integrity sha512-TKIVgNWLUOkr+Exrye70XTEE1lJjdQXdM4tAXRzfHE9iBA7LXWcNtVIuSnphTqpanPzTDFarF0yqq4kpbC6miA== integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
esbuild-openbsd-64@0.14.25: esbuild-netbsd-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz#31ebf616aadf6e60674469f2b92cec92280d9930" resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
integrity sha512-QgFJ37A15D7NIXBTYEqz29+uw3nNBOIyog+3kFidANn6kjw0GHZ0lEYQn+cwjyzu94WobR+fes7cTl/ZYlHb1A== integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
esbuild-sunos-64@0.14.25: esbuild-openbsd-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz#815e4f936d74970292a63ccfd5791fe5e3569f5f" resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
integrity sha512-rmWfjUItYIVlqr5EnTH1+GCxXiBOC42WBZ3w++qh7n2cS9Xo0lO5pGSG2N+huOU2fX5L+6YUuJ78/vOYvefeFw== integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
esbuild-windows-32@0.14.25: esbuild-sunos-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz#189e14df2478f2c193c86968ab1fb54e1ceaafd2" resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
integrity sha512-HGAxVUofl3iUIz9W10Y9XKtD0bNsK9fBXv1D55N/ljNvkrAYcGB8YCm0v7DjlwtyS6ws3dkdQyXadbxkbzaKOA== integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
esbuild-windows-64@0.14.25: esbuild-windows-32@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz#3d5fbfdc3856850bb47439299e3b60dd18be111f" resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
integrity sha512-TirEohRkfWU9hXLgoDxzhMQD1g8I2mOqvdQF2RS9E/wbkORTAqJHyh7wqGRCQAwNzdNXdg3JAyhQ9/177AadWA== integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
esbuild-windows-arm64@0.14.25: esbuild-windows-64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz#8b243cbbad8a86cf98697da9ccb88c05df2ef458" resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
integrity sha512-4ype9ERiI45rSh+R8qUoBtaj6kJvUOI7oVLhKqPEpcF4Pa5PpT3hm/mXAyotJHREkHpM87PAJcA442mLnbtlNA== integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
esbuild@^0.14.14: esbuild-windows-arm64@0.14.54:
version "0.14.25" version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.25.tgz#ddb9d47b91ca76abb7d850ce3dfed0bc3dc88d16" resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
integrity sha512-4JHEIOMNFvK09ziiL+iVmldIhLbn49V4NAVo888tcGFKedEZY/Y8YapfStJ6zSE23tzYPKxqKwQBnQoIO0BI/Q== integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
esbuild@^0.14.47:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
optionalDependencies: optionalDependencies:
esbuild-android-64 "0.14.25" "@esbuild/linux-loong64" "0.14.54"
esbuild-android-arm64 "0.14.25" esbuild-android-64 "0.14.54"
esbuild-darwin-64 "0.14.25" esbuild-android-arm64 "0.14.54"
esbuild-darwin-arm64 "0.14.25" esbuild-darwin-64 "0.14.54"
esbuild-freebsd-64 "0.14.25" esbuild-darwin-arm64 "0.14.54"
esbuild-freebsd-arm64 "0.14.25" esbuild-freebsd-64 "0.14.54"
esbuild-linux-32 "0.14.25" esbuild-freebsd-arm64 "0.14.54"
esbuild-linux-64 "0.14.25" esbuild-linux-32 "0.14.54"
esbuild-linux-arm "0.14.25" esbuild-linux-64 "0.14.54"
esbuild-linux-arm64 "0.14.25" esbuild-linux-arm "0.14.54"
esbuild-linux-mips64le "0.14.25" esbuild-linux-arm64 "0.14.54"
esbuild-linux-ppc64le "0.14.25" esbuild-linux-mips64le "0.14.54"
esbuild-linux-riscv64 "0.14.25" esbuild-linux-ppc64le "0.14.54"
esbuild-linux-s390x "0.14.25" esbuild-linux-riscv64 "0.14.54"
esbuild-netbsd-64 "0.14.25" esbuild-linux-s390x "0.14.54"
esbuild-openbsd-64 "0.14.25" esbuild-netbsd-64 "0.14.54"
esbuild-sunos-64 "0.14.25" esbuild-openbsd-64 "0.14.54"
esbuild-windows-32 "0.14.25" esbuild-sunos-64 "0.14.54"
esbuild-windows-64 "0.14.25" esbuild-windows-32 "0.14.54"
esbuild-windows-arm64 "0.14.25" esbuild-windows-64 "0.14.54"
esbuild-windows-arm64 "0.14.54"
estree-walker@^2.0.1: estree-walker@^2.0.1:
version "2.0.2" version "2.0.2"
@@ -176,22 +188,22 @@ has@^1.0.3:
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
is-core-module@^2.8.1: is-core-module@^2.9.0:
version "2.8.1" version "2.10.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
dependencies: dependencies:
has "^1.0.3" has "^1.0.3"
kleur@^4.1.4: kleur@^4.1.5:
version "4.1.4" version "4.1.5"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
magic-string@^0.25.7: magic-string@^0.26.2:
version "0.25.9" version "0.26.3"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.3.tgz#25840b875140f7b4785ab06bddc384270b7dd452"
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== integrity sha512-u1Po0NDyFcwdg2nzHT88wSK0+Rih0N1M+Ph1Sp08k8yvFFU3KR72wryS7e1qMPJypt99WB7fIFVCA92mQrMjrg==
dependencies: dependencies:
sourcemap-codec "^1.4.8" sourcemap-codec "^1.4.8"
@@ -200,10 +212,10 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.3.1: nanoid@^3.3.4:
version "3.3.1" version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
path-parse@^1.0.7: path-parse@^1.0.7:
version "1.0.7" version "1.0.7"
@@ -220,28 +232,28 @@ picomatch@^2.2.2:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
postcss@^8.4.6: postcss@^8.4.16:
version "8.4.8" version "8.4.16"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ== integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
dependencies: dependencies:
nanoid "^3.3.1" nanoid "^3.3.4"
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.0.2" source-map-js "^1.0.2"
resolve@^1.22.0: resolve@^1.22.1:
version "1.22.0" version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies: dependencies:
is-core-module "^2.8.1" is-core-module "^2.9.0"
path-parse "^1.0.7" path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0" supports-preserve-symlinks-flag "^1.0.0"
rollup@^2.59.0: "rollup@>=2.75.6 <2.77.0 || ~2.77.0":
version "2.70.0" version "2.77.3"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.0.tgz#17a92e5938e92a251b962352e904c9f558230ec7" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
integrity sha512-iEzYw+syFxQ0X9RefVwhr8BA2TNJsTaX8L8dhyeyMECDbmiba+8UQzcu+xZdji0+JQ+s7kouQnw+9Oz5M19XKA== integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
@@ -260,24 +272,24 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svelte-hmr@^0.14.9: svelte-hmr@^0.14.12:
version "0.14.11" version "0.14.12"
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.11.tgz#63d532dc9c2c849ab708592f034765fa2502e568" resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.12.tgz#a127aec02f1896500b10148b2d4d21ddde39973f"
integrity sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ== integrity sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==
svelte@^3.46.3: svelte@^3.50.0:
version "3.46.4" version "3.50.0"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.46.4.tgz#0c46bc4a3e20a2617a1b7dc43a722f9d6c084a38" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.50.0.tgz#d11a7a6bd1e084ec051d55104a9af8bccf54461f"
integrity sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg== integrity sha512-zXeOUDS7+85i+RxLN+0iB6PMbGH7OhEgjETcD1fD8ZrhuhNFxYxYEHU41xuhkHIulJavcu3PKbPyuCrBxdxskQ==
vite@^2.7.11: vite@^3.0.9:
version "2.8.6" version "3.0.9"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.8.6.tgz#32d50e23c99ca31b26b8ccdc78b1d72d4d7323d3" resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.9.tgz#45fac22c2a5290a970f23d66c1aef56a04be8a30"
integrity sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug== integrity sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==
dependencies: dependencies:
esbuild "^0.14.14" esbuild "^0.14.47"
postcss "^8.4.6" postcss "^8.4.16"
resolve "^1.22.0" resolve "^1.22.1"
rollup "^2.59.0" rollup ">=2.75.6 <2.77.0 || ~2.77.0"
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"

File diff suppressed because it is too large Load Diff

4784
yarn.lock

File diff suppressed because it is too large Load Diff