Merge branch 'master' into gh
This commit is contained in:
commit
421aa12781
47 changed files with 4964 additions and 6891 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,7 +8,6 @@
|
|||
_*.js
|
||||
config*.json
|
||||
tests/seeds/testdb.sqlite
|
||||
preso.md
|
||||
gancio.sqlite
|
||||
db.sqlite
|
||||
releases
|
||||
|
|
34
CHANGELOG
34
CHANGELOG
|
@ -1,6 +1,38 @@
|
|||
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**:
|
||||
- supported nodejs version >=14 <=16 (nodejs 12 reached End-of-Life on 30 April 2022)
|
||||
- minimum mariadb supported version >= 10.5.2
|
||||
|
|
|
@ -1,31 +1,43 @@
|
|||
import dayjs from 'dayjs'
|
||||
|
||||
export function attributesFromEvents (_events) {
|
||||
|
||||
// const colors = ['teal', 'green', 'yellow', 'teal', 'indigo', 'green', 'red', 'purple', 'pink', 'gray']
|
||||
export function attributesFromEvents(_events) {
|
||||
// merge events with same date
|
||||
let attributes = []
|
||||
const now = dayjs().unix()
|
||||
for(let e of _events) {
|
||||
const key = dayjs.unix(e.start_datetime).format('YYYYMMDD')
|
||||
const c = e.start_datetime < now ? 'vc-past' : ''
|
||||
for (let e of _events) {
|
||||
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.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)
|
||||
if (!i) {
|
||||
attributes.push({ day: key, key: e.id, n: 1, dates: new Date(e.start_datetime * 1000),
|
||||
dot: { color: 'teal', class: c } })
|
||||
attributes.push({
|
||||
day: key, key: e.id, n: 1, dates: new Date(e.start_datetime * 1000),
|
||||
dot: { color: 'teal', class: c }
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
i.n++
|
||||
if (i.n >= 20 ) {
|
||||
if (i.n >= 20) {
|
||||
i.dot = { color: 'purple', class: c }
|
||||
} else if ( i.n >= 10 ) {
|
||||
i.dot = { color: 'red', class: c}
|
||||
} else if ( i.n >= 5 ) {
|
||||
i.dot = { color: 'orange', class: c}
|
||||
} else if ( i.n >= 3 ) {
|
||||
i.dot = { color: 'yellow', class: c}
|
||||
} else if (i.n >= 10) {
|
||||
i.dot = { color: 'red', class: c }
|
||||
} else if (i.n >= 5) {
|
||||
i.dot = { color: 'orange', class: c }
|
||||
} else if (i.n >= 3) {
|
||||
i.dot = { color: 'yellow', class: c }
|
||||
} else {
|
||||
i.dot = { color: 'teal', class: c }
|
||||
}
|
||||
|
|
|
@ -57,11 +57,11 @@ li {
|
|||
padding: 15px;
|
||||
}
|
||||
|
||||
.editor .content {
|
||||
.editor .ProseMirror {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.theme--dark .editor .content {
|
||||
.theme--dark .editor .ProseMirror {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
@update:from-page='updatePage'
|
||||
:locale='$i18n.locale'
|
||||
:attributes='attributes'
|
||||
:timezone='settings.instance_timezone'
|
||||
transition='fade'
|
||||
aria-label='Calendar'
|
||||
is-expanded
|
||||
|
|
|
@ -2,35 +2,34 @@
|
|||
v-col(cols=12)
|
||||
.text-center
|
||||
v-btn-toggle.v-col-6.flex-column.flex-sm-row(v-model='type' color='primary' @change='type => change("type", type)')
|
||||
v-btn(value='normal' label="normal") {{$t('event.normal')}}
|
||||
v-btn(value='multidate' label='multidate') {{$t('event.multidate')}}
|
||||
v-btn(v-if='settings.allow_recurrent_event' value='recurrent' label="recurrent") {{$t('event.recurrent')}}
|
||||
v-btn(value='normal' label="normal") {{ $t('event.normal') }}
|
||||
v-btn(value='multidate' label='multidate') {{ $t('event.multidate') }}
|
||||
v-btn(v-if='settings.allow_recurrent_event' value='recurrent' label="recurrent") {{ $t('event.recurrent') }}
|
||||
|
||||
p {{$t(`event.${type}_description`)}}
|
||||
p {{ $t(`event.${type}_description`) }}
|
||||
|
||||
v-btn-toggle.v-col-6.flex-column.flex-sm-row(v-if='type === "recurrent"' color='primary' :value='value.recurrent.frequency' @change='fq => change("frequency", fq)')
|
||||
v-btn(v-for='f in frequencies' :key='f.value' :value='f.value') {{f.text}}
|
||||
v-btn(v-for='f in frequencies' :key='f.value' :value='f.value') {{ f.text }}
|
||||
|
||||
client-only
|
||||
.datePicker.mt-3
|
||||
v-input(:value='fromDate'
|
||||
:rules="[$validators.required('common.when')]")
|
||||
v-input(:value='fromDate' :rules="[$validators.required('common.when')]")
|
||||
vc-date-picker(
|
||||
:value='fromDate'
|
||||
@input="date => change('date', date)"
|
||||
v-model='fromDate'
|
||||
:is-range='type === "multidate"'
|
||||
@input="date => change('date', fromDate)"
|
||||
:timezone='settings.instance_timezone'
|
||||
:attributes='attributes'
|
||||
:locale='$i18n.locale'
|
||||
:from-page.sync='page'
|
||||
:is-dark="settings['theme.is_dark']"
|
||||
is-inline
|
||||
is-expanded
|
||||
:min-date='type !== "recurrent" && new Date()')
|
||||
|
||||
div.text-center.mb-2(v-if='type === "recurrent"')
|
||||
span(v-if='value.recurrent.frequency !== "1m" && value.recurrent.frequency !== "2m"') {{whenPatterns}}
|
||||
span(v-if='value.recurrent.frequency !== "1m" && value.recurrent.frequency !== "2m"') {{ whenPatterns }}
|
||||
v-btn-toggle.mt-1.flex-column.flex-sm-row(v-else :value='value.recurrent.type' color='primary' @change='fq => change("recurrentType", fq)')
|
||||
v-btn(v-for='whenPattern in whenPatterns' :value='whenPattern.key' :key='whenPatterns.key' small) {{whenPattern.label}}
|
||||
v-btn(v-for='whenPattern in whenPatterns' :value='whenPattern.key' :key='whenPatterns.key' small) {{ whenPattern.label }}
|
||||
|
||||
v-row.mt-3.col-md-6.mx-auto
|
||||
v-col.col-12.col-sm-6
|
||||
|
@ -42,6 +41,9 @@ v-col(cols=12)
|
|||
transition="scale-transition")
|
||||
template(v-slot:activator="{ on, attrs }")
|
||||
v-text-field(
|
||||
clearable
|
||||
:clear-icon='mdiClose'
|
||||
@click:clear='() => change("fromHour")'
|
||||
:label="$t('event.from')"
|
||||
:value="fromHour"
|
||||
:disabled='!value.from'
|
||||
|
@ -52,10 +54,10 @@ v-col(cols=12)
|
|||
v-on="on")
|
||||
v-time-picker(
|
||||
v-if="menuFromHour"
|
||||
:value="fromHour"
|
||||
v-model="fromHour"
|
||||
:allowedMinutes='allowedMinutes'
|
||||
format='24hr'
|
||||
@click:minute='menuFromHour=false'
|
||||
@click:minute='menuFromHour = false'
|
||||
@change='hr => change("fromHour", hr)')
|
||||
|
||||
|
||||
|
@ -68,6 +70,9 @@ v-col(cols=12)
|
|||
transition="scale-transition")
|
||||
template(v-slot:activator="{ on, attrs }")
|
||||
v-text-field(
|
||||
clearable
|
||||
:clear-icon='mdiClose'
|
||||
@click:clear='() => change("dueHour")'
|
||||
:label="$t('event.due')"
|
||||
:value="dueHour"
|
||||
:disabled='!fromHour'
|
||||
|
@ -77,13 +82,13 @@ v-col(cols=12)
|
|||
v-on="on")
|
||||
v-time-picker(
|
||||
v-if="menuDueHour"
|
||||
:value="dueHour"
|
||||
v-model="dueHour"
|
||||
:allowedMinutes='allowedMinutes'
|
||||
format='24hr'
|
||||
@click:minute='menuDueHour=false'
|
||||
@click:minute='menuDueHour = false'
|
||||
@change='hr => change("dueHour", hr)')
|
||||
|
||||
List(v-if='type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")')
|
||||
List(v-if='type === "normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")')
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
@ -91,7 +96,7 @@ import dayjs from 'dayjs'
|
|||
import { mapState } from 'vuex'
|
||||
import List from '@/components/List'
|
||||
import { attributesFromEvents } from '../assets/helper'
|
||||
import { mdiClockTimeFourOutline, mdiClockTimeEightOutline } from '@mdi/js'
|
||||
import { mdiClockTimeFourOutline, mdiClockTimeEightOutline, mdiClose } from '@mdi/js'
|
||||
|
||||
export default {
|
||||
name: 'DateInput',
|
||||
|
@ -100,14 +105,24 @@ export default {
|
|||
value: { type: Object, default: () => ({ from: null, due: null, recurrent: 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 {
|
||||
mdiClockTimeFourOutline, mdiClockTimeEightOutline,
|
||||
mdiClockTimeFourOutline, mdiClockTimeEightOutline, mdiClose,
|
||||
allowedMinutes: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55],
|
||||
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,
|
||||
type: 'normal',
|
||||
page: null,
|
||||
type: this.value.type || 'normal',
|
||||
events: [],
|
||||
frequencies: [
|
||||
{ value: '1w', text: this.$t('event.each_week') },
|
||||
|
@ -118,28 +133,15 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
todayEvents () {
|
||||
const start = dayjs(this.value.from).startOf('day').unix()
|
||||
const end = dayjs(this.value.from).endOf('day').unix()
|
||||
todayEvents() {
|
||||
const start = dayjs.tz(this.value.from).startOf('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)
|
||||
},
|
||||
attributes () {
|
||||
attributes() {
|
||||
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 }
|
||||
const date = dayjs(this.value.from)
|
||||
|
||||
|
@ -183,7 +185,7 @@ export default {
|
|||
return ''
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
async mounted() {
|
||||
if (this.value.multidate) {
|
||||
this.type = 'multidate'
|
||||
} else if (this.value.recurrent) {
|
||||
|
@ -198,10 +200,10 @@ export default {
|
|||
this.events = this.events.filter(e => e.id !== this.event.id)
|
||||
},
|
||||
methods: {
|
||||
updateRecurrent (value) {
|
||||
updateRecurrent(value) {
|
||||
this.$emit('input', { ...this.value, recurrent: value || null })
|
||||
},
|
||||
change (what, value) {
|
||||
change(what, value) {
|
||||
// change event's type
|
||||
if (what === 'type') {
|
||||
if (typeof value === 'undefined') { this.type = 'normal' }
|
||||
|
@ -225,52 +227,59 @@ export default {
|
|||
} else if (what === 'recurrentType') {
|
||||
this.$emit('input', { ...this.value, recurrent: { ...this.value.recurrent, type: value } })
|
||||
} else if (what === 'fromHour') {
|
||||
if (value) {
|
||||
const [hour, minute] = value.split(':')
|
||||
const from = dayjs.tz(this.value.from).hour(hour).minute(minute).second(0)
|
||||
this.$emit('input', { ...this.value, from, fromHour: true })
|
||||
} else {
|
||||
this.$emit('input', { ...this.value, fromHour: false })
|
||||
// if (value) {
|
||||
const [hour, minute] = value ? value.split(':') : [0, 0]
|
||||
let from = dayjs.tz(this.value.from).hour(hour).minute(minute).second(0).toDate()
|
||||
this.$emit('input', { ...this.value, from })
|
||||
if (!value) {
|
||||
this.fromHour = null
|
||||
}
|
||||
// } else {
|
||||
// this.$emit('input', { ...this.value })
|
||||
// }
|
||||
} else if (what === 'dueHour') {
|
||||
if (value) {
|
||||
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
|
||||
let due = dayjs(this.value.from)
|
||||
if (fromHour > Number(hour) && !this.value.multidate) {
|
||||
if (dayjs(this.value.from).hour() > Number(hour) && !this.value.multidate) {
|
||||
due = due.add(1, 'day')
|
||||
}
|
||||
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 {
|
||||
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...)
|
||||
} else if (what === 'date') {
|
||||
if (value === null) {
|
||||
this.$emit('input', { ...this.value, from: null, fromHour: false })
|
||||
this.$emit('input', { ...this.value, from: null, due: null })
|
||||
return
|
||||
}
|
||||
if (this.value.multidate) {
|
||||
let from = value.start
|
||||
let due = value.end
|
||||
if (this.value.fromHour) {
|
||||
from = dayjs.tz(value.start).hour(dayjs.tz(this.value.from).hour())
|
||||
if (this.fromHour) {
|
||||
const [hour, minute] = this.fromHour.split(':')
|
||||
from = dayjs.tz(from).hour(hour).minute(minute).second(0).toDate()
|
||||
}
|
||||
if (this.value.dueHour) {
|
||||
due = dayjs.tz(value.end).hour(dayjs.tz(this.value.due).hour())
|
||||
if (this.dueHour) {
|
||||
const [hour, minute] = this.dueHour.split(':')
|
||||
due = dayjs.tz(due).hour(hour).minute(minute).second(0).toDate()
|
||||
}
|
||||
this.$emit('input', { ...this.value, from, due })
|
||||
} else {
|
||||
let from = value
|
||||
let due = this.value.due
|
||||
if (this.value.fromHour) {
|
||||
from = dayjs.tz(value).hour(dayjs.tz(this.value.from).hour())
|
||||
if (this.fromHour) {
|
||||
const [hour, minute] = this.fromHour.split(':')
|
||||
from = dayjs.tz(value).hour(hour).minute(minute).second(0).toDate()
|
||||
}
|
||||
if (this.value.dueHour && this.value.due) {
|
||||
due = dayjs.tz(value).hour(dayjs.tz(this.value.due).hour())
|
||||
if (this.dueHour) {
|
||||
const [hour, minute] = this.dueHour.split(':')
|
||||
due = dayjs.tz(value).hour(hour).minute(minute).second(0).toDate()
|
||||
}
|
||||
this.$emit('input', { ...this.value, from, due })
|
||||
}
|
||||
|
|
|
@ -182,10 +182,6 @@ export default {
|
|||
margin-top: 4px;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 22px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #FF4500 transparent;
|
||||
scroll-behavior: smooth;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.1em;
|
||||
|
||||
|
@ -231,38 +227,19 @@ export default {
|
|||
// position: absolute;
|
||||
}
|
||||
|
||||
.focused .ProseMirror::after {
|
||||
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);
|
||||
}
|
||||
// .focused .::after {
|
||||
// width: 100%;
|
||||
// }
|
||||
|
||||
&::after {
|
||||
bottom: 0px;
|
||||
content: "";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
height: 0px;
|
||||
transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
|
||||
width: 100%;
|
||||
border-width: 2px 0 0 0;
|
||||
border-style: solid;
|
||||
border-color: #FF4500;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
.ProseMirror {
|
||||
padding: 5px 15px 0px 15px;
|
||||
outline: 0;
|
||||
min-height: 100px;
|
||||
max-height: 350px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #FF4500 transparent;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,61 +3,28 @@ v-card.h-event.event.d-flex(itemscope itemtype="https://schema.org/Event")
|
|||
nuxt-link(:to='`/event/${event.slug || event.id}`' itemprop="url")
|
||||
MyPicture(:event='event' thumb :lazy='lazy')
|
||||
v-icon.float-right.mr-1(v-if='event.parentId' color='success' v-text='mdiRepeat')
|
||||
.title.p-name(itemprop="name") {{event.title}}
|
||||
.title.p-name(itemprop="name") {{ event.title }}
|
||||
|
||||
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 }}
|
||||
.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}}
|
||||
.d-none(itemprop='location.address') {{event.place.address}}
|
||||
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') }}
|
||||
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='address') {{ event.place.address }}
|
||||
|
||||
v-card-actions.pt-0.actions.justify-space-between
|
||||
.tags
|
||||
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}}
|
||||
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 }}
|
||||
|
||||
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>
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import clipboard from '../assets/clipboard'
|
||||
import MyPicture from '~/components/MyPicture'
|
||||
import { mdiRepeat, mdiPencil, mdiDotsVertical, mdiContentCopy,
|
||||
mdiCalendarExport, mdiDeleteForever, mdiCalendar, mdiMapMarker } from '@mdi/js'
|
||||
import { mdiRepeat, mdiCalendar, mdiMapMarker } from '@mdi/js'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return { mdiRepeat, mdiPencil, mdiDotsVertical, mdiContentCopy, mdiCalendarExport,
|
||||
mdiDeleteForever, mdiMapMarker, mdiCalendar }
|
||||
data() {
|
||||
return { mdiRepeat, mdiMapMarker, mdiCalendar }
|
||||
},
|
||||
components: {
|
||||
MyPicture
|
||||
|
@ -66,27 +33,6 @@ export default {
|
|||
event: { type: Object, default: () => ({}) },
|
||||
lazy: Boolean
|
||||
},
|
||||
mixins: [clipboard],
|
||||
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')
|
||||
|
||||
}
|
||||
}
|
||||
computed: mapState(['settings'])
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<template lang='pug'>
|
||||
v-container
|
||||
v-card-title {{$t('common.announcements')}}
|
||||
v-card-title {{ $t('common.announcements') }}
|
||||
v-card-subtitle(v-html="$t('admin.announcement_description')")
|
||||
v-dialog(v-model='dialog' width='800px' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||
v-card
|
||||
v-card-title {{$t('admin.new_announcement')}}
|
||||
v-card-title {{ $t('admin.new_announcement') }}
|
||||
v-card-text.px-0
|
||||
v-form(v-model='valid' ref='announcement' @submit.prevent='save' lazy-validation)
|
||||
v-text-field.col-12(v-model='announcement.title'
|
||||
|
@ -14,10 +14,10 @@ v-container
|
|||
border no-save max-height='400px' :placeholder="$t('common.description')")
|
||||
v-card-actions
|
||||
v-spacer
|
||||
v-btn(@click='dialog=false' color='error') {{$t('common.cancel')}}
|
||||
v-btn(@click='save' color='primary' :disabled='!valid || loading' :loading='loading') {{$t(`common.${editing?'save':'send'}`)}}
|
||||
v-btn(@click='dialog = false' color='error' outlined) {{ $t('common.cancel') }}
|
||||
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-data-table(
|
||||
v-if='announcements.length'
|
||||
|
@ -26,9 +26,9 @@ v-container
|
|||
:items='announcements')
|
||||
template(v-slot:item.actions='{ item }')
|
||||
v-btn(text small @click.stop='toggle(item)'
|
||||
:color='item.visible?"warning":"success"') {{item.visible?$t('common.disable'):$t('common.enable')}}
|
||||
v-btn(text small @click='edit(item)' color='primary') {{$t('common.edit')}}
|
||||
v-btn(text small @click='remove(item)' color='error') {{$t('common.delete')}}
|
||||
:color='item.visible ? "warning" : "success"') {{ item.visible ? $t('common.disable') : $t('common.enable') }}
|
||||
v-btn(text small @click='edit(item)' color='primary') {{ $t('common.edit') }}
|
||||
v-btn(text small @click='remove(item)' color='error') {{ $t('common.delete') }}
|
||||
</template>
|
||||
<script>
|
||||
import { mapActions } from 'vuex'
|
||||
|
@ -39,7 +39,7 @@ import { mdiPlus, mdiChevronRight, mdiChevronLeft } from '@mdi/js'
|
|||
|
||||
export default {
|
||||
components: { Editor, Announcement },
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
mdiPlus, mdiChevronRight, mdiChevronLeft,
|
||||
valid: false,
|
||||
|
@ -54,32 +54,32 @@ export default {
|
|||
announcement: { title: '', announcement: '' }
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
async mounted() {
|
||||
this.announcements = await this.$axios.$get('/announcements')
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setAnnouncements']),
|
||||
edit (announcement) {
|
||||
edit(announcement) {
|
||||
this.announcement.title = announcement.title
|
||||
this.announcement.announcement = announcement.announcement
|
||||
this.announcement.id = announcement.id
|
||||
this.editing = true
|
||||
this.dialog = true
|
||||
},
|
||||
openDialog () {
|
||||
openDialog() {
|
||||
this.announcement = { title: '', announcement: '' }
|
||||
this.dialog = true
|
||||
this.$nextTick(() => this.$refs.announcement.reset())
|
||||
},
|
||||
async toggle (announcement) {
|
||||
async toggle(announcement) {
|
||||
try {
|
||||
announcement.visible = !announcement.visible
|
||||
await this.$axios.$put(`/announcements/${announcement.id}`, announcement)
|
||||
this.announcements = this.announcements.map(a => a.id === announcement.id ? announcement : a)
|
||||
this.setAnnouncements(cloneDeep(this.announcements.filter(a => a.visible)))
|
||||
} catch (e) {}
|
||||
} catch (e) { }
|
||||
},
|
||||
async remove (announcement) {
|
||||
async remove(announcement) {
|
||||
const ret = await this.$root.$confirm('admin.delete_announcement_confirm')
|
||||
if (!ret) { return }
|
||||
this.$axios.delete(`/announcements/${announcement.id}`)
|
||||
|
@ -88,7 +88,7 @@ export default {
|
|||
this.announcements = this.announcements.filter(a => a.id !== announcement.id)
|
||||
})
|
||||
},
|
||||
async save () {
|
||||
async save() {
|
||||
if (!this.$refs.announcement.validate()) { return }
|
||||
this.loading = true
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template lang='pug'>
|
||||
v-container
|
||||
v-card-title {{$t('common.collections')}}
|
||||
v-card-title {{ $t('common.collections') }}
|
||||
v-spacer
|
||||
v-text-field(v-model='search'
|
||||
:append-icon='mdiMagnify' outlined rounded
|
||||
|
@ -8,11 +8,11 @@ v-container
|
|||
single-line hide-details)
|
||||
v-card-subtitle(v-html="$t('admin.collections_description')")
|
||||
|
||||
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-card(color='secondary')
|
||||
v-card-title {{$t('admin.edit_collection')}}
|
||||
v-card
|
||||
v-card-title {{ $t('admin.edit_collection') }}
|
||||
v-card-text
|
||||
v-form(v-model='valid' ref='form' @submit.prevent.native='saveCollection')
|
||||
v-text-field(
|
||||
|
@ -23,7 +23,7 @@ v-container
|
|||
:placeholder='$t("common.name")')
|
||||
template(v-slot:append-outer v-if='!collection.id')
|
||||
v-btn(text @click='saveCollection' color='primary' :loading='loading'
|
||||
:disabled='!valid || loading || !!collection.id') {{$t('common.save')}}
|
||||
:disabled='!valid || loading || !!collection.id') {{ $t('common.save') }}
|
||||
h3(v-else class='text-h5' v-text='collection.name')
|
||||
|
||||
v-row
|
||||
|
@ -39,9 +39,9 @@ v-container
|
|||
:delimiters="[',', ';']"
|
||||
:items="tags"
|
||||
:label="$t('common.tags')")
|
||||
template(v-slot:selection="{ item, on, attrs, selected, parent}")
|
||||
template(v-slot:selection="{ item, on, attrs, selected, parent }")
|
||||
v-chip(v-bind="attrs" close :close-icon='mdiCloseCircle' @click:close='parent.selectItem(item)'
|
||||
:input-value="selected" label small) {{item}}
|
||||
:input-value="selected" label small) {{ item }}
|
||||
|
||||
v-col(cols=5)
|
||||
v-autocomplete(v-model='filterPlaces'
|
||||
|
@ -58,9 +58,9 @@ v-container
|
|||
:delimiters="[',', ';']"
|
||||
:items="places"
|
||||
:label="$t('common.places')")
|
||||
template(v-slot:selection="{ item, on, attrs, selected, parent}")
|
||||
template(v-slot:selection="{ item, on, attrs, selected, parent }")
|
||||
v-chip(v-bind="attrs" close :close-icon='mdiCloseCircle' @click:close='parent.selectItem(item)'
|
||||
:input-value="selected" label small) {{item.name}}
|
||||
:input-value="selected" label small) {{ item.name }}
|
||||
|
||||
//- template(v-slot:item="{ item, attrs, on }")
|
||||
//- v-list-item(v-bind='attrs' v-on='on')
|
||||
|
@ -74,31 +74,31 @@ v-container
|
|||
v-data-table(
|
||||
:headers='filterHeaders'
|
||||
:items='filters'
|
||||
:hide-default-footer='filters.length<5'
|
||||
:hide-default-footer='filters.length < 5'
|
||||
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }')
|
||||
template(v-slot:item.actions='{item}')
|
||||
template(v-slot:item.actions='{ item }')
|
||||
v-btn(@click='removeFilter(item)' color='error' icon)
|
||||
v-icon(v-text='mdiDeleteForever')
|
||||
template(v-slot:item.tags='{item}')
|
||||
template(v-slot:item.tags='{ item }')
|
||||
v-chip.ma-1(small v-for='tag in item.tags' v-text='tag' :key='tag')
|
||||
template(v-slot:item.places='{item}')
|
||||
template(v-slot:item.places='{ item }')
|
||||
v-chip.ma-1(small v-for='place in item.places' v-text='place.name' :key='place.id' )
|
||||
|
||||
|
||||
v-card-actions
|
||||
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-data-table(
|
||||
:headers='collectionHeaders'
|
||||
:items='collections'
|
||||
:hide-default-footer='collections.length<5'
|
||||
:hide-default-footer='collections.length < 5'
|
||||
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||
:search='search')
|
||||
template(v-slot:item.filters='{item}')
|
||||
span {{collectionFilters(item)}}
|
||||
template(v-slot:item.actions='{item}')
|
||||
template(v-slot:item.filters='{ item }')
|
||||
span {{ collectionFilters(item) }}
|
||||
template(v-slot:item.actions='{ item }')
|
||||
v-btn(@click='editCollection(item)' color='primary' icon)
|
||||
v-icon(v-text='mdiPencil')
|
||||
v-btn(@click='removeCollection(item)' color='error' icon)
|
||||
|
@ -111,7 +111,7 @@ import debounce from 'lodash/debounce'
|
|||
import { mdiPencil, mdiChevronLeft, mdiChevronRight, mdiMagnify, mdiPlus, mdiTagMultiple, mdiMapMarker, mdiDeleteForever, mdiCloseCircle } from '@mdi/js'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
mdiPencil, mdiChevronRight, mdiChevronLeft, mdiMagnify, mdiPlus, mdiTagMultiple, mdiMapMarker, mdiDeleteForever, mdiCloseCircle,
|
||||
loading: false,
|
||||
|
@ -139,7 +139,7 @@ export default {
|
|||
]
|
||||
}
|
||||
},
|
||||
async fetch () {
|
||||
async fetch() {
|
||||
this.collections = await this.$axios.$get('/collections?withFilters=true')
|
||||
},
|
||||
|
||||
|
@ -150,14 +150,14 @@ export default {
|
|||
searchPlaces: debounce(async function (ev) {
|
||||
this.places = await this.$axios.$get(`/place?search=${ev.target.value}`)
|
||||
}, 100),
|
||||
collectionFilters (collection) {
|
||||
collectionFilters(collection) {
|
||||
return collection.filters.map(f => {
|
||||
const tags = f.tags?.join(', ')
|
||||
const places = f.places?.map(p => p.name).join(', ')
|
||||
return '(' + (tags && places ? tags + ' - ' + places : tags + places) + ')'
|
||||
}).join(' - ')
|
||||
},
|
||||
async addFilter () {
|
||||
async addFilter() {
|
||||
this.loading = true
|
||||
const tags = this.filterTags
|
||||
const places = this.filterPlaces.map(p => ({ id: p.id, name: p.name }))
|
||||
|
@ -168,17 +168,17 @@ export default {
|
|||
this.filterPlaces = []
|
||||
this.loading = false
|
||||
},
|
||||
async editCollection (collection) {
|
||||
async editCollection(collection) {
|
||||
this.collection = { ...collection }
|
||||
this.filters = await this.$axios.$get(`/filter/${collection.id}`)
|
||||
this.dialog = true
|
||||
},
|
||||
newCollection () {
|
||||
newCollection() {
|
||||
this.collection = { name: '', id: null }
|
||||
this.filters = []
|
||||
this.dialog = true
|
||||
},
|
||||
async saveCollection () {
|
||||
async saveCollection() {
|
||||
if (!this.$refs.form.validate()) return
|
||||
this.loading = true
|
||||
this.collection.name = this.collection.name.trim()
|
||||
|
@ -197,7 +197,7 @@ export default {
|
|||
this.loading = false
|
||||
}
|
||||
},
|
||||
async removeCollection (collection) {
|
||||
async removeCollection(collection) {
|
||||
const ret = await this.$root.$confirm('admin.delete_collection_confirm', { collection: collection.name })
|
||||
if (!ret) { return }
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template lang='pug'>
|
||||
v-container
|
||||
v-card-title {{$t('common.places')}}
|
||||
v-card-title {{ $t('common.places') }}
|
||||
v-spacer
|
||||
v-text-field(v-model='search'
|
||||
:append-icon='mdiMagnify' outlined rounded
|
||||
|
@ -9,8 +9,8 @@ v-container
|
|||
v-card-subtitle(v-html="$t('admin.place_description')")
|
||||
|
||||
v-dialog(v-model='dialog' width='600' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||
v-card(color='secondary')
|
||||
v-card-title {{$t('admin.edit_place')}}
|
||||
v-card
|
||||
v-card-title {{ $t('admin.edit_place') }}
|
||||
v-card-text
|
||||
v-form(v-model='valid' ref='form' lazy-validation)
|
||||
v-text-field(
|
||||
|
@ -27,18 +27,18 @@ v-container
|
|||
|
||||
v-card-actions
|
||||
v-spacer
|
||||
v-btn(@click='dialog=false' color='warning') {{$t('common.cancel')}}
|
||||
v-btn(@click='savePlace' color='primary' :loading='loading'
|
||||
:disable='!valid || loading') {{$t('common.save')}}
|
||||
v-btn(@click='dialog = false' outlined color='warning') {{ $t('common.cancel') }}
|
||||
v-btn(@click='savePlace' color='primary' outlined :loading='loading'
|
||||
:disable='!valid || loading') {{ $t('common.save') }}
|
||||
|
||||
v-card-text
|
||||
v-data-table(
|
||||
:headers='headers'
|
||||
:items='places'
|
||||
:hide-default-footer='places.length<5'
|
||||
:hide-default-footer='places.length < 5'
|
||||
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||
:search='search')
|
||||
template(v-slot:item.actions='{item}')
|
||||
template(v-slot:item.actions='{ item }')
|
||||
v-btn(@click='editPlace(item)' color='primary' icon)
|
||||
v-icon(v-text='mdiPencil')
|
||||
nuxt-link(:to='`/place/${item.name}`')
|
||||
|
@ -49,7 +49,7 @@ v-container
|
|||
import { mdiPencil, mdiChevronLeft, mdiChevronRight, mdiMagnify, mdiEye } from '@mdi/js'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
mdiPencil, mdiChevronRight, mdiChevronLeft, mdiMagnify, mdiEye,
|
||||
loading: false,
|
||||
|
@ -65,17 +65,17 @@ export default {
|
|||
]
|
||||
}
|
||||
},
|
||||
async fetch () {
|
||||
async fetch() {
|
||||
this.places = await this.$axios.$get('/place/all')
|
||||
},
|
||||
methods: {
|
||||
editPlace (item) {
|
||||
editPlace(item) {
|
||||
this.place.name = item.name
|
||||
this.place.address = item.address
|
||||
this.place.id = item.id
|
||||
this.dialog = true
|
||||
},
|
||||
async savePlace () {
|
||||
async savePlace() {
|
||||
if (!this.$refs.form.validate()) return
|
||||
this.loading = true
|
||||
await this.$axios.$put('/place', this.place)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,40 @@ nav_order: 10
|
|||
|
||||
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**:
|
||||
- supported nodejs version >=14 <=16 (nodejs 12 reached End-of-Life on 30 April 2022)
|
||||
- minimum mariadb supported version >= 10.5.2
|
||||
|
|
|
@ -31,11 +31,6 @@ DB=sqlite
|
|||
wget {{site.url}}/docker/$DB/docker-compose.yml
|
||||
```
|
||||
|
||||
Build docker image
|
||||
```bash
|
||||
docker-compose build
|
||||
```
|
||||
|
||||
## Start gancio
|
||||
|
||||
Start your container:
|
||||
|
@ -60,6 +55,5 @@ You'll need to [setup nginx as a proxy]({% link install/nginx.md %}) then you ca
|
|||
|
||||
```bash
|
||||
cd /opt/gancio # or where your installation is
|
||||
wget https://gancio.org/docker/Dockerfile -O Dockerfile
|
||||
docker-compose up -d --no-deps --build
|
||||
docker-compose pull && docker-compose restart
|
||||
```
|
||||
|
|
|
@ -29,6 +29,8 @@ server {
|
|||
|
||||
location @proxy {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
"activate_user": "Bestätigt",
|
||||
"password_updated": "Passwort geändert.",
|
||||
"me": "Du",
|
||||
"disable": "Deaktiviere",
|
||||
"enable": "Aktiviere",
|
||||
"disable": "Ausschalten",
|
||||
"enable": "Einschalten",
|
||||
"cancel": "Abbrechen",
|
||||
"ok": "OK",
|
||||
"new_user": "Neue nutzende Person",
|
||||
|
@ -118,7 +118,7 @@
|
|||
"new_collection": "Bubble anlegen",
|
||||
"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",
|
||||
"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).",
|
||||
"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",
|
||||
|
@ -148,7 +148,7 @@
|
|||
"announcement_remove_ok": "Hinweis entfernt",
|
||||
"instance_block_confirm": "Bestätige, dass du die Instanz {instance} blockieren möchtest?",
|
||||
"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",
|
||||
"allow_recurrent_event": "Wiederkehrende Veranstaltungen erlauben",
|
||||
"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_registration_description": "Möchtest du die Registrierung aktivieren?",
|
||||
"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",
|
||||
"block": "Sperre",
|
||||
"show_resource": "Ressource anzeigen",
|
||||
|
@ -229,7 +229,7 @@
|
|||
"remove_recurrent_confirmation": "Bist du dir sicher, dass du diese wiederkehrende Veranstaltung entfernen möchtest?\nFrühere Veranstaltungen werden beibehalten, aber es werden keine zukünftigen Veranstaltungen mehr hinzugefügt.",
|
||||
"anon_description": "Du kannst eine Veranstaltung hinzufügen, ohne dich zu registrieren oder anzumelden,\nmusst dann aber warten, bis jemand es liest und bestätigt, dass es eine zulässige Veranstaltung ist.\nEs ist nicht möglich, den Eintrag zu verändern.<br/><br/>\nDu kannst dich stattdessen <a href='/login'>anmelden</a> oder <a href='/register'>registrieren</a>. In diesem Fall solltest du so schnell wie möglich eine Antwort erhalten. ",
|
||||
"where_description": "Wo ist die Veranstaltung? Wenn der Ort noch nicht beschrieben wurde, kannst du ihn selbst eintragen.",
|
||||
"follow_me_description": "Eine Möglichkeit, über die hier auf {title} veröffentlichten Veranstaltungen auf dem Laufenden zu bleiben, besteht darin, dem Account <u>{account}</u> aus dem Fediverse zu folgen, zum Beispiel über Mastodon, und von dort aus eventuell Ressourcen für eine Veranstaltung hinzuzufügen.<br/><br/>\nWenn Du noch nie von Mastodon und dem Fediverse gehört hast, empfehlen wir dir, diesen Artikel<a href='https://www.savjee.be/videos/simply-explained/mastodon-and-fediverse-explained/'>zu lesen</a>.<br/><br/>Gib unten deine Instanz ein (z.B. mastodon.social)",
|
||||
"follow_me_description": "Eine Möglichkeit, über die hier auf {title} veröffentlichten Veranstaltungen auf dem Laufenden zu bleiben, besteht darin, dem Account <u>{account}</u> aus dem Fediverse zu folgen, zum Beispiel über Mastodon, und von dort aus eventuell Ressourcen für eine Veranstaltung hinzuzufügen.<br/><br/>\nWenn Du noch nie von Mastodon und dem Fediverse gehört hast, empfehlen wir dir, diesen Artikel <a href='https://www.savjee.be/videos/simply-explained/mastodon-and-fediverse-explained/'> zu lesen</a>.<br/><br/>Gib unten deine Instanz ein (z.B. mastodon.social)",
|
||||
"media_description": "Du kannst (optional) einen Flyer hinzufügen",
|
||||
"edit_recurrent": "Bearbeite eine sich wiederholende Veranstaltung :",
|
||||
"show_recurrent": "regelmäßige Veranstaltungen",
|
||||
|
@ -278,7 +278,7 @@
|
|||
"login": {
|
||||
"ok": "Angemeldet",
|
||||
"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?",
|
||||
"not_registered": "Nicht registriert?",
|
||||
"check_email": "Überprüfe deinen E-Mail-Posteingang und Spam.",
|
||||
|
@ -291,5 +291,5 @@
|
|||
"copy_password_dialog": "Ja, du musst das Passwort kopieren!",
|
||||
"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
1
locales/email/fi.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
1
locales/email/nl.json
Normal file
1
locales/email/nl.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -88,7 +88,8 @@
|
|||
"theme": "Itxura",
|
||||
"tags": "Etiketak",
|
||||
"close": "Itxi",
|
||||
"blobs": "Mordoak"
|
||||
"blobs": "Mordoak",
|
||||
"collections": "Bildumak"
|
||||
},
|
||||
"login": {
|
||||
"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.",
|
||||
"confirmed": "Ekitaldia egiaztatu da",
|
||||
"not_found": "Ezin da ekitaldia aurkitu",
|
||||
"remove_confirmation": "Ziur 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_confirmation": "Ziur al zaude ekitaldi hau ezabatu nahi duzula?",
|
||||
"remove_recurrent_confirmation": "Ziur al zaude ekitaldi errepikari hau ezabatu nahi duzula?\nIragandako ekitaldiak mantenduko dira, baina ez da ekitaldi berririk sortuko.",
|
||||
"recurrent": "Errepikaria",
|
||||
"show_recurrent": "ekitaldi errepikariak",
|
||||
"show_past": "iragandako ekitaldiak ere",
|
||||
|
@ -168,14 +169,14 @@
|
|||
"download_flyer": "Deskargatu eskuorria"
|
||||
},
|
||||
"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",
|
||||
"delete_user": "Ezabatu",
|
||||
"remove_admin": "Kendu administratzaile baimena",
|
||||
"delete_user_confirm": "Ziur al zaude {user} ezabatu nahi duzula?",
|
||||
"user_remove_ok": "Erabiltzailea ezabatu 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_recurrent_event": "Ekitaldi errepikariak ahalbidetu",
|
||||
"recurrent_event_visible": "Erakutsi ekitaldi errepikariak modu lehenetsian",
|
||||
|
@ -195,14 +196,14 @@
|
|||
"hide_resource": "Ezkutatu baliabidea",
|
||||
"show_resource": "Erakutsi 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",
|
||||
"user_blocked": "{user} erabiltzailea blokeatuta dago",
|
||||
"filter_instances": "Iragazi instantziak",
|
||||
"filter_users": "Iragazi erabiltzaileak",
|
||||
"instance_name": "Instantziaren izena",
|
||||
"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?",
|
||||
"announcement_remove_ok": "Iragarpena ezabatu da",
|
||||
"announcement_description": "Atal honetan iragarpenak txertatu ditzakezu hasiera-orrialdean ager daitezen",
|
||||
|
@ -225,7 +226,7 @@
|
|||
"is_dark": "Itxura iluna",
|
||||
"instance_block_confirm": "Ziur al zaude {instance} instantzia blokeatu nahi duzula?",
|
||||
"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",
|
||||
"smtp_test_button": "Bidali probako eposta bat",
|
||||
"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>",
|
||||
"new_blob": "Mordo berria",
|
||||
"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": {
|
||||
"not_confirmed": "Oraindik baieztatu gabe dago…",
|
||||
|
|
1
locales/fi.json
Normal file
1
locales/fi.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"common": {
|
||||
"add_event": "Nuovo evento",
|
||||
"add_event": "Aggiungi evento",
|
||||
"next": "Continua",
|
||||
"export": "Esporta",
|
||||
"send": "Invia",
|
||||
|
|
1
locales/nl.json
Normal file
1
locales/nl.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -66,7 +66,7 @@ module.exports = {
|
|||
if (config.status === 'READY') {
|
||||
try {
|
||||
const Event = require('./server/api/models/event')
|
||||
const events = await Event.findAll({where: { is_visible: true }})
|
||||
const events = await Event.findAll({ where: { is_visible: true } })
|
||||
return events.map(e => `/event/${e.slug}`)
|
||||
} catch (e) {
|
||||
return []
|
||||
|
@ -134,6 +134,18 @@ module.exports = {
|
|||
defaultAssets: false
|
||||
},
|
||||
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,
|
||||
cache: true,
|
||||
hardSource: !isDev,
|
||||
|
|
28
package.json
28
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gancio",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.6",
|
||||
"description": "A shared agenda for local communities",
|
||||
"author": "lesion",
|
||||
"scripts": {
|
||||
|
@ -43,19 +43,19 @@
|
|||
"body-parser": "^1.20.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"dayjs": "^1.11.4",
|
||||
"dayjs": "^1.11.5",
|
||||
"dompurify": "^2.3.10",
|
||||
"email-templates": "^8.0.9",
|
||||
"email-templates": "^10.0.1",
|
||||
"express": "^4.18.1",
|
||||
"express-oauth-server": "lesion/express-oauth-server#master",
|
||||
"http-signature": "^1.3.6",
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"ical.js": "^1.5.0",
|
||||
"ics": "^2.37.0",
|
||||
"ics": "^2.40.0",
|
||||
"jsdom": "^20.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"linkify-html": "^3.0.4",
|
||||
"linkifyjs": "3.0.5",
|
||||
"linkify-html": "^4.0.0",
|
||||
"linkifyjs": "4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mariadb": "^3.0.1",
|
||||
"microformat-node": "^2.0.1",
|
||||
|
@ -63,27 +63,25 @@
|
|||
"mkdirp": "^1.0.4",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^2.3.3",
|
||||
"nuxt-edge": "2.16.0-27616340.013f051b",
|
||||
"pg": "^8.6.0",
|
||||
"sequelize": "^6.21.3",
|
||||
"sequelize-slugify": "^1.6.1",
|
||||
"nuxt-edge": "2.16.0-27720022.54e852f",
|
||||
"pg": "^8.8.0",
|
||||
"sequelize": "^6.23.0",
|
||||
"sequelize-slugify": "^1.6.2",
|
||||
"sharp": "^0.27.2",
|
||||
"sqlite3": "^5.0.11",
|
||||
"tiptap": "^1.32.0",
|
||||
"tiptap-extensions": "^1.35.0",
|
||||
"umzug": "^2.3.0",
|
||||
"v-calendar": "^2.4.1",
|
||||
"vue": "2.7.8",
|
||||
"vue-i18n": "^8.26.7",
|
||||
"vue-template-compiler": "2.7.8",
|
||||
"vuetify": "2.6.8",
|
||||
"winston": "^3.8.1",
|
||||
"vuetify": "2.6.10",
|
||||
"winston": "^3.8.2",
|
||||
"winston-daily-rotate-file": "^4.7.1",
|
||||
"yargs": "^17.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/vuetify": "^1.12.3",
|
||||
"jest": "^28.1.3",
|
||||
"jest": "^29.0.3",
|
||||
"prettier": "^2.7.1",
|
||||
"pug": "^3.0.2",
|
||||
"pug-plain-loader": "^1.1.0",
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
v-container.container.pa-0.pa-md-3
|
||||
v-card
|
||||
v-card-title
|
||||
h4 {{edit?$t('common.edit_event'):$t('common.add_event')}}
|
||||
h4 {{ edit ? $t('common.edit_event') : $t('common.add_event') }}
|
||||
v-spacer
|
||||
v-btn(link text color='primary' @click='openImportDialog=true')
|
||||
<v-icon v-text='mdiFileImport'></v-icon> {{$t('common.import')}}
|
||||
v-btn(link text color='primary' @click='openImportDialog = true')
|
||||
<v-icon v-text='mdiFileImport'></v-icon> {{ $t('common.import') }}
|
||||
v-dialog(v-model='openImportDialog' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||
ImportDialog(@close='openImportDialog=false' @imported='eventImported')
|
||||
ImportDialog(@close='openImportDialog = false' @imported='eventImported')
|
||||
|
||||
v-card-text.px-0.px-xs-2
|
||||
v-form(v-model='valid' ref='form' lazy-validation)
|
||||
|
@ -44,7 +44,7 @@ v-container.container.pa-0.pa-md-3
|
|||
|
||||
//- MEDIA / FLYER / POSTER
|
||||
v-col(cols=12 md=6)
|
||||
MediaInput(v-model='event.media[0]' :event='event' @remove='event.media=[]')
|
||||
MediaInput(v-model='event.media[0]' :event='event' @remove='event.media = []')
|
||||
|
||||
//- tags
|
||||
v-col(cols=12 md=6)
|
||||
|
@ -57,14 +57,14 @@ v-container.container.pa-0.pa-md-3
|
|||
:items="tags"
|
||||
:menu-props="{ maxWidth: 400, eager: true }"
|
||||
:label="$t('common.tags')")
|
||||
template(v-slot:selection="{ item, on, attrs, selected, parent}")
|
||||
template(v-slot:selection="{ item, on, attrs, selected, parent }")
|
||||
v-chip(v-bind="attrs" close :close-icon='mdiCloseCircle' @click:close='parent.selectItem(item)'
|
||||
:input-value="selected" label small) {{item}}
|
||||
:input-value="selected" label small) {{ item }}
|
||||
|
||||
v-card-actions
|
||||
v-spacer
|
||||
v-btn(@click='done' :loading='loading' :disabled='!valid || loading' outlined
|
||||
color='primary') {{edit?$t('common.save'):$t('common.send')}}
|
||||
color='primary') {{ edit ? $t('common.save') : $t('common.send') }}
|
||||
|
||||
</template>
|
||||
<script>
|
||||
|
@ -91,10 +91,10 @@ export default {
|
|||
WhereInput,
|
||||
DateInput
|
||||
},
|
||||
validate ({ store }) {
|
||||
validate({ store }) {
|
||||
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
|
||||
},
|
||||
async asyncData ({ params, $axios, error }) {
|
||||
async asyncData({ params, $axios, error }) {
|
||||
if (params.edit) {
|
||||
const data = { event: { place: {}, media: [] } }
|
||||
data.id = params.edit
|
||||
|
@ -127,7 +127,7 @@ export default {
|
|||
}
|
||||
return {}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
const month = dayjs.tz().month() + 1
|
||||
const year = dayjs.tz().year()
|
||||
return {
|
||||
|
@ -151,26 +151,26 @@ export default {
|
|||
disableAddress: false
|
||||
}
|
||||
},
|
||||
head () {
|
||||
head() {
|
||||
return {
|
||||
title: `${this.settings.title} - ${this.$t('common.add_event')}`
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
filteredTags () {
|
||||
filteredTags() {
|
||||
if (!this.tagName) { return this.tags.slice(0, 10).map(t => t.tag) }
|
||||
const tagName = this.tagName.trim().toLowerCase()
|
||||
return this.tags.filter(t => t.tag.toLowerCase().includes(tagName)).map(t => t.tag)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchTags: debounce( async function(ev) {
|
||||
searchTags: debounce(async function (ev) {
|
||||
const search = ev.target.value
|
||||
if (!search) return
|
||||
this.tags = await this.$axios.$get(`/tag?search=${search}`)
|
||||
}, 100),
|
||||
eventImported (event) {
|
||||
eventImported(event) {
|
||||
this.event = Object.assign(this.event, event)
|
||||
this.$refs.where.selectPlace({ name: event.place.name || event.place, create: true })
|
||||
this.date = {
|
||||
|
@ -183,7 +183,7 @@ export default {
|
|||
}
|
||||
this.openImportDialog = false
|
||||
},
|
||||
async done () {
|
||||
async done() {
|
||||
if (!this.$refs.form.validate()) {
|
||||
this.$nextTick(() => {
|
||||
const el = document.querySelector('.v-input.error--text:first-of-type')
|
||||
|
@ -217,7 +217,7 @@ export default {
|
|||
formData.append('description', this.event.description)
|
||||
formData.append('multidate', !!this.date.multidate)
|
||||
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) {
|
||||
formData.append('id', this.event.id)
|
||||
|
|
|
@ -49,7 +49,7 @@ v-container#event.pa-0.pa-sm-2
|
|||
:href='`/api/event/${event.slug || event.id}.ics`')
|
||||
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')"
|
||||
:href='event | mediaURL')
|
||||
:href='event | mediaURL("download")')
|
||||
v-icon(v-text='mdiFileDownloadOutline')
|
||||
|
||||
.p-description.text-body-1.pa-3.rounded(v-if='hasMedia && event.description' itemprop='description' v-html='event.description')
|
||||
|
|
|
@ -113,7 +113,7 @@ export default {
|
|||
theme: $store.state.settings['theme.is_dark'] ? 'dark' : 'light',
|
||||
sidebar: 'true'
|
||||
},
|
||||
filters: { tags: [], places: [], show_recurrent: false },
|
||||
filters: { tags: [], places: [], show_recurrent: $store.state.settings.recurrent_event_visible },
|
||||
events: []
|
||||
}
|
||||
},
|
||||
|
|
|
@ -16,7 +16,17 @@ v-container.pa-0
|
|||
.col.pt-0.pt-md-2.mt-4.ma-md-0.pb-0
|
||||
//- v-btn(to='/search' color='primary' ) {{$t('common.search')}}
|
||||
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-chip(v-if='selectedDay' close :close-icon='mdiCloseCircle' @click:close='dayChange()') {{selectedDay}}
|
||||
|
||||
|
||||
//- Events
|
||||
#events.mb-2.mt-1.pl-1.pl-sm-2
|
||||
|
@ -29,7 +39,7 @@ import dayjs from 'dayjs'
|
|||
import Event from '@/components/Event'
|
||||
import Announcement from '@/components/Announcement'
|
||||
import Calendar from '@/components/Calendar'
|
||||
import { mdiMagnify } from '@mdi/js'
|
||||
import { mdiMagnify, mdiCloseCircle } from '@mdi/js'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
|
@ -43,9 +53,9 @@ export default {
|
|||
})
|
||||
return { events }
|
||||
},
|
||||
data () {
|
||||
data ({ $store }) {
|
||||
return {
|
||||
mdiMagnify,
|
||||
mdiMagnify, mdiCloseCircle,
|
||||
first: true,
|
||||
isCurrentMonth: true,
|
||||
now: dayjs().unix(),
|
||||
|
@ -53,7 +63,8 @@ export default {
|
|||
events: [],
|
||||
start: dayjs().startOf('month').unix(),
|
||||
end: null,
|
||||
selectedDay: null
|
||||
selectedDay: null,
|
||||
show_recurrent: $store.state.settings.recurrent_event_visible
|
||||
}
|
||||
},
|
||||
head () {
|
||||
|
@ -80,11 +91,11 @@ export default {
|
|||
if (this.selectedDay) {
|
||||
const min = dayjs.tz(this.selectedDay).startOf('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) {
|
||||
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 {
|
||||
return this.events
|
||||
return this.events.filter(e => this.show_recurrent || !e.parentId)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<template>
|
||||
<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>
|
||||
<span class="d-block text-subtitle text-center w-100 mb-14">{{place.address}}</span>
|
||||
<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>
|
||||
|
||||
<!-- Events -->
|
||||
<div class="mb-2 mt-1 pl-1 pl-sm-2" id="events">
|
||||
<Event :event='event' v-for='(event, idx) in events' :lazy='idx>2' :key='event.id'></Event>
|
||||
<Event :event='event' v-for='(event, idx) in events' :lazy='idx > 2' :key='event.id'></Event>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
|
@ -17,7 +19,7 @@ import Event from '@/components/Event'
|
|||
export default {
|
||||
name: 'Place',
|
||||
components: { Event },
|
||||
head () {
|
||||
head() {
|
||||
const title = `${this.settings.title} - ${this.place.name}`
|
||||
return {
|
||||
title,
|
||||
|
@ -28,7 +30,7 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: mapState(['settings']),
|
||||
asyncData ({ $axios, params, error }) {
|
||||
asyncData({ $axios, params, error }) {
|
||||
try {
|
||||
const place = params.place
|
||||
return $axios.$get(`/place/${place}`)
|
||||
|
|
|
@ -42,14 +42,15 @@ export default ({ app, store }) => {
|
|||
// shown in mobile homepage
|
||||
Vue.filter('day', value => dayjs.unix(value).tz().locale(store.state.locale).format('dddd, D MMM'))
|
||||
Vue.filter('mediaURL', (event, type, format = '.jpg') => {
|
||||
const mediaPath = type === 'download' ? '/download/' : '/media/'
|
||||
if (event.media && event.media.length) {
|
||||
if (type === 'alt') {
|
||||
return event.media[0].name
|
||||
} 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') {
|
||||
return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + 'logo.svg'
|
||||
return store.state.settings.baseurl + mediaPath + (type === 'thumb' ? 'thumb/' : '') + 'logo.svg'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
@ -78,27 +79,12 @@ export default ({ app, store }) => {
|
|||
const start = dayjs.unix(event.start_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
|
||||
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
|
||||
return `${start.format('dddd, D MMMM, HH:mm')}-${end.format('HH:mm')}`
|
||||
return `${start.format('dddd D MMMM HH:mm')}-${end.format('HH:mm')}`
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ export default () => {
|
|||
Vue.use(VCalendar, {
|
||||
componentPrefix: 'vc',
|
||||
// why is that ?!
|
||||
firstDayOfWeek: 2
|
||||
// firstDayOfWeek: 2
|
||||
})
|
||||
}
|
||||
|
|
252
preso.md
Normal file
252
preso.md
Normal 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
|
11
release.sh
11
release.sh
|
@ -1,12 +1,15 @@
|
|||
RELEASE=v$(cat package.json | jq ".version" | sed -e 's/"//g')
|
||||
echo "Releasing $RELEASE"
|
||||
rm -fr node_modules
|
||||
yarn
|
||||
yarn build
|
||||
yarn pack
|
||||
yarn publish
|
||||
yarn doc
|
||||
gpg --detach-sign --local-user 5DAC477D5441B7A15ACBF680BBEB4DD39AC6CCA9 gancio-$RELEASE.tgz
|
||||
# yarn publish
|
||||
gpg --pinentry-mode loopback --passphrase `pass underscore/pgp` --detach-sign --local-user 5DAC477D5441B7A15ACBF680BBEB4DD39AC6CCA9 gancio-$RELEASE.tgz
|
||||
cp gancio-$RELEASE.tgz releases/
|
||||
mv gancio-$RELEASE.tgz releases/latest.tgz
|
||||
cp gancio-$RELEASE.tgz.sig releases/
|
||||
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
4
reveal-md.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"separator": "^---",
|
||||
"verticalSeparator": "^--"
|
||||
}
|
5
reveal.json
Normal file
5
reveal.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"controls": true,
|
||||
"progress": true,
|
||||
"transition": "slide"
|
||||
}
|
|
@ -23,18 +23,18 @@ const log = require('../../log')
|
|||
|
||||
const eventController = {
|
||||
|
||||
async searchMeta (req, res) {
|
||||
async searchMeta(req, res) {
|
||||
const search = req.query.search
|
||||
|
||||
const places = await Place.findAll({
|
||||
order: [[Sequelize.col('w'), 'DESC']],
|
||||
where: {
|
||||
[Op.or]: [
|
||||
Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), 'LIKE', '%' + search + '%' ),
|
||||
Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), 'LIKE', '%' + search + '%'),
|
||||
Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('address')), 'LIKE', '%' + search + '%')
|
||||
]
|
||||
},
|
||||
attributes: [['name', 'label'], 'address', 'id', [Sequelize.cast(Sequelize.fn('COUNT', Sequelize.col('events.placeId')),'INTEGER'), 'w']],
|
||||
attributes: [['name', 'label'], 'address', 'id', [Sequelize.cast(Sequelize.fn('COUNT', Sequelize.col('events.placeId')), 'INTEGER'), 'w']],
|
||||
include: [{ model: Event, where: { is_visible: true }, required: true, attributes: [] }],
|
||||
group: ['place.id'],
|
||||
raw: true
|
||||
|
@ -45,7 +45,7 @@ const eventController = {
|
|||
where: {
|
||||
tag: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('tag')), 'LIKE', '%' + search + '%'),
|
||||
},
|
||||
attributes: [['tag','label'], [Sequelize.cast(Sequelize.fn('COUNT', Sequelize.col('tag.tag')), 'INTEGER'), 'w']],
|
||||
attributes: [['tag', 'label'], [Sequelize.cast(Sequelize.fn('COUNT', Sequelize.col('tag.tag')), 'INTEGER'), 'w']],
|
||||
include: [{ model: Event, where: { is_visible: true }, attributes: [], through: { attributes: [] }, required: true }],
|
||||
group: ['tag.tag'],
|
||||
raw: true
|
||||
|
@ -57,13 +57,13 @@ const eventController = {
|
|||
}).concat(tags.map(t => {
|
||||
t.type = 'tag'
|
||||
return t
|
||||
})).sort( (a, b) => b.w - a.w).slice(0, 10)
|
||||
})).sort((a, b) => b.w - a.w).slice(0, 10)
|
||||
|
||||
return res.json(ret)
|
||||
},
|
||||
|
||||
|
||||
async search (req, res) {
|
||||
async search(req, res) {
|
||||
const search = req.query.search.trim().toLocaleLowerCase()
|
||||
const show_recurrent = req.query.show_recurrent || false
|
||||
const end = req.query.end
|
||||
|
@ -129,9 +129,9 @@ const eventController = {
|
|||
|
||||
},
|
||||
|
||||
async getNotifications (event, action) {
|
||||
async getNotifications(event, action) {
|
||||
log.debug(`getNotifications ${event.title} ${action}`)
|
||||
function match (event, filters) {
|
||||
function match(event, filters) {
|
||||
// matches if no filter specified
|
||||
if (!filters) { return true }
|
||||
|
||||
|
@ -170,7 +170,7 @@ const eventController = {
|
|||
})
|
||||
},
|
||||
|
||||
async get (req, res) {
|
||||
async get(req, res) {
|
||||
const format = req.params.format || 'json'
|
||||
const is_admin = res.locals.user && res.locals.user.is_admin
|
||||
const slug = req.params.event_slug
|
||||
|
@ -270,7 +270,7 @@ const eventController = {
|
|||
/** confirm an anonymous event
|
||||
* and send related notifications
|
||||
*/
|
||||
async confirm (req, res) {
|
||||
async confirm(req, res) {
|
||||
const id = Number(req.params.event_id)
|
||||
const event = await Event.findByPk(id, { include: [Place, Tag] })
|
||||
if (!event) {
|
||||
|
@ -299,7 +299,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async unconfirm (req, res) {
|
||||
async unconfirm(req, res) {
|
||||
const id = Number(req.params.event_id)
|
||||
const event = await Event.findByPk(id)
|
||||
if (!event) { return req.sendStatus(404) }
|
||||
|
@ -318,7 +318,7 @@ const eventController = {
|
|||
},
|
||||
|
||||
/** get all unconfirmed events */
|
||||
async getUnconfirmed (_req, res) {
|
||||
async getUnconfirmed(_req, res) {
|
||||
try {
|
||||
const events = await Event.findAll({
|
||||
where: {
|
||||
|
@ -336,7 +336,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async addNotification (req, res) {
|
||||
async addNotification(req, res) {
|
||||
try {
|
||||
const notification = {
|
||||
filters: { is_visible: true },
|
||||
|
@ -351,7 +351,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async delNotification (req, res) {
|
||||
async delNotification(req, res) {
|
||||
const remove_code = req.params.code
|
||||
try {
|
||||
const notification = await Notification.findOne({ where: { remove_code } })
|
||||
|
@ -362,14 +362,14 @@ const eventController = {
|
|||
res.sendStatus(200)
|
||||
},
|
||||
|
||||
async isAnonEventAllowed (_req, res, next) {
|
||||
async isAnonEventAllowed(_req, res, next) {
|
||||
if (!res.locals.settings.allow_anon_event && !res.locals.user) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
next()
|
||||
},
|
||||
|
||||
async add (req, res) {
|
||||
async add(req, res) {
|
||||
// req.err comes from multer streaming error
|
||||
if (req.err) {
|
||||
log.warn(req.err)
|
||||
|
@ -380,7 +380,7 @@ const eventController = {
|
|||
const body = req.body
|
||||
const recurrent = body.recurrent ? JSON.parse(body.recurrent) : null
|
||||
|
||||
const required_fields = [ 'title', 'start_datetime']
|
||||
const required_fields = ['title', 'start_datetime']
|
||||
let missing_field = required_fields.find(required_field => !body[required_field])
|
||||
if (missing_field) {
|
||||
log.warn(`${missing_field} required`)
|
||||
|
@ -398,7 +398,7 @@ const eventController = {
|
|||
if (!body.place_name) {
|
||||
return res.status(400).send(`Place not found`)
|
||||
}
|
||||
place = await Place.findOne({ where: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), Op.eq, body.place_name.trim().toLocaleLowerCase() )})
|
||||
place = await Place.findOne({ where: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), Op.eq, body.place_name.trim().toLocaleLowerCase()) })
|
||||
if (!place) {
|
||||
if (!body.place_address || !body.place_name) {
|
||||
return res.status(400).send(`place_id or place_name and place_address required`)
|
||||
|
@ -479,7 +479,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async update (req, res) {
|
||||
async update(req, res) {
|
||||
if (res.err) {
|
||||
log.warn(req.err)
|
||||
return res.status(400).json(req.err.toString())
|
||||
|
@ -537,7 +537,7 @@ const eventController = {
|
|||
} else if (body.image_focalpoint && event.media.length) {
|
||||
let focalpoint = body.image_focalpoint ? body.image_focalpoint.split(',') : ['0', '0']
|
||||
focalpoint = [parseFloat(parseFloat(focalpoint[0]).toFixed(2)), parseFloat(parseFloat(focalpoint[1]).toFixed(2))]
|
||||
eventDetails.media = [ { ...event.media[0], focalpoint } ] // [0].focalpoint = focalpoint
|
||||
eventDetails.media = [{ ...event.media[0], focalpoint }] // [0].focalpoint = focalpoint
|
||||
}
|
||||
await event.update(eventDetails)
|
||||
|
||||
|
@ -552,7 +552,7 @@ const eventController = {
|
|||
if (!body.place_name) {
|
||||
return res.status(400).send(`Place not found`)
|
||||
}
|
||||
place = await Place.findOne({ where: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), Op.eq, body.place_name.trim().toLocaleLowerCase() )})
|
||||
place = await Place.findOne({ where: Sequelize.where(Sequelize.fn('LOWER', Sequelize.col('name')), Op.eq, body.place_name.trim().toLocaleLowerCase()) })
|
||||
if (!place) {
|
||||
if (!body.place_address || !body.place_name) {
|
||||
return res.status(400).send(`place_id or place_name and place_address required`)
|
||||
|
@ -570,8 +570,8 @@ const eventController = {
|
|||
let tags = []
|
||||
if (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] })
|
||||
newEvent = newEvent.get()
|
||||
|
@ -593,7 +593,7 @@ const eventController = {
|
|||
}
|
||||
},
|
||||
|
||||
async remove (req, res) {
|
||||
async remove(req, res) {
|
||||
const event = await Event.findByPk(req.params.id)
|
||||
// check if event is mine (or user is admin)
|
||||
if (event && (res.locals.user.is_admin || res.locals.user.id === event.userId)) {
|
||||
|
@ -626,7 +626,7 @@ const eventController = {
|
|||
* Method to search for events with pagination and filtering
|
||||
* @returns
|
||||
*/
|
||||
async _select ({
|
||||
async _select({
|
||||
start = dayjs().unix(),
|
||||
end,
|
||||
tags,
|
||||
|
@ -666,7 +666,7 @@ const eventController = {
|
|||
const replacements = []
|
||||
if (tags && places) {
|
||||
where[Op.and] = [
|
||||
{ placeId: places ? places.split(',') : []},
|
||||
{ placeId: places ? places.split(',') : [] },
|
||||
Sequelize.fn('EXISTS', Sequelize.literal(`SELECT 1 FROM event_tags WHERE ${Col('event_tags.eventId')}=${Col('event.id')} AND LOWER(${Col('tagTag')}) in (?)`))
|
||||
]
|
||||
replacements.push(tags)
|
||||
|
@ -688,9 +688,9 @@ const eventController = {
|
|||
const events = await Event.findAll({
|
||||
where,
|
||||
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: [
|
||||
{
|
||||
model: Tag,
|
||||
|
@ -717,14 +717,14 @@ const eventController = {
|
|||
/**
|
||||
* Select events based on params
|
||||
*/
|
||||
async select (req, res) {
|
||||
async select(req, res) {
|
||||
const settings = res.locals.settings
|
||||
const start = req.query.start || dayjs().unix()
|
||||
const end = req.query.end
|
||||
const tags = req.query.tags
|
||||
const places = req.query.places
|
||||
const limit = req.query.max
|
||||
const page = req.query.page = 0
|
||||
const limit = Number(req.query.max) || 0
|
||||
const page = Number(req.query.page) || 0
|
||||
const older = req.query.older || false
|
||||
|
||||
const show_recurrent = settings.allow_recurrent_event &&
|
||||
|
@ -738,7 +738,7 @@ const eventController = {
|
|||
/**
|
||||
* Ensure we have the next instance of a recurrent event
|
||||
*/
|
||||
async _createRecurrentOccurrence (e, startAt) {
|
||||
async _createRecurrentOccurrence(e, startAt) {
|
||||
log.debug(`Create recurrent event [${e.id}] ${e.title}"`)
|
||||
const event = {
|
||||
parentId: e.id,
|
||||
|
@ -798,12 +798,12 @@ const eventController = {
|
|||
/**
|
||||
* Create instances of recurrent events
|
||||
*/
|
||||
async _createRecurrent (start_datetime = dayjs().unix()) {
|
||||
async _createRecurrent(start_datetime = dayjs().unix()) {
|
||||
// select recurrent events and its childs
|
||||
const events = await Event.findAll({
|
||||
where: { is_visible: true, recurrent: { [Op.ne]: null } },
|
||||
include: [{ model: Tag, required: false },
|
||||
{ model: Event, as: 'child', required: false, where: { start_datetime: { [Op.gte]: start_datetime } }}],
|
||||
{ model: Event, as: 'child', required: false, where: { start_datetime: { [Op.gte]: start_datetime } } }],
|
||||
order: [['child', 'start_datetime', 'DESC']]
|
||||
})
|
||||
|
||||
|
@ -811,7 +811,7 @@ const eventController = {
|
|||
const creations = events.map(e => {
|
||||
if (e.child.length) {
|
||||
if (e.child.find(c => c.is_visible)) return
|
||||
return eventController._createRecurrentOccurrence(e, dayjs.unix(e.child[0].start_datetime+1))
|
||||
return eventController._createRecurrentOccurrence(e, dayjs.unix(e.child[0].start_datetime + 1))
|
||||
}
|
||||
return eventController._createRecurrentOccurrence(e, dayjs())
|
||||
})
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
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 path = require('path')
|
||||
const config = require('../../config')
|
||||
|
@ -12,12 +8,12 @@ const settingsController = require('../controller/settings')
|
|||
|
||||
const db = {
|
||||
sequelize: null,
|
||||
close () {
|
||||
close() {
|
||||
if (db.sequelize) {
|
||||
return db.sequelize.close()
|
||||
}
|
||||
},
|
||||
connect (dbConf = config.db) {
|
||||
connect(dbConf = config.db) {
|
||||
dbConf.dialectOptions = { autoJsonMap: true }
|
||||
log.debug(`Connecting to DB: ${JSON.stringify(dbConf)}`)
|
||||
if (dbConf.dialect === 'sqlite') {
|
||||
|
@ -34,7 +30,7 @@ const db = {
|
|||
db.sequelize = new Sequelize(dbConf)
|
||||
return db.sequelize.authenticate()
|
||||
},
|
||||
async isEmpty () {
|
||||
async isEmpty() {
|
||||
try {
|
||||
const users = await db.sequelize.query('SELECT * from users')
|
||||
return !(users && users.length)
|
||||
|
@ -42,7 +38,7 @@ const db = {
|
|||
return true
|
||||
}
|
||||
},
|
||||
async runMigrations () {
|
||||
async runMigrations() {
|
||||
const logging = config.status !== 'READY' ? false : log.debug.bind(log)
|
||||
const umzug = new Umzug({
|
||||
storage: 'sequelize',
|
||||
|
@ -61,7 +57,7 @@ const db = {
|
|||
})
|
||||
return umzug.up()
|
||||
},
|
||||
async initialize () {
|
||||
async initialize() {
|
||||
if (config.status === 'CONFIGURED') {
|
||||
try {
|
||||
await db.connect()
|
||||
|
|
|
@ -48,14 +48,14 @@ domPurify.addHook('beforeSanitizeElements', node => {
|
|||
|
||||
module.exports = {
|
||||
|
||||
randomString (length = 12) {
|
||||
randomString(length = 12) {
|
||||
const wishlist = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||
return Array.from(crypto.randomFillSync(new Uint32Array(length)))
|
||||
.map(x => wishlist[x % wishlist.length])
|
||||
.join('')
|
||||
},
|
||||
|
||||
sanitizeHTML (html) {
|
||||
sanitizeHTML(html) {
|
||||
return domPurify.sanitize(html, {
|
||||
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'br', 'i', 'span',
|
||||
'h6', 'b', 'a', 'li', 'ul', 'ol', 'code', 'blockquote', 'u', 's', 'strong'],
|
||||
|
@ -63,7 +63,7 @@ module.exports = {
|
|||
})
|
||||
},
|
||||
|
||||
async setUserLocale (req, res, next) {
|
||||
async setUserLocale(req, res, next) {
|
||||
// select locale based on cookie? and accept-language header
|
||||
acceptLanguage.languages(Object.keys(locales))
|
||||
res.locals.acceptedLocale = acceptLanguage.get(req.headers['accept-language'])
|
||||
|
@ -71,7 +71,7 @@ module.exports = {
|
|||
next()
|
||||
},
|
||||
|
||||
async initSettings (_req, res, next) {
|
||||
async initSettings(_req, res, next) {
|
||||
// initialize settings
|
||||
res.locals.settings = cloneDeep(settingsController.settings)
|
||||
delete res.locals.settings.smtp
|
||||
|
@ -87,10 +87,17 @@ module.exports = {
|
|||
next()
|
||||
},
|
||||
|
||||
serveStatic () {
|
||||
serveStatic() {
|
||||
const router = express.Router()
|
||||
// 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('/logo.png', (req, res, next) => {
|
||||
|
@ -106,12 +113,12 @@ module.exports = {
|
|||
return router
|
||||
},
|
||||
|
||||
logRequest (req, _res, next) {
|
||||
logRequest(req, _res, next) {
|
||||
log.debug(`${req.method} ${req.path}`)
|
||||
next()
|
||||
},
|
||||
|
||||
col (field) {
|
||||
col(field) {
|
||||
if (config.db.dialect === 'postgres') {
|
||||
return '"' + field.split('.').join('"."') + '"'
|
||||
} else if (config.db.dialect === 'mariadb') {
|
||||
|
@ -121,14 +128,14 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
|
||||
async getImageFromURL (url) {
|
||||
async getImageFromURL(url) {
|
||||
log.debug(`getImageFromURL ${url}`)
|
||||
|
||||
const filename = crypto.randomBytes(16).toString('hex')
|
||||
const sharpStream = sharp({ failOnError: true })
|
||||
const promises = [
|
||||
sharpStream.clone().resize(500, null, { withoutEnlargement: true }).jpeg({ effort: 6, mozjpeg: true }).toFile(path.resolve(config.upload_path, 'thumb', filename + '.jpg')),
|
||||
sharpStream.clone().resize(1200, null, { withoutEnlargement: true } ).jpeg({ quality: 95, effort: 6, mozjpeg: true}).toFile(path.resolve(config.upload_path, filename + '.jpg')),
|
||||
sharpStream.clone().resize(1200, null, { withoutEnlargement: true }).jpeg({ quality: 95, effort: 6, mozjpeg: true }).toFile(path.resolve(config.upload_path, filename + '.jpg')),
|
||||
]
|
||||
|
||||
const response = await axios({ method: 'GET', url: encodeURI(url), responseType: 'stream' })
|
||||
|
@ -157,7 +164,7 @@ module.exports = {
|
|||
* Import events from url
|
||||
* It does supports ICS and H-EVENT
|
||||
*/
|
||||
async importURL (req, res) {
|
||||
async importURL(req, res) {
|
||||
const URL = req.query.URL
|
||||
try {
|
||||
const response = await axios.get(URL)
|
||||
|
@ -210,7 +217,7 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
|
||||
getWeekdayN (date, n, weekday) {
|
||||
getWeekdayN(date, n, weekday) {
|
||||
let cursor
|
||||
if (n === -1) {
|
||||
cursor = date.endOf('month')
|
||||
|
@ -228,7 +235,7 @@ module.exports = {
|
|||
return cursor
|
||||
},
|
||||
|
||||
async APRedirect (req, res, next) {
|
||||
async APRedirect(req, res, next) {
|
||||
const acceptJson = req.accepts('html', 'application/activity+json') === 'application/activity+json'
|
||||
if (acceptJson) {
|
||||
const eventController = require('../server/api/controller/event')
|
||||
|
@ -240,7 +247,7 @@ module.exports = {
|
|||
next()
|
||||
},
|
||||
|
||||
async feedRedirect (req, res, next) {
|
||||
async feedRedirect(req, res, next) {
|
||||
const accepted = req.accepts('html', 'application/rss+xml', 'text/calendar')
|
||||
if (['application/rss+xml', 'text/calendar'].includes(accepted) && /^\/(tag|place|collection)\/.*/.test(req.path)) {
|
||||
return res.redirect((accepted === 'application/rss+xml' ? '/feed/rss' : '/feed/ics') + req.path)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ let token
|
|||
let app
|
||||
let places = []
|
||||
|
||||
beforeAll( async () => {
|
||||
beforeAll(async () => {
|
||||
switch (process.env.DB) {
|
||||
case 'mariadb':
|
||||
process.env.config_path = path.resolve(__dirname, './seeds/config.mariadb.json')
|
||||
|
@ -32,7 +32,7 @@ beforeAll( async () => {
|
|||
await sequelize.query('DELETE FROM filters')
|
||||
})
|
||||
|
||||
afterAll( async () => {
|
||||
afterAll(async () => {
|
||||
await require('../server/initialize.server.js').shutdown(false)
|
||||
})
|
||||
|
||||
|
@ -105,7 +105,7 @@ describe('Settings', () => {
|
|||
test('should retrieve stored array settings', async () => {
|
||||
await request(app).post('/api/settings')
|
||||
.auth(token.access_token, { type: 'bearer' })
|
||||
.send({ key: 'test', value: [1,2,'test'] })
|
||||
.send({ key: 'test', value: [1, 2, 'test'] })
|
||||
.expect(200)
|
||||
|
||||
const response = await request(app)
|
||||
|
@ -114,7 +114,7 @@ describe('Settings', () => {
|
|||
.expect(200)
|
||||
|
||||
expect(response.body.test.length).toBe(3)
|
||||
expect(response.body.test).toStrictEqual([1,2,'test'])
|
||||
expect(response.body.test).toStrictEqual([1, 2, 'test'])
|
||||
})
|
||||
|
||||
|
||||
|
@ -155,7 +155,7 @@ describe('Events', () => {
|
|||
const required_fields = {
|
||||
'title': {},
|
||||
'start_datetime': { title: 'test title' },
|
||||
'place_id or place_name and place_address': { title: 'test title', start_datetime: dayjs().unix()+1000, place_name: 'test place name'},
|
||||
'place_id or place_name and place_address': { title: 'test title', start_datetime: dayjs().unix() + 1000, place_name: 'test place name' },
|
||||
}
|
||||
|
||||
const promises = Object.keys(required_fields).map(async field => {
|
||||
|
@ -179,7 +179,7 @@ describe('Events', () => {
|
|||
.expect(403)
|
||||
|
||||
let response = await request(app).post('/api/event')
|
||||
.send({ title: 'test title 2', place_name: 'place name', place_address: 'address', tags: ['test'], start_datetime: dayjs().unix()+1000 })
|
||||
.send({ title: 'test title 2', place_name: 'place name', place_address: 'address', tags: ['test'], start_datetime: dayjs().unix() + 1000 })
|
||||
.auth(token.access_token, { type: 'bearer' })
|
||||
.expect(200)
|
||||
|
||||
|
@ -192,7 +192,7 @@ describe('Events', () => {
|
|||
.expect(200)
|
||||
|
||||
response = await request(app).post('/api/event')
|
||||
.send({ title: 'test title 3', place_name: 'place name 2', place_address: 'address 2', tags: ['test'], start_datetime: dayjs().unix()+1000 })
|
||||
.send({ title: 'test title 3', place_name: 'place name 2', place_address: 'address 2', tags: ['test'], start_datetime: dayjs().unix() + 1000 })
|
||||
.expect(200)
|
||||
|
||||
expect(response.body.place.id).toBeDefined()
|
||||
|
@ -204,7 +204,7 @@ describe('Events', () => {
|
|||
const event = {
|
||||
title: 'test title 4',
|
||||
place_id: places[0],
|
||||
start_datetime: dayjs().unix()+1000,
|
||||
start_datetime: dayjs().unix() + 1000,
|
||||
tags: [' test tag ']
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,7 @@ let event = {}
|
|||
describe('Tags', () => {
|
||||
test('should create event with tags', async () => {
|
||||
event = await request(app).post('/api/event')
|
||||
.send({ title: 'test tags', place_id: places[1], start_datetime: dayjs().unix()+1000 , tags: ['tag1', 'Tag2', 'tAg3'] })
|
||||
.send({ title: 'test tags', place_id: places[1], start_datetime: dayjs().unix() + 1000, tags: ['tag1', 'Tag2', 'tAg3'] })
|
||||
.auth(token.access_token, { type: 'bearer' })
|
||||
.expect(200)
|
||||
|
||||
|
@ -231,7 +231,7 @@ describe('Tags', () => {
|
|||
|
||||
test('should create event trimming tags / ignore sensitiviness', async () => {
|
||||
const ret = await request(app).post('/api/event')
|
||||
.send({ title: 'test trimming tags', place_id: places[1], start_datetime: dayjs().unix()+1000, tags: ['Tag1', 'taG2 '] })
|
||||
.send({ title: 'test trimming tags', place_id: places[1], start_datetime: dayjs().unix() + 1000, tags: ['Tag1', 'taG2 '] })
|
||||
.auth(token.access_token, { type: 'bearer' })
|
||||
.expect(200)
|
||||
|
||||
|
@ -258,6 +258,17 @@ describe('Tags', () => {
|
|||
// expect(response.body[0].title).toBe('test tags')
|
||||
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', () => {
|
||||
|
@ -293,7 +304,7 @@ describe('Place', () => {
|
|||
|
||||
let collections = []
|
||||
let filters = []
|
||||
describe ('Collection', () => {
|
||||
describe('Collection', () => {
|
||||
test('should not create a new collection if not allowed', () => {
|
||||
return request(app).post('/api/collections')
|
||||
.send({ name: 'test collection' })
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
"serve": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.34",
|
||||
"svelte": "^3.46.3",
|
||||
"vite": "^2.7.11"
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.4",
|
||||
"svelte": "^3.50.0",
|
||||
"vite": "^3.0.9"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
<svelte:options tag="gancio-event" />
|
||||
|
||||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { when } from './helpers'
|
||||
export let baseurl = 'https://demo.gancio.org'
|
||||
export let id
|
||||
|
||||
let mounted = false
|
||||
let event
|
||||
|
||||
function update (id, baseurl) {
|
||||
function update(id, baseurl) {
|
||||
if (mounted) {
|
||||
fetch(`${baseurl}/api/event/${id}`)
|
||||
.then(res => res.json())
|
||||
.then(e => event = e)
|
||||
.then((res) => res.json())
|
||||
.then((e) => (event = e))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,18 +23,6 @@
|
|||
})
|
||||
$: 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) {
|
||||
return `${baseurl}/media/thumb/${event.media[0].url}`
|
||||
}
|
||||
|
@ -43,26 +34,35 @@
|
|||
}
|
||||
return 'center center'
|
||||
}
|
||||
|
||||
</script>
|
||||
<svelte:options tag="gancio-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}
|
||||
<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}
|
||||
<div class="container">
|
||||
<strong>{event.title}</strong>
|
||||
<div>{when(event)}</div>
|
||||
<div class='place'>@{event.place.name}</div>
|
||||
<div class="place">@{event.place.name}</div>
|
||||
</div>
|
||||
</a>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.card {
|
||||
.card {
|
||||
display: block;
|
||||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS',
|
||||
sans-serif;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
||||
transition: 0.3s;
|
||||
border-radius: 5px; /* 5px rounded corners */
|
||||
max-width: 500px;
|
||||
|
@ -70,35 +70,35 @@
|
|||
color: white;
|
||||
background-color: #1e1e1e;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add rounded corners to the top left and the top right corner of the image */
|
||||
img {
|
||||
/* Add rounded corners to the top left and the top right corner of the image */
|
||||
img {
|
||||
border-radius: 5px 5px 0 0;
|
||||
max-height: 250px;
|
||||
min-height: 160px;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
}
|
||||
}
|
||||
|
||||
.card:hover .container {
|
||||
.card:hover .container {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* On mouse-over, add a deeper shadow */
|
||||
.card:hover {
|
||||
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
/* On mouse-over, add a deeper shadow */
|
||||
.card:hover {
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Add some padding inside the card container */
|
||||
.container {
|
||||
transition: padding-left .2s;
|
||||
/* Add some padding inside the card container */
|
||||
.container {
|
||||
transition: padding-left 0.2s;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.place {
|
||||
.place {
|
||||
font-weight: 600;
|
||||
color: #ff6e40;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<script>
|
||||
<svelte:options tag="gancio-events" />
|
||||
|
||||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import { when } from './helpers'
|
||||
export let baseurl = ''
|
||||
export let title = ''
|
||||
export let maxlength = false
|
||||
export let tags = ''
|
||||
export let places = ''
|
||||
export let theme = 'light'
|
||||
export let show_recurrent=false
|
||||
export let show_recurrent = false
|
||||
export let sidebar = 'true'
|
||||
|
||||
export let external_style = ''
|
||||
|
@ -15,14 +17,14 @@
|
|||
let mounted = false
|
||||
let events = []
|
||||
|
||||
function update (v) {
|
||||
function update(v) {
|
||||
if (!mounted) return
|
||||
const params = []
|
||||
if (maxlength) {
|
||||
params.push(`max=${maxlength}`)
|
||||
}
|
||||
|
||||
if(tags) {
|
||||
if (tags) {
|
||||
params.push(`tags=${tags}`)
|
||||
}
|
||||
|
||||
|
@ -30,168 +32,181 @@
|
|||
params.push(`places=${places}`)
|
||||
}
|
||||
|
||||
params.push(`show_recurrent=${show_recurrent?'true':'false'}`)
|
||||
params.push(`show_recurrent=${show_recurrent ? 'true' : 'false'}`)
|
||||
|
||||
fetch(`${baseurl}/api/events?${params.join('&')}`)
|
||||
.then(res => res.json())
|
||||
.then(e => {
|
||||
.then((res) => res.json())
|
||||
.then((e) => {
|
||||
events = e
|
||||
})
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
console.error('Error loading Gancio API -> ', e)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function position(event) {
|
||||
if (event.media[0].focalpoint) {
|
||||
if (event.media && event.media[0].focalpoint) {
|
||||
const focalpoint = event.media[0].focalpoint
|
||||
return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%`
|
||||
}
|
||||
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(() => {
|
||||
mounted = true
|
||||
update()
|
||||
})
|
||||
$: update(maxlength && title && places && tags && theme && show_recurrent && sidebar)
|
||||
|
||||
$: update(
|
||||
maxlength && title && places && tags && theme && show_recurrent && sidebar
|
||||
)
|
||||
</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}
|
||||
<div id='gancioEvents'
|
||||
class:dark="{theme === 'dark'}" class:light="{theme === 'light'}"
|
||||
class:sidebar="{sidebar === 'true'}" class:nosidebar="{sidebar !== 'true'}">
|
||||
<div
|
||||
id="gancioEvents"
|
||||
class:dark={theme === 'dark'}
|
||||
class:light={theme === 'light'}
|
||||
class:sidebar={sidebar === 'true'}
|
||||
class:nosidebar={sidebar !== 'true'}
|
||||
>
|
||||
{#if title && sidebar === 'true'}
|
||||
<a href='{baseurl}' target='_blank' id='header'>
|
||||
<div class='content'>
|
||||
<div class='title'>{title}</div>
|
||||
<img id='logo' alt='logo' src='{baseurl}/logo.png'/>
|
||||
<a href={baseurl} target="_blank" id="header">
|
||||
<div class="content">
|
||||
<div class="title">{title}</div>
|
||||
<img id="logo" alt="logo" src="{baseurl}/logo.png" />
|
||||
</div>
|
||||
</a>
|
||||
{/if}
|
||||
{#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'}
|
||||
<div class='img'>
|
||||
<div class="img">
|
||||
{#if event.media.length}
|
||||
<img style="object-position: {position(event)}; aspect-ratio=1.7778;"
|
||||
alt="{event.media[0].name}"
|
||||
src="{baseurl + '/media/thumb/' + event.media[0].url}" loading='lazy'/>
|
||||
<img
|
||||
style="object-position: {position(event)}; aspect-ratio=1.7778;"
|
||||
alt={event.media[0].name}
|
||||
src={baseurl + '/media/thumb/' + event.media[0].url}
|
||||
loading="lazy"
|
||||
/>
|
||||
{:else}
|
||||
<img style="aspect-ratio=1.7778;"
|
||||
alt="{event.title}"
|
||||
src="{baseurl + '/noimg.svg'}" loading='lazy'/>
|
||||
<img
|
||||
style="aspect-ratio=1.7778;"
|
||||
alt={event.title}
|
||||
src={baseurl + '/noimg.svg'}
|
||||
loading="lazy"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class='content'>
|
||||
<div class='subtitle'>
|
||||
{when(event.start_datetime)}
|
||||
<div class="content">
|
||||
<div class="subtitle">
|
||||
{when(event)}
|
||||
</div>
|
||||
<div class='title'>
|
||||
<div class="title">
|
||||
{event.title}
|
||||
</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}
|
||||
<div class='tags'>
|
||||
<div class="tags">
|
||||
{#each event.tags as tag}
|
||||
<span class='tag'>#{tag}</span>
|
||||
<span class="tag">#{tag}</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
#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";
|
||||
#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';
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
box-sizing: content-box;
|
||||
margin: 0 auto;
|
||||
font-size: 1rem;
|
||||
}
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.nosidebar {
|
||||
.nosidebar {
|
||||
max-width: 1200px;
|
||||
}
|
||||
}
|
||||
|
||||
#header{
|
||||
#header {
|
||||
padding: 1.2rem 1rem;
|
||||
background-color: var(--bg-odd-color);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
.sidebar {
|
||||
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;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.event .img {
|
||||
.event .img {
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
max-height: 250px;
|
||||
aspect-ratio: 1.7778;
|
||||
flex: 1 0 auto;
|
||||
/* height: 100%; */
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: 800px) {
|
||||
.event {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.event .img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
.event img {
|
||||
}
|
||||
.event img {
|
||||
object-fit: cover;
|
||||
border-radius: 15px;
|
||||
width: 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 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.nosidebar .content {
|
||||
.nosidebar .content {
|
||||
margin-left: 1rem;
|
||||
margin-top: 5px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
.tags {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
#logo {
|
||||
#logo {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
|
@ -199,77 +214,77 @@ a {
|
|||
margin: 0;
|
||||
line-height: 1.275rem;
|
||||
font-weight: 400;
|
||||
font-size: .875rem;
|
||||
font-size: 0.875rem;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover .title,
|
||||
a:focus .title,
|
||||
a:active .title {
|
||||
text-decoration:underline;
|
||||
}
|
||||
a:hover .title,
|
||||
a:focus .title,
|
||||
a:active .title {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.dark {
|
||||
--bg-odd-color: #161616;
|
||||
--bg-even-color: #222;
|
||||
--bg-hover-color: #333;
|
||||
--text-color: white;
|
||||
--title-color: white;
|
||||
--line-color: rgba(120, 120, 120, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.light {
|
||||
.light {
|
||||
--bg-odd-color: #f5f5f5;
|
||||
--bg-even-color: #FAFAFA;
|
||||
--bg-hover-color: #EEE;
|
||||
--bg-even-color: #fafafa;
|
||||
--bg-hover-color: #eee;
|
||||
--text-color: #222;
|
||||
--title-color: black;
|
||||
--line-color: rgba(220, 220, 220, 0.9);
|
||||
}
|
||||
.sidebar a {
|
||||
}
|
||||
.sidebar a {
|
||||
background-color: var(--bg-even-color);
|
||||
border-bottom:1px solid var(--line-color);
|
||||
}
|
||||
border-bottom: 1px solid var(--line-color);
|
||||
}
|
||||
|
||||
.sidebar a:hover,
|
||||
.sidebar a:focus,
|
||||
.sidebar a:active {
|
||||
.sidebar a:hover,
|
||||
.sidebar a:focus,
|
||||
.sidebar a:active {
|
||||
background-color: var(--bg-hover-color);
|
||||
padding-left: 15px;
|
||||
padding-right:25px;
|
||||
}
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.place {
|
||||
.place {
|
||||
font-weight: 400;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.4rem;
|
||||
color: orangered;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
.title {
|
||||
color: var(--title-color);
|
||||
font-weight: bold;
|
||||
font-size: 1.3rem;
|
||||
line-height: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
.nosidebar .title {
|
||||
.nosidebar .title {
|
||||
font-size: 1.9em;
|
||||
line-height: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
line-height: 1.1em;
|
||||
color: var(--title-color);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.tag {
|
||||
.tag {
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
24
webcomponents/src/helpers.js
Normal file
24
webcomponents/src/helpers.js
Normal 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') : '')
|
||||
)
|
||||
}
|
|
@ -2,157 +2,169 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@rollup/pluginutils@^4.1.2":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.0.tgz#a14bbd058fdbba0a5647143b16ed0d86fb60bd08"
|
||||
integrity sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA==
|
||||
"@esbuild/linux-loong64@0.14.54":
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
|
||||
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:
|
||||
estree-walker "^2.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@sveltejs/vite-plugin-svelte@^1.0.0-next.34":
|
||||
version "1.0.0-next.39"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.0-next.39.tgz#b9437de18d13a475f76cf603511174cdf905b8d4"
|
||||
integrity sha512-gnvvcAW2LK+KnUn8lKb2ypcXKwSp2K57mem5C4VNKfjxdRpM6+XwNavWwVf6otnDhz3qPYl/TKKW6/dRr6eeAw==
|
||||
"@sveltejs/vite-plugin-svelte@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-1.0.4.tgz#363a0adeb9221c35abb65197c6db0754b9994a08"
|
||||
integrity sha512-UZco2fdj0OVuRWC0SUJjEOftITc2IeHLFJNp00ym9MuQ9dShnlO4P29G8KUxRlcS7kSpzHuko6eCR9MOALj7lQ==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^4.1.2"
|
||||
debug "^4.3.3"
|
||||
kleur "^4.1.4"
|
||||
magic-string "^0.25.7"
|
||||
svelte-hmr "^0.14.9"
|
||||
"@rollup/pluginutils" "^4.2.1"
|
||||
debug "^4.3.4"
|
||||
deepmerge "^4.2.2"
|
||||
kleur "^4.1.5"
|
||||
magic-string "^0.26.2"
|
||||
svelte-hmr "^0.14.12"
|
||||
|
||||
debug@^4.3.3:
|
||||
version "4.3.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
|
||||
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
|
||||
debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
esbuild-android-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz#d532d38cb5fe0ae45167ce35f4bbc784c636be40"
|
||||
integrity sha512-L5vCUk7TzFbBnoESNoXjU3x9+/+7TDIE/1mTfy/erAfvZAqC+S3sp/Qa9wkypFMcFvN9FzvESkTlpeQDolREtQ==
|
||||
deepmerge@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
||||
|
||||
esbuild-android-arm64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz#9c5bb3366aabfd14a1c726d36978b79441dfcb6e"
|
||||
integrity sha512-4jv5xPjM/qNm27T5j3ZEck0PvjgQtoMHnz4FzwF5zNP56PvY2CT0WStcAIl6jNlsuDdN63rk2HRBIsO6xFbcFw==
|
||||
esbuild-android-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
|
||||
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
|
||||
|
||||
esbuild-darwin-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz#05dcdb6d884f427039ffee5e92ff97527e56c26d"
|
||||
integrity sha512-TGp8tuudIxOyWd1+8aYPxQmC1ZQyvij/AfNBa35RubixD0zJ1vkKHVAzo0Zao1zcG6pNqiSyzfPto8vmg0s7oA==
|
||||
esbuild-android-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
|
||||
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
|
||||
|
||||
esbuild-darwin-arm64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz#28e080da4ea0cfe9498071e7f8060498caee1a95"
|
||||
integrity sha512-oTcDgdm0MDVEmw2DWu8BV68pYuImpFgvWREPErBZmNA4MYKGuBRaCiJqq6jZmBR1x+3y1DWCjez+5uLtuAm6mw==
|
||||
esbuild-darwin-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
|
||||
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
|
||||
|
||||
esbuild-freebsd-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz#200d3664a3b945bc9fdcba73614b49a11ebd1cfa"
|
||||
integrity sha512-ueAqbnMZ8arnuLH8tHwTCQYeptnHOUV7vA6px6j4zjjQwDx7TdP7kACPf3TLZLdJQ3CAD1XCvQ2sPhX+8tacvQ==
|
||||
esbuild-darwin-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
|
||||
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
|
||||
|
||||
esbuild-freebsd-arm64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz#624b08c5da6013bdc312aaa23c4ff409580f5c3c"
|
||||
integrity sha512-+ZVWud2HKh+Ob6k/qiJWjBtUg4KmJGGmbvEXXW1SNKS7hW7HU+Zq2ZCcE1akFxOPkVB+EhOty/sSek30tkCYug==
|
||||
esbuild-freebsd-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
|
||||
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
|
||||
|
||||
esbuild-linux-32@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz#0238e597eb0b60aa06c7e98fccbbfd6bb9a0d6c5"
|
||||
integrity sha512-3OP/lwV3kCzEz45tobH9nj+uE4ubhGsfx+tn0L26WAGtUbmmcRpqy7XRG/qK7h1mClZ+eguIANcQntYMdYklfw==
|
||||
esbuild-freebsd-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
|
||||
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
|
||||
|
||||
esbuild-linux-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz#8a8b8cf47dfce127c858e71229d9a385a82c62e8"
|
||||
integrity sha512-+aKHdHZmX9qwVlQmu5xYXh7GsBFf4TWrePgeJTalhXHOG7NNuUwoHmketGiZEoNsWyyqwH9rE5BC+iwcLY30Ug==
|
||||
esbuild-linux-32@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
|
||||
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
|
||||
|
||||
esbuild-linux-arm64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz#7ac94371418a2640ba413bc1700aaedeb2794e52"
|
||||
integrity sha512-UxfenPx/wSZx55gScCImPtXekvZQLI2GW3qe5dtlmU7luiqhp5GWPzGeQEbD3yN3xg/pHc671m5bma5Ns7lBHw==
|
||||
esbuild-linux-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
|
||||
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
|
||||
|
||||
esbuild-linux-arm@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz#034bd18e9310b9f010c89f90ef7f05706689600b"
|
||||
integrity sha512-aTLcE2VBoLydL943REcAcgnDi3bHtmULSXWLbjtBdtykRatJVSxKMjK9YlBXUZC4/YcNQfH7AxwVeQr9fNxPhw==
|
||||
esbuild-linux-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
|
||||
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
|
||||
|
||||
esbuild-linux-mips64le@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz#05f98a8cf6b578eab6b4e6b0ab094f37530934f4"
|
||||
integrity sha512-wLWYyqVfYx9Ur6eU5RT92yJVsaBGi5RdkoWqRHOqcJ38Kn60QMlcghsKeWfe9jcYut8LangYZ98xO1LxIoSXrQ==
|
||||
esbuild-linux-arm@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
|
||||
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
|
||||
|
||||
esbuild-linux-ppc64le@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz#46fd0add8d8535678439d7a9c2876ad20042d952"
|
||||
integrity sha512-0dR6Csl6Zas3g4p9ULckEl8Mo8IInJh33VCJ3eaV1hj9+MHGdmDOakYMN8MZP9/5nl+NU/0ygpd14cWgy8uqRw==
|
||||
esbuild-linux-mips64le@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
|
||||
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
|
||||
|
||||
esbuild-linux-riscv64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz#ea2e986f0f3e5df73c635135dd778051734fc605"
|
||||
integrity sha512-J4d20HDmTrgvhR0bdkDhvvJGaikH3LzXQnNaseo8rcw9Yqby9A90gKUmWpfwqLVNRILvNnAmKLfBjCKU9ajg8w==
|
||||
esbuild-linux-ppc64le@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
|
||||
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
|
||||
|
||||
esbuild-linux-s390x@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz#efe89486e9a1b1508925048076e3f3a6698aa6a3"
|
||||
integrity sha512-YI2d5V6nTE73ZnhEKQD7MtsPs1EtUZJ3obS21oxQxGbbRw1G+PtJKjNyur+3t6nzHP9oTg6GHQ3S3hOLLmbDIQ==
|
||||
esbuild-linux-riscv64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
|
||||
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
|
||||
|
||||
esbuild-netbsd-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz#439fe27d8ee3b5887501ee63988e85f920107db6"
|
||||
integrity sha512-TKIVgNWLUOkr+Exrye70XTEE1lJjdQXdM4tAXRzfHE9iBA7LXWcNtVIuSnphTqpanPzTDFarF0yqq4kpbC6miA==
|
||||
esbuild-linux-s390x@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
|
||||
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
|
||||
|
||||
esbuild-openbsd-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz#31ebf616aadf6e60674469f2b92cec92280d9930"
|
||||
integrity sha512-QgFJ37A15D7NIXBTYEqz29+uw3nNBOIyog+3kFidANn6kjw0GHZ0lEYQn+cwjyzu94WobR+fes7cTl/ZYlHb1A==
|
||||
esbuild-netbsd-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
|
||||
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
|
||||
|
||||
esbuild-sunos-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz#815e4f936d74970292a63ccfd5791fe5e3569f5f"
|
||||
integrity sha512-rmWfjUItYIVlqr5EnTH1+GCxXiBOC42WBZ3w++qh7n2cS9Xo0lO5pGSG2N+huOU2fX5L+6YUuJ78/vOYvefeFw==
|
||||
esbuild-openbsd-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
|
||||
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
|
||||
|
||||
esbuild-windows-32@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz#189e14df2478f2c193c86968ab1fb54e1ceaafd2"
|
||||
integrity sha512-HGAxVUofl3iUIz9W10Y9XKtD0bNsK9fBXv1D55N/ljNvkrAYcGB8YCm0v7DjlwtyS6ws3dkdQyXadbxkbzaKOA==
|
||||
esbuild-sunos-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
|
||||
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
|
||||
|
||||
esbuild-windows-64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz#3d5fbfdc3856850bb47439299e3b60dd18be111f"
|
||||
integrity sha512-TirEohRkfWU9hXLgoDxzhMQD1g8I2mOqvdQF2RS9E/wbkORTAqJHyh7wqGRCQAwNzdNXdg3JAyhQ9/177AadWA==
|
||||
esbuild-windows-32@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
|
||||
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
|
||||
|
||||
esbuild-windows-arm64@0.14.25:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz#8b243cbbad8a86cf98697da9ccb88c05df2ef458"
|
||||
integrity sha512-4ype9ERiI45rSh+R8qUoBtaj6kJvUOI7oVLhKqPEpcF4Pa5PpT3hm/mXAyotJHREkHpM87PAJcA442mLnbtlNA==
|
||||
esbuild-windows-64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
|
||||
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
|
||||
|
||||
esbuild@^0.14.14:
|
||||
version "0.14.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.25.tgz#ddb9d47b91ca76abb7d850ce3dfed0bc3dc88d16"
|
||||
integrity sha512-4JHEIOMNFvK09ziiL+iVmldIhLbn49V4NAVo888tcGFKedEZY/Y8YapfStJ6zSE23tzYPKxqKwQBnQoIO0BI/Q==
|
||||
esbuild-windows-arm64@0.14.54:
|
||||
version "0.14.54"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
|
||||
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:
|
||||
esbuild-android-64 "0.14.25"
|
||||
esbuild-android-arm64 "0.14.25"
|
||||
esbuild-darwin-64 "0.14.25"
|
||||
esbuild-darwin-arm64 "0.14.25"
|
||||
esbuild-freebsd-64 "0.14.25"
|
||||
esbuild-freebsd-arm64 "0.14.25"
|
||||
esbuild-linux-32 "0.14.25"
|
||||
esbuild-linux-64 "0.14.25"
|
||||
esbuild-linux-arm "0.14.25"
|
||||
esbuild-linux-arm64 "0.14.25"
|
||||
esbuild-linux-mips64le "0.14.25"
|
||||
esbuild-linux-ppc64le "0.14.25"
|
||||
esbuild-linux-riscv64 "0.14.25"
|
||||
esbuild-linux-s390x "0.14.25"
|
||||
esbuild-netbsd-64 "0.14.25"
|
||||
esbuild-openbsd-64 "0.14.25"
|
||||
esbuild-sunos-64 "0.14.25"
|
||||
esbuild-windows-32 "0.14.25"
|
||||
esbuild-windows-64 "0.14.25"
|
||||
esbuild-windows-arm64 "0.14.25"
|
||||
"@esbuild/linux-loong64" "0.14.54"
|
||||
esbuild-android-64 "0.14.54"
|
||||
esbuild-android-arm64 "0.14.54"
|
||||
esbuild-darwin-64 "0.14.54"
|
||||
esbuild-darwin-arm64 "0.14.54"
|
||||
esbuild-freebsd-64 "0.14.54"
|
||||
esbuild-freebsd-arm64 "0.14.54"
|
||||
esbuild-linux-32 "0.14.54"
|
||||
esbuild-linux-64 "0.14.54"
|
||||
esbuild-linux-arm "0.14.54"
|
||||
esbuild-linux-arm64 "0.14.54"
|
||||
esbuild-linux-mips64le "0.14.54"
|
||||
esbuild-linux-ppc64le "0.14.54"
|
||||
esbuild-linux-riscv64 "0.14.54"
|
||||
esbuild-linux-s390x "0.14.54"
|
||||
esbuild-netbsd-64 "0.14.54"
|
||||
esbuild-openbsd-64 "0.14.54"
|
||||
esbuild-sunos-64 "0.14.54"
|
||||
esbuild-windows-32 "0.14.54"
|
||||
esbuild-windows-64 "0.14.54"
|
||||
esbuild-windows-arm64 "0.14.54"
|
||||
|
||||
estree-walker@^2.0.1:
|
||||
version "2.0.2"
|
||||
|
@ -176,22 +188,22 @@ has@^1.0.3:
|
|||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
is-core-module@^2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
|
||||
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
|
||||
is-core-module@^2.9.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
|
||||
integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
kleur@^4.1.4:
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
|
||||
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
|
||||
kleur@^4.1.5:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
|
||||
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
|
||||
|
||||
magic-string@^0.25.7:
|
||||
version "0.25.9"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
|
||||
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
|
||||
magic-string@^0.26.2:
|
||||
version "0.26.3"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.3.tgz#25840b875140f7b4785ab06bddc384270b7dd452"
|
||||
integrity sha512-u1Po0NDyFcwdg2nzHT88wSK0+Rih0N1M+Ph1Sp08k8yvFFU3KR72wryS7e1qMPJypt99WB7fIFVCA92mQrMjrg==
|
||||
dependencies:
|
||||
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"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
nanoid@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
|
||||
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
|
||||
nanoid@^3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||
|
||||
path-parse@^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"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
postcss@^8.4.6:
|
||||
version "8.4.8"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032"
|
||||
integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ==
|
||||
postcss@^8.4.16:
|
||||
version "8.4.16"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
|
||||
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
|
||||
dependencies:
|
||||
nanoid "^3.3.1"
|
||||
nanoid "^3.3.4"
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
resolve@^1.22.0:
|
||||
version "1.22.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
|
||||
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
|
||||
resolve@^1.22.1:
|
||||
version "1.22.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
|
||||
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
|
||||
dependencies:
|
||||
is-core-module "^2.8.1"
|
||||
is-core-module "^2.9.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
rollup@^2.59.0:
|
||||
version "2.70.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.0.tgz#17a92e5938e92a251b962352e904c9f558230ec7"
|
||||
integrity sha512-iEzYw+syFxQ0X9RefVwhr8BA2TNJsTaX8L8dhyeyMECDbmiba+8UQzcu+xZdji0+JQ+s7kouQnw+9Oz5M19XKA==
|
||||
"rollup@>=2.75.6 <2.77.0 || ~2.77.0":
|
||||
version "2.77.3"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
|
||||
integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
|
||||
optionalDependencies:
|
||||
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"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
svelte-hmr@^0.14.9:
|
||||
version "0.14.11"
|
||||
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.11.tgz#63d532dc9c2c849ab708592f034765fa2502e568"
|
||||
integrity sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ==
|
||||
svelte-hmr@^0.14.12:
|
||||
version "0.14.12"
|
||||
resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.14.12.tgz#a127aec02f1896500b10148b2d4d21ddde39973f"
|
||||
integrity sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==
|
||||
|
||||
svelte@^3.46.3:
|
||||
version "3.46.4"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.46.4.tgz#0c46bc4a3e20a2617a1b7dc43a722f9d6c084a38"
|
||||
integrity sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==
|
||||
svelte@^3.50.0:
|
||||
version "3.50.0"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.50.0.tgz#d11a7a6bd1e084ec051d55104a9af8bccf54461f"
|
||||
integrity sha512-zXeOUDS7+85i+RxLN+0iB6PMbGH7OhEgjETcD1fD8ZrhuhNFxYxYEHU41xuhkHIulJavcu3PKbPyuCrBxdxskQ==
|
||||
|
||||
vite@^2.7.11:
|
||||
version "2.8.6"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-2.8.6.tgz#32d50e23c99ca31b26b8ccdc78b1d72d4d7323d3"
|
||||
integrity sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug==
|
||||
vite@^3.0.9:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.9.tgz#45fac22c2a5290a970f23d66c1aef56a04be8a30"
|
||||
integrity sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==
|
||||
dependencies:
|
||||
esbuild "^0.14.14"
|
||||
postcss "^8.4.6"
|
||||
resolve "^1.22.0"
|
||||
rollup "^2.59.0"
|
||||
esbuild "^0.14.47"
|
||||
postcss "^8.4.16"
|
||||
resolve "^1.22.1"
|
||||
rollup ">=2.75.6 <2.77.0 || ~2.77.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue