Merge calendar and subscription list
Signed-off-by: Georg Ehrke <developer@georgehrke.com> Alternative design with calendar-icon Signed-off-by: Georg Ehrke <developer@georgehrke.com> Move new calendar to bottom of list Signed-off-by: Georg Ehrke <developer@georgehrke.com> Reset new calendar menu on close Signed-off-by: Georg Ehrke <developer@georgehrke.com> use actions instead of counter Signed-off-by: Georg Ehrke <developer@georgehrke.com>
This commit is contained in:
parent
a203bd6150
commit
578d3b36a4
|
@ -206,6 +206,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
.app-navigation-entry-new-calendar {
|
||||
.app-navigation-entry__title {
|
||||
color: var(--color-text-maxcontrast) !important;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&--open {
|
||||
.app-navigation-entry__title {
|
||||
color: var(--color-text-light) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.action-item:not(.action-item--open) {
|
||||
.action-item__menutoggle:not(:hover):not(:focus):not(:active) {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
ul {
|
||||
|
||||
// Calendar list items / Subscription list items
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
@include icon-black-white('eye', 'calendar', 4);
|
||||
@include icon-black-white('invitees-no-response', 'calendar', 5);
|
||||
@include icon-black-white('leftarrow', 'calendar', 2);
|
||||
@include icon-black-white('new-calendar', 'calendar', 2);
|
||||
@include icon-black-white('new-calendar-with-task-list', 'calendar', 2);
|
||||
@include icon-black-white('random', 'calendar', 1);
|
||||
@include icon-black-white('reminder', 'calendar', 4);
|
||||
@include icon-black-white('reminder-audio', 'calendar', 1);
|
||||
|
|
|
@ -39,3 +39,13 @@
|
|||
- Created by: Google
|
||||
- License: Apache License version 2.0
|
||||
- Link: https://material.io/resources/icons/?search=view_&icon=view_week&style=baseline
|
||||
|
||||
## new-calendar.svg
|
||||
- Created by: Austin Andrews
|
||||
- License: Apache License version 2.0
|
||||
- Link: https://materialdesignicons.com/icon/calendar-blank
|
||||
|
||||
## new-calendar-with-task-list.svg
|
||||
- Created by: Google
|
||||
- License: Apache License version 2.0
|
||||
- Link: https://materialdesignicons.com/icon/calendar-check
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 19H5V8h14m0-5h-1V1h-2v2H8V1H6v2H5L3 5v14a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2m-2 8l-2-1-4 5-3-2-1 1 4 3 6-6z"/></svg>
|
After Width: | Height: | Size: 196 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 19H5V8h14m-3-7v2H8V1H6v2H5L3 5v14a2 2 0 002 2h14a2 2 0 002-2V5l-2-2h-1V1"/></svg>
|
After Width: | Height: | Size: 155 B |
|
@ -24,30 +24,22 @@
|
|||
id="calendars-list"
|
||||
name="list"
|
||||
tag="ul">
|
||||
<CalendarListNew
|
||||
:key="newCalendarKey"
|
||||
:disabled="loadingCalendars" />
|
||||
<AppNavigationSpacer :key="spacerKey" />
|
||||
<CalendarListItemLoadingPlaceholder v-if="loadingCalendars" :key="loadingKeyCalendars" />
|
||||
<CalendarListItem
|
||||
v-for="calendar in calendars"
|
||||
:key="calendar.id"
|
||||
:calendar="calendar" />
|
||||
|
||||
<AppNavigationSpacer
|
||||
:key="spacerKey" />
|
||||
|
||||
<SubscriptionListNew :key="newSubscriptionKey" :disabled="loadingCalendars" />
|
||||
<CalendarListItemLoadingPlaceholder v-if="loadingCalendars" :key="loadingKeySubscriptions" />
|
||||
<CalendarListItem
|
||||
v-for="calendar in subscriptions"
|
||||
v-for="calendar in allCalendars"
|
||||
:key="calendar.id"
|
||||
:calendar="calendar" />
|
||||
<CalendarListNew
|
||||
v-if="!loadingCalendars"
|
||||
:key="newCalendarKey"
|
||||
:disabled="loadingCalendars" />
|
||||
</transition-group>
|
||||
<transition-group v-else
|
||||
id="calendars-list"
|
||||
name="list"
|
||||
tag="ul">
|
||||
<CalendarListItemLoadingPlaceholder v-if="loadingCalendars" :key="loadingKeySubscriptions" />
|
||||
<CalendarListItemLoadingPlaceholder v-if="loadingCalendars" :key="loadingKeyCalendars" />
|
||||
<PublicCalendarListItem
|
||||
v-for="calendar in subscriptions"
|
||||
:key="calendar.id"
|
||||
|
@ -56,26 +48,22 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
AppNavigationSpacer
|
||||
} from '@nextcloud/vue'
|
||||
import {
|
||||
mapGetters
|
||||
} from 'vuex'
|
||||
import { AppNavigationSpacer } from '@nextcloud/vue'
|
||||
import CalendarListNew from './CalendarList/CalendarListNew.vue'
|
||||
import CalendarListItem from './CalendarList/CalendarListItem.vue'
|
||||
import PublicCalendarListItem from './CalendarList/PublicCalendarListItem.vue'
|
||||
import CalendarListItemLoadingPlaceholder from './CalendarList/CalendarListItemLoadingPlaceholder.vue'
|
||||
import CalendarListNew from './CalendarList/CalendarListNew.vue'
|
||||
import SubscriptionListNew from './CalendarList/SubscriptionListNew.vue'
|
||||
|
||||
export default {
|
||||
name: 'CalendarList',
|
||||
components: {
|
||||
AppNavigationSpacer,
|
||||
CalendarListNew,
|
||||
CalendarListItem,
|
||||
CalendarListItemLoadingPlaceholder,
|
||||
CalendarListNew,
|
||||
SubscriptionListNew,
|
||||
PublicCalendarListItem
|
||||
},
|
||||
props: {
|
||||
|
@ -90,7 +78,7 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
calendars: 'sortedCalendars',
|
||||
allCalendars: 'sortedCalendarsSubscriptions',
|
||||
subscriptions: 'sortedSubscriptions'
|
||||
}),
|
||||
newCalendarKey() {
|
||||
|
@ -99,12 +87,6 @@ export default {
|
|||
loadingKeyCalendars() {
|
||||
return this._uid + '-loading-placeholder-calendars'
|
||||
},
|
||||
loadingKeySubscriptions() {
|
||||
return this._uid + '-loading-placeholder-subscriptions'
|
||||
},
|
||||
newSubscriptionKey() {
|
||||
return this._uid + '-new-subscription'
|
||||
},
|
||||
spacerKey() {
|
||||
return this._uid + '-spacer'
|
||||
}
|
||||
|
|
|
@ -21,95 +21,259 @@
|
|||
|
||||
<template>
|
||||
<AppNavigationItem
|
||||
v-if="!showForm"
|
||||
icon="icon-add"
|
||||
:class="{disabled: disabled}"
|
||||
:title="$t('calendar', 'New calendar')"
|
||||
@click.prevent.stop="openDialog" />
|
||||
class="app-navigation-entry-new-calendar"
|
||||
:class="{'app-navigation-entry-new-calendar--open': isOpen}"
|
||||
:title="$t('calendar', '+ New calendar')"
|
||||
:menu-open.sync="isOpen"
|
||||
menu-icon="icon-add"
|
||||
@click.prevent.stop="toggleDialog">
|
||||
<template slot="actions">
|
||||
<ActionButton
|
||||
v-if="showCreateCalendarLabel"
|
||||
icon="icon-new-calendar"
|
||||
@click.prevent.stop="openCreateCalendarInput">
|
||||
{{ $t('calendar', 'New calendar') }}
|
||||
</ActionButton>
|
||||
<ActionInput
|
||||
v-if="showCreateCalendarInput"
|
||||
icon="icon-new-calendar"
|
||||
@submit.prevent.stop="createNewCalendar" />
|
||||
<ActionText
|
||||
v-if="showCreateCalendarSaving"
|
||||
icon="icon-loading-small">
|
||||
{{ $t('calendar', 'Creating calendar ...') }}
|
||||
</ActionText>
|
||||
|
||||
<ActionInput
|
||||
v-else
|
||||
v-click-outside="closeNewCalendarForm"
|
||||
:icon="inputIcon"
|
||||
:value="name"
|
||||
:disabled="isCreating"
|
||||
@submit.prevent.stop="addCalendar">
|
||||
{{ $t('calendar', 'Name of calendar') }}
|
||||
</ActionInput>
|
||||
<ActionButton
|
||||
v-if="showCreateCalendarTaskListLabel"
|
||||
icon="icon-new-calendar-with-task-list"
|
||||
@click.prevent.stop="openCreateCalendarTaskListInput">
|
||||
{{ $t('calendar', 'New calendar with task list') }}
|
||||
</ActionButton>
|
||||
<ActionInput
|
||||
v-if="showCreateCalendarTaskListInput"
|
||||
icon="icon-new-calendar-with-task-list"
|
||||
@submit.prevent.stop="createNewCalendarTaskList" />
|
||||
<ActionText
|
||||
v-if="showCreateCalendarTaskListSaving"
|
||||
icon="icon-loading-small">
|
||||
{{ $t('calendar', 'Creating calendar ...') }}
|
||||
</ActionText>
|
||||
|
||||
<ActionButton
|
||||
v-if="showCreateSubscriptionLabel"
|
||||
icon="icon-public"
|
||||
@click.prevent.stop="openCreateSubscriptionInput">
|
||||
{{ $t('calendar', 'New subscription from link') }}
|
||||
</ActionButton>
|
||||
<ActionInput
|
||||
v-if="showCreateSubscriptionInput"
|
||||
icon="icon-public"
|
||||
@submit.prevent.stop="createNewSubscription" />
|
||||
<ActionText
|
||||
v-if="showCreateSubscriptionSaving"
|
||||
icon="icon-loading-small">
|
||||
{{ $t('calendar', 'Creating subscription ...') }}
|
||||
</ActionText>
|
||||
</template>
|
||||
</AppNavigationItem>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
AppNavigationItem,
|
||||
ActionInput
|
||||
} from '@nextcloud/vue'
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
import { ActionButton } from '@nextcloud/vue/dist/Components/ActionButton'
|
||||
import { ActionInput } from '@nextcloud/vue/dist/Components/ActionInput'
|
||||
import { ActionText } from '@nextcloud/vue/dist/Components/ActionText'
|
||||
import { AppNavigationItem } from '@nextcloud/vue/dist/Components/AppNavigationItem'
|
||||
|
||||
import { getRandomColor } from '../../../utils/color.js'
|
||||
|
||||
export default {
|
||||
name: 'CalendarListNew',
|
||||
components: {
|
||||
AppNavigationItem,
|
||||
ActionInput
|
||||
},
|
||||
directives: {
|
||||
ClickOutside
|
||||
},
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
ActionButton,
|
||||
ActionInput,
|
||||
ActionText,
|
||||
AppNavigationItem
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
isCreating: false,
|
||||
showForm: false,
|
||||
name: ''
|
||||
// Open state
|
||||
isOpen: false,
|
||||
// New calendar
|
||||
showCreateCalendarLabel: true,
|
||||
showCreateCalendarInput: false,
|
||||
showCreateCalendarSaving: false,
|
||||
// New calendar with task list
|
||||
showCreateCalendarTaskListLabel: true,
|
||||
showCreateCalendarTaskListInput: false,
|
||||
showCreateCalendarTaskListSaving: false,
|
||||
// New subscription
|
||||
showCreateSubscriptionLabel: true,
|
||||
showCreateSubscriptionInput: false,
|
||||
showCreateSubscriptionSaving: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
inputIcon() {
|
||||
if (this.isCreating) {
|
||||
return 'icon-loading-small'
|
||||
watch: {
|
||||
isOpen() {
|
||||
if (this.isOpen) {
|
||||
return
|
||||
}
|
||||
|
||||
return 'icon-add'
|
||||
this.closeMenu()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openDialog() {
|
||||
if (this.disabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.showForm = true
|
||||
this.$nextTick(() => {
|
||||
this.$el.querySelector('input[type=text]').focus()
|
||||
})
|
||||
/**
|
||||
* Opens the Actions menu when clicking on the main item label
|
||||
*/
|
||||
toggleDialog() {
|
||||
this.isOpen = !this.isOpen
|
||||
},
|
||||
addCalendar(event) {
|
||||
/**
|
||||
* Opens the create calendar input
|
||||
*/
|
||||
openCreateCalendarInput() {
|
||||
this.showCreateCalendarLabel = false
|
||||
this.showCreateCalendarInput = true
|
||||
this.showCreateCalendarSaving = false
|
||||
|
||||
this.showCreateCalendarTaskListLabel = true
|
||||
this.showCreateCalendarTaskListInput = false
|
||||
|
||||
this.showCreateSubscriptionLabel = true
|
||||
this.showCreateSubscriptionInput = false
|
||||
},
|
||||
/**
|
||||
* Opens the create calendar with task list input
|
||||
*/
|
||||
openCreateCalendarTaskListInput() {
|
||||
this.showCreateCalendarTaskListLabel = false
|
||||
this.showCreateCalendarTaskListInput = true
|
||||
this.showCreateCalendarTaskListSaving = false
|
||||
|
||||
this.showCreateCalendarLabel = true
|
||||
this.showCreateCalendarInput = false
|
||||
|
||||
this.showCreateSubscriptionLabel = true
|
||||
this.showCreateSubscriptionInput = false
|
||||
},
|
||||
/**
|
||||
* Opens the create subscription input
|
||||
*/
|
||||
openCreateSubscriptionInput() {
|
||||
this.showCreateSubscriptionLabel = false
|
||||
this.showCreateSubscriptionInput = true
|
||||
this.showCreateSubscriptionSaving = false
|
||||
|
||||
this.showCreateCalendarLabel = true
|
||||
this.showCreateCalendarInput = false
|
||||
|
||||
this.showCreateCalendarTaskListLabel = true
|
||||
this.showCreateCalendarTaskListInput = false
|
||||
},
|
||||
/**
|
||||
* Creates a new calendar
|
||||
*
|
||||
* @param {Event} event The submit event
|
||||
*/
|
||||
async createNewCalendar(event) {
|
||||
this.showCreateCalendarInput = false
|
||||
this.showCreateCalendarSaving = true
|
||||
|
||||
const displayName = event.target.querySelector('input[type=text]').value
|
||||
|
||||
// Keep displayname visible while saving
|
||||
this.name = displayName
|
||||
this.isCreating = true
|
||||
|
||||
this.$store.dispatch('appendCalendar', { displayName, color: getRandomColor() }) // TODO - use uid2color
|
||||
.then(() => {
|
||||
this.showForm = false
|
||||
this.isCreating = false
|
||||
this.name = ''
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
this.$toast.error(this.$t('calendar', 'An error occurred, unable to create the calendar.'))
|
||||
this.isCreating = false
|
||||
try {
|
||||
await this.$store.dispatch('appendCalendar', {
|
||||
displayName,
|
||||
color: getRandomColor() // TODO - use uid2color
|
||||
})
|
||||
} catch (error) {
|
||||
console.debug(error)
|
||||
this.$toast.error(this.$t('calendar', 'An error occurred, unable to create the calendar.'))
|
||||
} finally {
|
||||
this.showCreateCalendarSaving = false
|
||||
this.showCreateCalendarLabel = true
|
||||
this.isOpen = false
|
||||
this.closeMenu()
|
||||
}
|
||||
},
|
||||
closeNewCalendarForm() {
|
||||
this.showForm = false
|
||||
this.name = ''
|
||||
/**
|
||||
* Creates a new calendar with task list
|
||||
*
|
||||
* @param {Event} event The submit event
|
||||
*/
|
||||
async createNewCalendarTaskList(event) {
|
||||
this.showCreateCalendarTaskListInput = false
|
||||
this.showCreateCalendarTaskListSaving = true
|
||||
|
||||
const displayName = event.target.querySelector('input[type=text]').value
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('appendCalendar', {
|
||||
displayName,
|
||||
color: getRandomColor(), // TODO - uid2color
|
||||
components: ['VEVENT', 'VTODO']
|
||||
})
|
||||
} catch (error) {
|
||||
console.debug(error)
|
||||
this.$toast.error(this.$t('calendar', 'An error occurred, unable to create the calendar.'))
|
||||
} finally {
|
||||
this.showCreateCalendarTaskListSaving = false
|
||||
this.showCreateCalendarTaskListLabel = true
|
||||
this.isOpen = false
|
||||
this.closeMenu()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Creates a new subscription
|
||||
*
|
||||
* @param {Event} event The submit event
|
||||
*/
|
||||
async createNewSubscription(event) {
|
||||
this.showCreateSubscriptionInput = false
|
||||
this.showCreateSubscriptionSaving = true
|
||||
|
||||
const link = event.target.querySelector('input[type=text]').value
|
||||
let url
|
||||
let hostname
|
||||
try {
|
||||
url = new URL(link)
|
||||
hostname = url.hostname
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
this.$toast.error(this.$t('calendar', 'Please enter a valid link (starting with http://, https://, webcal://, or webcals://)'))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('appendSubscription', {
|
||||
displayName: hostname,
|
||||
color: getRandomColor(), // TODO - uid2color
|
||||
source: link
|
||||
})
|
||||
} catch (error) {
|
||||
console.debug(error)
|
||||
this.$toast.error(this.$t('calendar', 'An error occurred, unable to create the calendar.'))
|
||||
} finally {
|
||||
this.showCreateSubscriptionSaving = false
|
||||
this.showCreateSubscriptionLabel = true
|
||||
this.isOpen = false
|
||||
this.closeMenu()
|
||||
}
|
||||
},
|
||||
/**
|
||||
* This resets the actions on close of menu
|
||||
*/
|
||||
closeMenu() {
|
||||
this.showCreateCalendarLabel = true
|
||||
this.showCreateCalendarInput = false
|
||||
this.showCreateCalendarSaving = false
|
||||
this.showCreateCalendarTaskListLabel = true
|
||||
this.showCreateCalendarTaskListInput = false
|
||||
this.showCreateCalendarTaskListSaving = false
|
||||
this.showCreateSubscriptionLabel = true
|
||||
this.showCreateSubscriptionInput = false
|
||||
this.showCreateSubscriptionSaving = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
|
||||
- @author Georg Ehrke <oc.list@georgehrke.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<AppNavigationItem
|
||||
v-if="!showForm"
|
||||
icon="icon-add"
|
||||
:class="{disabled: disabled}"
|
||||
:title="$t('calendar', 'New subscription')"
|
||||
@click.prevent.stop="openDialog" />
|
||||
|
||||
<ActionInput
|
||||
v-else
|
||||
v-click-outside="closeNewCalendarForm"
|
||||
:icon="inputIcon"
|
||||
:value="link"
|
||||
:disabled="isCreating"
|
||||
@submit.prevent.stop="addCalendar">
|
||||
{{ $t('calendar', 'Link to iCal') }}
|
||||
</ActionInput>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
AppNavigationItem,
|
||||
ActionInput
|
||||
} from '@nextcloud/vue'
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
import { getRandomColor } from '../../../utils/color.js'
|
||||
|
||||
export default {
|
||||
name: 'SubscriptionListNew',
|
||||
components: {
|
||||
AppNavigationItem,
|
||||
ActionInput
|
||||
},
|
||||
directives: {
|
||||
ClickOutside
|
||||
},
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
link: '',
|
||||
isCreating: false,
|
||||
showForm: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
inputIcon() {
|
||||
if (this.isCreating) {
|
||||
return 'icon-loading-small'
|
||||
}
|
||||
|
||||
return 'icon-add'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openDialog() {
|
||||
if (this.disabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.showForm = true
|
||||
this.$nextTick(() => {
|
||||
this.$el.querySelector('input[type=text]').focus()
|
||||
})
|
||||
},
|
||||
addCalendar(event) {
|
||||
const link = event.target.querySelector('input[type=text]').value
|
||||
|
||||
// Keep link visible while saving
|
||||
this.link = link
|
||||
this.isCreating = true
|
||||
|
||||
let url
|
||||
let hostname
|
||||
try {
|
||||
url = new URL(link)
|
||||
hostname = url.hostname
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
this.$toast.error(this.$t('calendar', 'Please enter a valid link (starting with http://, https://, webcal://, or webcals://)'))
|
||||
this.link = ''
|
||||
this.isCreating = false
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.dispatch('appendSubscription', { displayName: hostname, color: getRandomColor(), source: link }) // TODO - use uid2color
|
||||
.then(() => {
|
||||
this.displayName = ''
|
||||
this.showForm = false
|
||||
this.isCreating = false
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
this.$toast.error(this.$t('calendar', 'An error occurred, unable to create the calendar.'))
|
||||
this.isCreating = false
|
||||
})
|
||||
},
|
||||
closeNewCalendarForm() {
|
||||
this.showForm = false
|
||||
this.link = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue