Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 2 additions & 18 deletions src/components/AppNavigation/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,12 @@
import { getDefaultAlarms } from '@/defaults/defaultAlarmProvider.js'
import alarmFormat from '@/filters/alarmFormat.js'
import {
IMPORT_STAGE_DEFAULT,

Check failure on line 188 in src/components/AppNavigation/Settings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'IMPORT_STAGE_DEFAULT' is defined but never used
IMPORT_STAGE_IMPORTING,

Check failure on line 189 in src/components/AppNavigation/Settings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'IMPORT_STAGE_IMPORTING' is defined but never used
IMPORT_STAGE_PROCESSING,

Check failure on line 190 in src/components/AppNavigation/Settings.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'IMPORT_STAGE_PROCESSING' is defined but never used
} from '@/models/consts.js'
import useCalendarsStore from '@/store/calendars.js'
import useImportFilesStore from '@/store/importFiles.js'
import useImportStore from '@/store/import.ts'
import usePrincipalsStore from '@/store/principals.js'
import useSettingsStore from '@/store/settings.js'
import {
Expand Down Expand Up @@ -254,7 +254,7 @@
},

computed: {
...mapStores(useSettingsStore, useCalendarsStore, useImportFilesStore, usePrincipalsStore),
...mapStores(useSettingsStore, useCalendarsStore, useImportStore, usePrincipalsStore),
...mapState(useSettingsStore, [
'eventLimit',
'showTasks',
Expand Down Expand Up @@ -283,26 +283,10 @@
return isAfterVersion(34)
},

files() {
return this.importFilesStore.importFiles
},

hasBirthdayCalendar() {
return !!this.calendarsStore.getBirthdayCalendar
},

showUploadButton() {
return this.importStateStore.importState.stage === IMPORT_STAGE_DEFAULT
},

showImportModal() {
return this.importStateStore.importState.stage === IMPORT_STAGE_PROCESSING
},

showProgressBar() {
return this.importStateStore.importState.stage === IMPORT_STAGE_IMPORTING
},

settingsTitle() {
return this.$t('calendar', 'Calendar settings')
},
Expand Down
122 changes: 90 additions & 32 deletions src/components/AppNavigation/Settings/ImportScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,118 @@
<template>
<Modal
class="import-modal"
size="large"
size="normal"
:name="$t('calendar', 'Import destination selection')"
@close="cancelImport">
<h2 class="import-modal__title">
{{ $t('calendar', 'Import calendars') }}
</h2>
<h4 class="import-modal__subtitle">
<!-- eslint-disable-next-line no-irregular-whitespace -->
{{ $t('calendar', 'Please select a calendar to import into …') }}
</h4>

<transition-group class="import-modal__file-list" tag="ul">
<li key="import-header-row" class="import-modal-file-item import-modal-file-item--header">
<div class="import-modal-file-item__filename">
{{ $t('calendar', 'Filename') }}
</div>
<div class="import-modal-file-item__calendar-select">
{{ $t('calendar', 'Calendar to import into') }}
</div>
</li>
<ImportScreenRow v-for="(file, index) in files" :key="`import-file-${index}`" :file="file" />
</transition-group>

<div class="import-modal__actions">
<NcButton @click="cancelImport">
{{ $t('calendar', 'Cancel') }}
</NcButton>
<NcButton class="primary" @click="importCalendar">
{{ $n('calendar', 'Import calendar', 'Import calendars', files.length) }}
</NcButton>
</div>
<template v-if="isSelecting">
<transition-group class="import-modal__file-list" tag="ul">
<ImportScreenRow v-for="entry in entries" :key="`import-file-${entry.file.id}`" :entry="entry" />
</transition-group>

<div class="import-modal__actions">
<NcButton @click="cancelImport">
{{ $t('calendar', 'Cancel') }}
</NcButton>
<NcButton variant="primary" @click="importCalendar">
<template #icon>
<Upload :size="20" />
</template>
{{ $n('calendar', 'Import calendar', 'Import calendars', entries.length) }}
</NcButton>
</div>
</template>

<template v-else>
<h4 class="import-modal__title">
{{ activeFileLabel }}
</h4>

<NcProgressBar class="import-modal__progress-bar" :value="progressValue" size="medium" />

<div class="import-modal__counters">
<NcNoteCard type="info">
<strong>{{ totals.discovered }}</strong> {{ $t('calendar', 'Discovered') }}
</NcNoteCard>
<NcNoteCard type="info">
<strong>{{ totals.processed }}</strong> {{ $t('calendar', 'Processed') }}
</NcNoteCard>
<NcNoteCard type="success">
<strong>{{ totals.created }}</strong> {{ $t('calendar', 'Created') }}
</NcNoteCard>
<NcNoteCard type="info">
<strong>{{ totals.updated }}</strong> {{ $t('calendar', 'Updated') }}
</NcNoteCard>
<NcNoteCard type="warning">
<strong>{{ totals.exists }}</strong> {{ $t('calendar', 'Skipped') }}
</NcNoteCard>
<NcNoteCard type="error">
<strong>{{ totals.error }}</strong> {{ $t('calendar', 'Errors') }}
</NcNoteCard>
</div>
</template>
</Modal>
</template>

<script>
import { NcModal as Modal, NcButton } from '@nextcloud/vue'
import { NcModal as Modal, NcButton, NcNoteCard, NcProgressBar } from '@nextcloud/vue'
import Upload from 'vue-material-design-icons/TrayArrowUp.vue'
import ImportScreenRow from '@/components/AppNavigation/Settings/ImportScreenRow.vue'

export default {
name: 'ImportScreen',
components: {
NcButton,
NcNoteCard,
NcProgressBar,
ImportScreenRow,
Modal,
Upload,
},

props: {
files: {
entries: {
type: Array,
required: true,
},

stage: {
type: String,
required: true,
},

totals: {
type: Object,
required: true,
},

activeSession: {
type: Object,
default: null,
},
},

emits: ['importCalendar', 'cancelImport'],
emits: ['cancelImport', 'importCalendar'],

computed: {
isSelecting() {
return this.stage === 'selecting'
},

progressValue() {
return Math.round(this.totals.processed / Math.max(this.totals.discovered, 1) * 100)
},

activeFileLabel() {
if (!this.activeSession) {
return this.$t('calendar', 'Preparing import…')
}

return this.$t('calendar', 'Importing {fileName} into {calendar}', {
fileName: this.activeSession.fileName,
calendar: this.activeSession.targetDisplayName || this.$t('calendar', 'selected calendar'),
})
},
},

methods: {
importCalendar() {
Expand Down
135 changes: 116 additions & 19 deletions src/components/AppNavigation/Settings/ImportScreenRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,118 @@

<template>
<li class="import-modal-file-item">
<div class="import-modal-file-item__filename">
<div>{{ file.name }}</div>
<div
v-if="disabledHint"
class="import-modal-file-item__calendar-disabled-hint">
{{ disabledHint }}
</div>
</div>
<CalendarPicker
class="import-modal-file-item__calendar-select"
:value="calendar"
:calendars="calendars"
:isCalendarSelectable="isCalendarSelectable"
@selectCalendar="selectCalendar" />
<NcFormGroup :label="$t('calendar', 'Importing file')">
<NcFormBox v-slot="{ itemClass }">
<NcFormBoxCopyButton :class="itemClass" :label="$t('calendar', 'File')" :value="file.name" />
</NcFormBox>
<NcFormBox v-slot="{ itemClass }">
<CalendarPicker
:class="itemClass"
:inputLabel="$t('calendar', 'Calendar to import into')"
:value="calendar"
:calendars="calendars"
:isCalendarSelectable="isCalendarSelectable"
@selectCalendar="selectCalendar" />
<p v-if="disabledHint" class="import-modal-file-item__calendar-hint">
{{ disabledHint }}
</p>
</NcFormBox>
<NcFormBox v-slot="{ itemClass }">
<NcSelect
v-model="selectedFormat"
:class="itemClass"
:inputId="formatInputId"
:inputLabel="$t('calendar', 'Filetype')"
:options="formatOptions"
:allowEmpty="false"
:clearable="false"
label="label" />
</NcFormBox>
<NcFormBox v-slot="{ itemClass }">
<NcFormBoxSwitch
:class="itemClass"
:modelValue="supersede"
:label="$t('calendar', 'Overwrite existing events')"
:description="$t('calendar', 'Replace events in the calendar that match the imported ones instead of skipping them.')"
@update:modelValue="setSupersede" />
</NcFormBox>
</NcFormGroup>
</li>
</template>

<script>
import { getLanguage } from '@nextcloud/l10n'
import { NcFormBox, NcFormBoxCopyButton, NcFormBoxSwitch, NcFormGroup, NcSelect } from '@nextcloud/vue'
import { mapStores } from 'pinia'
import CalendarPicker from '@/components/Shared/CalendarPicker.vue'
import useCalendarsStore from '@/store/calendars.js'
import useImportFilesStore from '@/store/importFiles.js'
import useImportStore from '@/store/import.ts'
import usePrincipalsStore from '@/store/principals.js'
import { uidToHexColor } from '@/utils/color.js'

export default {
name: 'ImportScreenRow',
components: {
CalendarPicker,
NcFormBox,
NcFormBoxCopyButton,
NcFormBoxSwitch,
NcFormGroup,
NcSelect,
},

props: {
file: {
entry: {
type: Object,
required: true,
},
},

computed: {
...mapStores(usePrincipalsStore, useImportFilesStore, useCalendarsStore),
...mapStores(usePrincipalsStore, useImportStore, useCalendarsStore),

file() {
return this.entry.file
},

formatInputId() {
return `import-format-${this.file.id}`
},

formatOptions() {
return [{
id: 'ical',
label: this.$t('calendar', 'iCalendar (.ics)'),
}, {
id: 'jcal',
label: this.$t('calendar', 'jCal (.json)'),
}, {
id: 'xcal',
label: this.$t('calendar', 'xCal (.xml)'),
}]
},

selectedFormat: {
get() {
const format = this.entry.options.format
return this.formatOptions.find((option) => option.id === format) ?? this.formatOptions[0]
},

set(option) {
if (!option) {
return
}
this.importStore.setOptionsForFile({
fileId: this.file.id,
options: { format: option.id },
})
},
},

supersede() {
return this.entry.options.supersede
},

newCalendar() {
return {
id: 'new',
Expand All @@ -57,7 +128,7 @@ export default {
},

calendar() {
const calendarId = this.importFilesStore.importCalendarRelation[this.file.id]
const calendarId = this.entry.calendarId
if (calendarId === this.newCalendar.id) {
return this.newCalendar
}
Expand Down Expand Up @@ -127,11 +198,37 @@ export default {
},

selectCalendar(newCalendar) {
this.importFilesStore.setCalendarForFileId({
this.importStore.setCalendarForFile({
fileId: this.file.id,
calendarId: newCalendar.id,
})
},

setSupersede(supersede) {
this.importStore.setOptionsForFile({
fileId: this.file.id,
options: { supersede },
})
},
},
}
</script>

<style lang="scss" scoped>
.import-modal-file-item {
&:not(:last-child) {
border-bottom: var(--border-width-input) solid var(--color-border);
padding-bottom: calc(var(--default-grid-baseline) * 4);
margin-bottom: calc(var(--default-grid-baseline) * 4);
}

&__calendar-hint {
color: var(--color-text-maxcontrast);
font-size: var(--default-font-size);
}

:deep(legend) {
font-size: calc(var(--default-font-size) * (4 / 3));
}
}
</style>
Loading
Loading