diff options
Diffstat (limited to 'apps/user_status/src/components/SetStatusModal.vue')
-rw-r--r-- | apps/user_status/src/components/SetStatusModal.vue | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/apps/user_status/src/components/SetStatusModal.vue b/apps/user_status/src/components/SetStatusModal.vue new file mode 100644 index 00000000000..8624ed19e94 --- /dev/null +++ b/apps/user_status/src/components/SetStatusModal.vue @@ -0,0 +1,391 @@ +<!-- + - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> + +<template> + <NcModal size="normal" + label-id="user_status-set-dialog" + dark + :set-return-focus="setReturnFocus" + @close="closeModal"> + <div class="set-status-modal"> + <!-- Status selector --> + <h2 id="user_status-set-dialog" class="set-status-modal__header"> + {{ $t('user_status', 'Online status') }} + </h2> + <div class="set-status-modal__online-status" + role="radiogroup" + :aria-label="$t('user_status', 'Online status')"> + <OnlineStatusSelect v-for="status in statuses" + :key="status.type" + v-bind="status" + :checked="status.type === statusType" + @select="changeStatus" /> + </div> + + <!-- Status message form --> + <form @submit.prevent="saveStatus" @reset="clearStatus"> + <h3 class="set-status-modal__header"> + {{ $t('user_status', 'Status message') }} + </h3> + <div class="set-status-modal__custom-input"> + <CustomMessageInput ref="customMessageInput" + :icon="icon" + :message="editedMessage" + @change="setMessage" + @select-icon="setIcon" /> + <NcButton v-if="messageId === 'vacationing'" + :href="absencePageUrl" + target="_blank" + type="secondary" + :aria-label="$t('user_status', 'Set absence period')"> + {{ $t('user_status', 'Set absence period and replacement') + ' ↗' }} + </NcButton> + </div> + <div v-if="hasBackupStatus" + class="set-status-modal__automation-hint"> + {{ $t('user_status', 'Your status was set automatically') }} + </div> + <PreviousStatus v-if="hasBackupStatus" + :icon="backupIcon" + :message="backupMessage" + @select="revertBackupFromServer" /> + <PredefinedStatusesList @select-status="selectPredefinedMessage" /> + <ClearAtSelect :clear-at="clearAt" + @select-clear-at="setClearAt" /> + <div class="status-buttons"> + <NcButton :wide="true" + type="tertiary" + native-type="reset" + :aria-label="$t('user_status', 'Clear status message')" + :disabled="isSavingStatus"> + {{ $t('user_status', 'Clear status message') }} + </NcButton> + <NcButton :wide="true" + type="primary" + native-type="submit" + :aria-label="$t('user_status', 'Set status message')" + :disabled="isSavingStatus"> + {{ $t('user_status', 'Set status message') }} + </NcButton> + </div> + </form> + </div> + </NcModal> +</template> + +<script> +import { showError } from '@nextcloud/dialogs' +import { generateUrl } from '@nextcloud/router' +import NcModal from '@nextcloud/vue/components/NcModal' +import NcButton from '@nextcloud/vue/components/NcButton' +import { getAllStatusOptions } from '../services/statusOptionsService.js' +import OnlineStatusMixin from '../mixins/OnlineStatusMixin.js' +import PredefinedStatusesList from './PredefinedStatusesList.vue' +import PreviousStatus from './PreviousStatus.vue' +import CustomMessageInput from './CustomMessageInput.vue' +import ClearAtSelect from './ClearAtSelect.vue' +import OnlineStatusSelect from './OnlineStatusSelect.vue' + +export default { + name: 'SetStatusModal', + + components: { + ClearAtSelect, + CustomMessageInput, + NcModal, + OnlineStatusSelect, + PredefinedStatusesList, + PreviousStatus, + NcButton, + }, + mixins: [OnlineStatusMixin], + + props: { + /** + * Whether the component should be rendered as a Dashboard Status or a User Menu Entries + * true = Dashboard Status + * false = User Menu Entries + */ + inline: { + type: Boolean, + default: false, + }, + }, + + data() { + return { + clearAt: null, + editedMessage: '', + predefinedMessageId: null, + isSavingStatus: false, + statuses: getAllStatusOptions(), + } + }, + + computed: { + messageId() { + return this.$store.state.userStatus.messageId + }, + icon() { + return this.$store.state.userStatus.icon + }, + message() { + return this.$store.state.userStatus.message || '' + }, + hasBackupStatus() { + return this.messageId && (this.backupIcon || this.backupMessage) + }, + backupIcon() { + return this.$store.state.userBackupStatus.icon || '' + }, + backupMessage() { + return this.$store.state.userBackupStatus.message || '' + }, + + absencePageUrl() { + return generateUrl('settings/user/availability#absence') + }, + + resetButtonText() { + if (this.backupIcon && this.backupMessage) { + return this.$t('user_status', 'Reset status to "{icon} {message}"', { + icon: this.backupIcon, + message: this.backupMessage, + }) + } else if (this.backupMessage) { + return this.$t('user_status', 'Reset status to "{message}"', { + message: this.backupMessage, + }) + } else if (this.backupIcon) { + return this.$t('user_status', 'Reset status to "{icon}"', { + icon: this.backupIcon, + }) + } + + return this.$t('user_status', 'Reset status') + }, + + setReturnFocus() { + if (this.inline) { + return undefined + } + return document.querySelector('[aria-controls="header-menu-user-menu"]') ?? undefined + }, + }, + + watch: { + message: { + immediate: true, + handler(newValue) { + this.editedMessage = newValue + }, + }, + }, + + /** + * Loads the current status when a user opens dialog + */ + mounted() { + this.$store.dispatch('fetchBackupFromServer') + + this.predefinedMessageId = this.$store.state.userStatus.messageId + if (this.$store.state.userStatus.clearAt !== null) { + this.clearAt = { + type: '_time', + time: this.$store.state.userStatus.clearAt, + } + } + }, + methods: { + /** + * Closes the Set Status modal + */ + closeModal() { + this.$emit('close') + }, + /** + * Sets a new icon + * + * @param {string} icon The new icon + */ + setIcon(icon) { + this.predefinedMessageId = null + this.$store.dispatch('setCustomMessage', { + message: this.message, + icon, + clearAt: this.clearAt, + }) + this.$nextTick(() => { + this.$refs.customMessageInput.focus() + }) + }, + /** + * Sets a new message + * + * @param {string} message The new message + */ + setMessage(message) { + this.predefinedMessageId = null + this.editedMessage = message + }, + /** + * Sets a new clearAt value + * + * @param {object} clearAt The new clearAt object + */ + setClearAt(clearAt) { + this.clearAt = clearAt + }, + /** + * Sets new icon/message/clearAt based on a predefined message + * + * @param {object} status The predefined status object + */ + selectPredefinedMessage(status) { + this.predefinedMessageId = status.id + this.clearAt = status.clearAt + this.$store.dispatch('setPredefinedMessage', { + messageId: status.id, + clearAt: status.clearAt, + }) + }, + /** + * Saves the status and closes the + * + * @return {Promise<void>} + */ + async saveStatus() { + if (this.isSavingStatus) { + return + } + + try { + this.isSavingStatus = true + + if (this.predefinedMessageId === null) { + await this.$store.dispatch('setCustomMessage', { + message: this.editedMessage, + icon: this.icon, + clearAt: this.clearAt, + }) + } else { + this.$store.dispatch('setPredefinedMessage', { + messageId: this.predefinedMessageId, + clearAt: this.clearAt, + }) + } + } catch (err) { + showError(this.$t('user_status', 'There was an error saving the status')) + console.debug(err) + this.isSavingStatus = false + return + } + + this.isSavingStatus = false + this.closeModal() + }, + /** + * + * @return {Promise<void>} + */ + async clearStatus() { + try { + this.isSavingStatus = true + + await this.$store.dispatch('clearMessage') + } catch (err) { + showError(this.$t('user_status', 'There was an error clearing the status')) + console.debug(err) + this.isSavingStatus = false + return + } + + this.isSavingStatus = false + this.predefinedMessageId = null + this.closeModal() + }, + /** + * + * @return {Promise<void>} + */ + async revertBackupFromServer() { + try { + this.isSavingStatus = true + + await this.$store.dispatch('revertBackupFromServer', { + messageId: this.messageId, + }) + } catch (err) { + showError(this.$t('user_status', 'There was an error reverting the status')) + console.debug(err) + this.isSavingStatus = false + return + } + + this.isSavingStatus = false + this.predefinedMessageId = this.$store.state.userStatus?.messageId + }, + }, +} +</script> + +<style lang="scss" scoped> + +.set-status-modal { + padding: 8px 20px 20px 20px; + + &, & * { + box-sizing: border-box; + } + + &__header { + font-size: 21px; + text-align: center; + height: fit-content; + min-height: var(--default-clickable-area); + line-height: var(--default-clickable-area); + overflow-wrap: break-word; + margin-block: 0 calc(2 * var(--default-grid-baseline)); + } + + &__online-status { + display: flex; + flex-direction: column; + gap: calc(2 * var(--default-grid-baseline)); + margin-block: 0 calc(2 * var(--default-grid-baseline)); + } + + &__custom-input { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--default-grid-baseline); + width: 100%; + padding-inline-start: var(--default-grid-baseline); + margin-block: 0 calc(2 * var(--default-grid-baseline)); + } + + &__automation-hint { + display: flex; + width: 100%; + margin-block: 0 calc(2 * var(--default-grid-baseline)); + color: var(--color-text-maxcontrast); + } + + .status-buttons { + display: flex; + padding: 3px; + padding-inline-start:0; + gap: 3px; + } +} + +@media only screen and (max-width: 500px) { + .set-status-modal__online-status { + grid-template-columns: none !important; + } +} + +</style> |