aboutsummaryrefslogtreecommitdiffstats
path: root/apps/user_status/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/user_status/src')
-rw-r--r--apps/user_status/src/UserStatus.vue238
-rw-r--r--apps/user_status/src/components/ClearAtSelect.vue53
-rw-r--r--apps/user_status/src/components/CustomMessageInput.vue81
-rw-r--r--apps/user_status/src/components/OnlineStatusSelect.vue100
-rw-r--r--apps/user_status/src/components/PredefinedStatus.vue136
-rw-r--r--apps/user_status/src/components/PredefinedStatusesList.vue65
-rw-r--r--apps/user_status/src/components/PreviousStatus.vue106
-rw-r--r--apps/user_status/src/components/SetStatusModal.vue290
-rw-r--r--apps/user_status/src/dashboard.js44
-rw-r--r--apps/user_status/src/filters/clearAtFilter.js23
-rw-r--r--apps/user_status/src/menu.js73
-rw-r--r--apps/user_status/src/mixins/OnlineStatusMixin.js46
-rw-r--r--apps/user_status/src/services/clearAtOptionsService.js21
-rw-r--r--apps/user_status/src/services/clearAtService.js23
-rw-r--r--apps/user_status/src/services/dateService.js21
-rw-r--r--apps/user_status/src/services/heartbeatService.js21
-rw-r--r--apps/user_status/src/services/predefinedStatusService.js21
-rw-r--r--apps/user_status/src/services/statusOptionsService.js30
-rw-r--r--apps/user_status/src/services/statusService.js49
-rw-r--r--apps/user_status/src/store/index.js27
-rw-r--r--apps/user_status/src/store/predefinedStatuses.js31
-rw-r--r--apps/user_status/src/store/userBackupStatus.js102
-rw-r--r--apps/user_status/src/store/userStatus.js29
-rw-r--r--apps/user_status/src/views/Dashboard.vue121
24 files changed, 745 insertions, 1006 deletions
diff --git a/apps/user_status/src/UserStatus.vue b/apps/user_status/src/UserStatus.vue
index a4b7b9552f3..07d81aad95c 100644
--- a/apps/user_status/src/UserStatus.vue
+++ b/apps/user_status/src/UserStatus.vue
@@ -1,80 +1,70 @@
<!--
- - @copyright Copyright (c) 2020 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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
- <component :is="elementTag">
- <div class="user-status-menu-item">
- <!-- Username display -->
- <a v-if="!inline"
- class="user-status-menu-item__header"
- :href="profilePageLink"
- @click="loadProfilePage">
- <div class="user-status-menu-item__header-content">
- <div class="user-status-menu-item__header-content-displayname">{{ displayName }}</div>
- <div v-if="!loadingProfilePage" class="user-status-menu-item__header-content-placeholder" />
- <div v-else class="icon-loading-small" />
- </div>
- <div v-if="profileEnabled">
- {{ t('user_status', 'View profile') }}
- </div>
- </a>
-
- <!-- Status modal toggle -->
- <toggle :is="inline ? 'button' : 'a'"
- :class="{'user-status-menu-item__toggle--inline': inline}"
- class="user-status-menu-item__toggle"
- href="#"
- @click.prevent.stop="openModal">
- <span :class="statusIcon" class="user-status-menu-item__toggle-icon" />
+ <Fragment>
+ <NcListItem v-if="!inline"
+ class="user-status-menu-item"
+ compact
+ :name="visibleMessage"
+ @click.stop="openModal">
+ <template #icon>
+ <NcUserStatusIcon class="user-status-icon"
+ :status="statusType"
+ aria-hidden="true" />
+ </template>
+ </NcListItem>
+
+ <div v-else>
+ <!-- Dashboard Status -->
+ <NcButton @click.stop="openModal">
+ <template #icon>
+ <NcUserStatusIcon class="user-status-icon"
+ :status="statusType"
+ aria-hidden="true" />
+ </template>
{{ visibleMessage }}
- </toggle>
+ </NcButton>
</div>
-
<!-- Status management modal -->
<SetStatusModal v-if="isModalOpen"
+ :inline="inline"
@close="closeModal" />
- </component>
+ </Fragment>
</template>
<script>
-import { generateUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
-import { loadState } from '@nextcloud/initial-state'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
+import { Fragment } from 'vue-frag'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcListItem from '@nextcloud/vue/components/NcListItem'
+import NcUserStatusIcon from '@nextcloud/vue/components/NcUserStatusIcon'
import debounce from 'debounce'
-import { sendHeartbeat } from './services/heartbeatService'
-import OnlineStatusMixin from './mixins/OnlineStatusMixin'
-
-const { profileEnabled } = loadState('user_status', 'profileEnabled', false)
+import { sendHeartbeat } from './services/heartbeatService.js'
+import OnlineStatusMixin from './mixins/OnlineStatusMixin.js'
export default {
name: 'UserStatus',
components: {
- SetStatusModal: () => import(/* webpackChunkName: 'user-status-modal' */'./components/SetStatusModal'),
+ Fragment,
+ NcButton,
+ NcListItem,
+ NcUserStatusIcon,
+ SetStatusModal: () => import(/* webpackChunkName: 'user-status-modal' */'./components/SetStatusModal.vue'),
},
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,
@@ -83,44 +73,19 @@ export default {
data() {
return {
- displayName: getCurrentUser().displayName,
heartbeatInterval: null,
isAway: false,
isModalOpen: false,
- loadingProfilePage: false,
mouseMoveListener: null,
- profileEnabled,
setAwayTimeout: null,
}
},
- computed: {
- elementTag() {
- return this.inline ? 'div' : 'li'
- },
- /**
- * The profile page link
- *
- * @return {string | null}
- */
- profilePageLink() {
- if (this.profileEnabled) {
- return generateUrl('/u/{userId}', { userId: getCurrentUser().uid })
- }
- // Since an anchor element is used rather than a button,
- // this hack removes href if the profile is disabled so that disabling pointer-events is not needed to prevent a click from opening a page
- // and to allow the hover event for styling
- return null
- },
- },
/**
* Loads the current user's status from initial state
* and stores it in Vuex
*/
mounted() {
- subscribe('settings:display-name:updated', this.handleDisplayNameUpdate)
- subscribe('settings:profile-enabled:updated', this.handleProfileEnabledUpdate)
-
this.$store.dispatch('loadStatusFromInitialState')
if (OC.config.session_keepalive) {
@@ -157,28 +122,12 @@ export default {
* Some housekeeping before destroying the component
*/
beforeDestroy() {
- unsubscribe('settings:display-name:updated', this.handleDisplayNameUpdate)
- unsubscribe('settings:profile-enabled:updated', this.handleProfileEnabledUpdate)
window.removeEventListener('mouseMove', this.mouseMoveListener)
clearInterval(this.heartbeatInterval)
unsubscribe('user_status:status.updated', this.handleUserStatusUpdated)
},
methods: {
- handleDisplayNameUpdate(displayName) {
- this.displayName = displayName
- },
-
- handleProfileEnabledUpdate(profileEnabled) {
- this.profileEnabled = profileEnabled
- },
-
- loadProfilePage() {
- if (this.profileEnabled) {
- this.loadingProfilePage = true
- }
- },
-
/**
* Opens the modal to set a custom status
*/
@@ -211,7 +160,7 @@ export default {
}
},
handleUserStatusUpdated(state) {
- if (OC.getCurrentUser().uid === state.userId) {
+ if (getCurrentUser()?.uid === state.userId) {
this.$store.dispatch('setStatusFromObject', {
status: state.status,
icon: state.icon,
@@ -224,97 +173,12 @@ export default {
</script>
<style lang="scss" scoped>
-.user-status-menu-item {
- &__header {
- display: flex !important;
- flex-direction: column !important;
- width: auto !important;
- height: 44px * 1.5 !important;
- padding: 10px 12px 5px 12px !important;
- align-items: flex-start !important;
- color: var(--color-main-text) !important;
-
- &:focus-visible {
- padding: 6px 8px 1px 8px !important;
- margin: 2px !important;
- }
-
- &:not([href]) {
- height: var(--header-menu-item-height) !important;
- color: var(--color-text-maxcontrast) !important;
- cursor: default !important;
-
- & * {
- cursor: default !important;
- }
-
- &:hover {
- background-color: transparent !important;
- }
- }
-
- &-content {
- display: inline-flex !important;
- font-weight: bold !important;
- gap: 0 10px !important;
- width: auto;
-
- &-displayname {
- width: auto;
- }
-
- &-placeholder {
- width: 16px !important;
- height: 24px !important;
- margin-right: 10px !important;
- visibility: hidden !important;
- }
- }
-
- span {
- color: var(--color-text-maxcontrast) !important;
- }
- }
-
- &__toggle {
- &-icon {
- width: 16px;
- height: 16px;
- margin-right: 10px;
- opacity: 1 !important;
- background-size: 16px;
- vertical-align: middle !important;
- }
-
- // In dashboard
- &--inline {
- width: auto;
- min-width: 44px;
- height: 44px;
- margin: 0;
- border: 0;
- border-radius: var(--border-radius-pill);
- background-color: var(--color-main-background-blur);
- font-size: inherit;
- font-weight: normal;
-
- -webkit-backdrop-filter: var(--background-blur);
- backdrop-filter: var(--background-blur);
-
- &:active,
- &:hover,
- &:focus {
- background-color: var(--color-background-hover);
- }
- &:focus {
- box-shadow: 0 0 0 2px var(--color-main-text) !important;
- }
- }
- }
+.user-status-icon {
+ width: 20px;
+ height: 20px;
+ margin: calc((var(--default-clickable-area) - 20px) / 2); // 20px icon size
+ opacity: 1 !important;
+ background-size: 20px;
+ vertical-align: middle !important;
}
-
-li {
- list-style-type: none;
-}
-
</style>
diff --git a/apps/user_status/src/components/ClearAtSelect.vue b/apps/user_status/src/components/ClearAtSelect.vue
index 620b72a3a41..91b816dc04a 100644
--- a/apps/user_status/src/components/ClearAtSelect.vue
+++ b/apps/user_status/src/components/ClearAtSelect.vue
@@ -1,47 +1,33 @@
<!--
- - @copyright Copyright (c) 2020 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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<div class="clear-at-select">
<label class="clear-at-select__label" for="clearStatus">
{{ $t('user_status', 'Clear status after') }}
</label>
- <NcMultiselect id="clearStatus"
- label="label"
- :value="option"
+ <NcSelect input-id="clearStatus"
+ class="clear-at-select__select"
:options="options"
- open-direction="top"
- @select="select" />
+ :value="option"
+ :clearable="false"
+ placement="top"
+ label-outside
+ @option:selected="select" />
</div>
</template>
<script>
-import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect'
-import { getAllClearAtOptions } from '../services/clearAtOptionsService'
-import { clearAtFilter } from '../filters/clearAtFilter'
+import NcSelect from '@nextcloud/vue/components/NcSelect'
+import { getAllClearAtOptions } from '../services/clearAtOptionsService.js'
+import { clearAtFilter } from '../filters/clearAtFilter.js'
export default {
name: 'ClearAtSelect',
components: {
- NcMultiselect,
+ NcSelect,
},
props: {
clearAt: {
@@ -87,16 +73,13 @@ export default {
<style lang="scss" scoped>
.clear-at-select {
display: flex;
- margin-bottom: 10px;
+ gap: calc(2 * var(--default-grid-baseline));
align-items: center;
+ margin-block: 0 calc(2 * var(--default-grid-baseline));
- &__label {
- margin-right: 10px;
- }
-
- .multiselect {
+ &__select {
flex-grow: 1;
- min-width: 130px;
+ min-width: 215px;
}
}
</style>
diff --git a/apps/user_status/src/components/CustomMessageInput.vue b/apps/user_status/src/components/CustomMessageInput.vue
index 4fd649dc166..fb129281430 100644
--- a/apps/user_status/src/components/CustomMessageInput.vue
+++ b/apps/user_status/src/components/CustomMessageInput.vue
@@ -1,56 +1,40 @@
<!--
- - @copyright Copyright (c) 2020 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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
- <div class="custom-input">
+ <div class="custom-input" role="group">
<NcEmojiPicker container=".custom-input" @select="setIcon">
- <NcButton class="custom-input__emoji-button" type="tertiary">
- {{ visibleIcon }}
+ <NcButton type="tertiary"
+ :aria-label="t('user_status', 'Emoji for your status message')">
+ <template #icon>
+ {{ visibleIcon }}
+ </template>
</NcButton>
</NcEmojiPicker>
<div class="custom-input__container">
- <label class="hidden-visually" for="user_status_message">
- {{ t('user_status', 'What is your status?') }}
- </label>
- <input id="user_status_message"
- ref="input"
+ <NcTextField ref="input"
maxlength="80"
:disabled="disabled"
- :placeholder="$t('user_status', 'What is your status?')"
- type="text"
+ :placeholder="t('user_status', 'What is your status?')"
:value="message"
- @change="onChange"
- @keyup="onKeyup"
- @paste="onKeyup">
+ type="text"
+ :label="t('user_status', 'What is your status?')"
+ @input="onChange" />
</div>
</div>
</template>
<script>
-import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
-import NcEmojiPicker from '@nextcloud/vue/dist/Components/NcEmojiPicker.js'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcEmojiPicker from '@nextcloud/vue/components/NcEmojiPicker'
+import NcTextField from '@nextcloud/vue/components/NcTextField'
export default {
name: 'CustomMessageInput',
components: {
+ NcTextField,
NcButton,
NcEmojiPicker,
},
@@ -73,8 +57,7 @@ export default {
emits: [
'change',
- 'submit',
- 'icon-selected',
+ 'select-icon',
],
computed: {
@@ -98,12 +81,8 @@ export default {
*
* @param {Event} event The Change Event
*/
- onKeyup(event) {
- this.$emit('change', event.target.value)
- },
-
onChange(event) {
- this.$emit('submit', event.target.value)
+ this.$emit('change', event.target.value)
},
setIcon(icon) {
@@ -116,28 +95,12 @@ export default {
<style lang="scss" scoped>
.custom-input {
display: flex;
+ align-items: flex-end;
+ gap: var(--default-grid-baseline);
width: 100%;
- &__emoji-button {
- min-height: 36px;
- padding: 0;
- border: 2px solid var(--color-border-maxcontrast);
- border-right: none;
- border-radius: var(--border-radius) 0 0 var(--border-radius);
-
- &:hover {
- border-color: var(--color-primary-element);
- }
- }
-
&__container {
width: 100%;
-
- input {
- width: 100%;
- margin: 0;
- border-radius: 0 var(--border-radius) var(--border-radius) 0;
- }
}
}
</style>
diff --git a/apps/user_status/src/components/OnlineStatusSelect.vue b/apps/user_status/src/components/OnlineStatusSelect.vue
index 52b6022fb44..0abcc8d68e6 100644
--- a/apps/user_status/src/components/OnlineStatusSelect.vue
+++ b/apps/user_status/src/components/OnlineStatusSelect.vue
@@ -1,53 +1,40 @@
<!--
- - @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
- -
- - @author John Molakvoæ <skjnldsv@protonmail.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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<div class="user-status-online-select">
<input :id="id"
:checked="checked"
- class="user-status-online-select__input"
+ class="hidden-visually user-status-online-select__input"
type="radio"
name="user-status-online"
@change="onChange">
<label :for="id" class="user-status-online-select__label">
+ <NcUserStatusIcon :status="type"
+ class="user-status-online-select__icon"
+ aria-hidden="true" />
{{ label }}
- <span :class="icon" role="img" />
<em class="user-status-online-select__subline">{{ subline }}</em>
</label>
</div>
</template>
<script>
+import NcUserStatusIcon from '@nextcloud/vue/components/NcUserStatusIcon'
+
export default {
name: 'OnlineStatusSelect',
+ components: {
+ NcUserStatusIcon,
+ },
+
props: {
checked: {
type: Boolean,
default: false,
},
- icon: {
- type: String,
- required: true,
- },
type: {
type: String,
required: true,
@@ -77,56 +64,42 @@ export default {
</script>
<style lang="scss" scoped>
-@use 'sass:math';
-$icon-size: 24px;
-$label-padding: 8px;
-
.user-status-online-select {
- // Inputs are here for keyboard navigation, they are not visually visible
- &__input {
- position: absolute;
- top: auto;
- left: -10000px;
- overflow: hidden;
- width: 1px;
- height: 1px;
- }
-
&__label {
- position: relative;
- display: block;
- margin: $label-padding;
- padding: $label-padding;
- padding-left: $icon-size + $label-padding * 2;
- border: 2px solid var(--color-main-background);
+ box-sizing: inherit;
+ display: grid;
+ grid-template-columns: var(--default-clickable-area) 1fr 2fr;
+ align-items: center;
+ gap: var(--default-grid-baseline);
+ min-height: var(--default-clickable-area);
+ padding: var(--default-grid-baseline);
border-radius: var(--border-radius-large);
background-color: var(--color-background-hover);
- background-position: $label-padding center;
- background-size: $icon-size;
- span,
- & {
+ &, & * {
cursor: pointer;
}
- span {
- position: absolute;
- top: calc(50% - math.div($icon-size, 2));
- left: $label-padding;
- display: block;
- width: $icon-size;
- height: $icon-size;
+ &:hover {
+ background-color: var(--color-background-dark);
}
}
- &__input:checked + &__label,
- &__input:focus + &__label,
- &__label:hover {
- border-color: var(--color-primary);
+ &__icon {
+ flex-shrink: 0;
+ max-width: 34px;
+ max-height: 100%;
}
- &__label:active {
- border-color: var(--color-border-dark);
+ &__input:checked + &__label {
+ outline: 2px solid var(--color-main-text);
+ background-color: var(--color-background-dark);
+ box-shadow: 0 0 0 4px var(--color-main-background);
+ }
+
+ &__input:focus-visible + &__label {
+ outline: 2px solid var(--color-primary-element) !important;
+ background-color: var(--color-background-dark);
}
&__subline {
@@ -134,5 +107,4 @@ $label-padding: 8px;
color: var(--color-text-lighter);
}
}
-
</style>
diff --git a/apps/user_status/src/components/PredefinedStatus.vue b/apps/user_status/src/components/PredefinedStatus.vue
index bca94b41a76..b12892d4add 100644
--- a/apps/user_status/src/components/PredefinedStatus.vue
+++ b/apps/user_status/src/components/PredefinedStatus.vue
@@ -1,43 +1,31 @@
<!--
- - @copyright Copyright (c) 2020 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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
- <div class="predefined-status"
- tabindex="0"
- @keyup.enter="select"
- @keyup.space="select"
- @click="select">
- <span class="predefined-status__icon">
- {{ icon }}
- </span>
- <span class="predefined-status__message">
- {{ message }}
- </span>
- <span class="predefined-status__clear-at">
- {{ clearAt | clearAtFilter }}
- </span>
- </div>
+ <li class="predefined-status">
+ <input :id="id"
+ class="hidden-visually predefined-status__input"
+ type="radio"
+ name="predefined-status"
+ :checked="selected"
+ @change="select">
+ <label class="predefined-status__label" :for="id">
+ <span aria-hidden="true" class="predefined-status__label--icon">
+ {{ icon }}
+ </span>
+ <span class="predefined-status__label--message">
+ {{ message }}
+ </span>
+ <span class="predefined-status__label--clear-at">
+ {{ clearAt | clearAtFilter }}
+ </span>
+ </label>
+ </li>
</template>
<script>
-import { clearAtFilter } from '../filters/clearAtFilter'
+import { clearAtFilter } from '../filters/clearAtFilter.js'
export default {
name: 'PredefinedStatus',
@@ -62,6 +50,16 @@ export default {
required: false,
default: null,
},
+ selected: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ id() {
+ return `user-status-predefined-status-${this.messageId}`
+ },
},
methods: {
/**
@@ -76,39 +74,55 @@ export default {
<style lang="scss" scoped>
.predefined-status {
- display: flex;
- flex-wrap: nowrap;
- justify-content: flex-start;
- flex-basis: 100%;
- border-radius: var(--border-radius);
- align-items: center;
- min-height: 44px;
+ &__label {
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ flex-basis: 100%;
+ border-radius: var(--border-radius);
+ align-items: center;
+ min-height: var(--default-clickable-area);
+ padding-inline: var(--default-grid-baseline);
- &:hover,
- &:focus {
- background-color: var(--color-background-hover);
- }
+ &, & * {
+ cursor: pointer;
+ }
- &:active{
- background-color: var(--color-background-dark);
- }
+ &:hover {
+ background-color: var(--color-background-dark);
+ }
- &__icon {
- flex-basis: 40px;
- text-align: center;
- }
+ &--icon {
+ flex-basis: var(--default-clickable-area);
+ text-align: center;
+ }
- &__message {
- font-weight: bold;
- padding: 0 6px;
- }
+ &--message {
+ font-weight: bold;
+ padding: 0 6px;
+ }
- &__clear-at {
- opacity: .7;
+ &--clear-at {
+ color: var(--color-text-maxcontrast);
- &::before {
- content: ' – ';
+ &::before {
+ content: ' – ';
+ }
}
}
+
+ &__input:checked + &__label,
+ &__label:active {
+ outline: 2px solid var(--color-main-text);
+ box-shadow: 0 0 0 4px var(--color-main-background);
+ background-color: var(--color-background-dark);
+ border-radius: var(--border-radius-large);
+ }
+
+ &__input:focus-visible + &__label {
+ outline: 2px solid var(--color-primary-element) !important;
+ background-color: var(--color-background-dark);
+ border-radius: var(--border-radius-large);
+ }
}
</style>
diff --git a/apps/user_status/src/components/PredefinedStatusesList.vue b/apps/user_status/src/components/PredefinedStatusesList.vue
index cff03289715..cdf359dce76 100644
--- a/apps/user_status/src/components/PredefinedStatusesList.vue
+++ b/apps/user_status/src/components/PredefinedStatusesList.vue
@@ -1,35 +1,21 @@
<!--
- - @copyright Copyright (c) 2020 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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
- <div v-if="hasLoaded"
- class="predefined-statuses-list">
+ <ul v-if="statusesHaveLoaded"
+ class="predefined-statuses-list"
+ :aria-label="t('user_status', 'Predefined statuses')">
<PredefinedStatus v-for="status in predefinedStatuses"
:key="status.id"
:message-id="status.id"
:icon="status.icon"
:message="status.message"
:clear-at="status.clearAt"
+ :selected="lastSelected === status.id"
@select="selectStatus(status)" />
- </div>
+ </ul>
<div v-else
class="predefined-statuses-list">
<div class="icon icon-loading-small" />
@@ -37,32 +23,41 @@
</template>
<script>
-import PredefinedStatus from './PredefinedStatus'
-import { mapState } from 'vuex'
+import PredefinedStatus from './PredefinedStatus.vue'
+import { mapGetters, mapState } from 'vuex'
export default {
name: 'PredefinedStatusesList',
components: {
PredefinedStatus,
},
+ data() {
+ return {
+ lastSelected: null,
+ }
+ },
computed: {
...mapState({
predefinedStatuses: state => state.predefinedStatuses.predefinedStatuses,
+ messageId: state => state.userStatus.messageId,
}),
- /**
- * Indicator whether the predefined statuses have already been loaded
- *
- * @return {boolean}
- */
- hasLoaded() {
- return this.predefinedStatuses.length > 0
- },
+ ...mapGetters(['statusesHaveLoaded']),
},
+
+ watch: {
+ messageId: {
+ immediate: true,
+ handler() {
+ this.lastSelected = this.messageId
+ },
+ },
+ },
+
/**
* Loads all predefined statuses from the server
* when this component is mounted
*/
- mounted() {
+ created() {
this.$store.dispatch('loadAllPredefinedStatuses')
},
methods: {
@@ -72,6 +67,7 @@ export default {
* @param {object} status The selected status
*/
selectStatus(status) {
+ this.lastSelected = status.id
this.$emit('select-status', status)
},
},
@@ -82,6 +78,7 @@ export default {
.predefined-statuses-list {
display: flex;
flex-direction: column;
- margin-bottom: 10px;
+ gap: var(--default-grid-baseline);
+ margin-block: 0 calc(2 * var(--default-grid-baseline));
}
</style>
diff --git a/apps/user_status/src/components/PreviousStatus.vue b/apps/user_status/src/components/PreviousStatus.vue
new file mode 100644
index 00000000000..58d6ebd294b
--- /dev/null
+++ b/apps/user_status/src/components/PreviousStatus.vue
@@ -0,0 +1,106 @@
+<!--
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<template>
+ <div class="predefined-status backup-status"
+ tabindex="0"
+ @keyup.enter="select"
+ @keyup.space="select"
+ @click="select">
+ <span class="predefined-status__icon">
+ {{ icon }}
+ </span>
+ <span class="predefined-status__message">
+ {{ message }}
+ </span>
+ <span class="predefined-status__clear-at">
+ {{ $t('user_status', 'Previously set') }}
+ </span>
+
+ <div class="backup-status__reset-button">
+ <NcButton @click="select">
+ {{ $t('user_status', 'Reset status') }}
+ </NcButton>
+ </div>
+ </div>
+</template>
+
+<script>
+import NcButton from '@nextcloud/vue/components/NcButton'
+
+export default {
+ name: 'PreviousStatus',
+
+ components: {
+ NcButton,
+ },
+
+ props: {
+ icon: {
+ type: [String, null],
+ required: true,
+ },
+ message: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ /**
+ * Emits an event when the user clicks the row
+ */
+ select() {
+ this.$emit('select')
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.predefined-status {
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ flex-basis: 100%;
+ border-radius: var(--border-radius);
+ align-items: center;
+ min-height: var(--default-clickable-area);
+ padding-inline: var(--default-grid-baseline);
+
+ &:hover,
+ &:focus {
+ background-color: var(--color-background-hover);
+ }
+
+ &:active{
+ background-color: var(--color-background-dark);
+ }
+
+ &__icon {
+ flex-basis: var(--default-clickable-area);
+ text-align: center;
+ }
+
+ &__message {
+ font-weight: bold;
+ padding: 0 6px;
+ }
+
+ &__clear-at {
+ color: var(--color-text-maxcontrast);
+
+ &::before {
+ content: ' – ';
+ }
+ }
+}
+
+.backup-status {
+ &__reset-button {
+ justify-content: flex-end;
+ display: flex;
+ flex-grow: 1;
+ }
+}
+</style>
diff --git a/apps/user_status/src/components/SetStatusModal.vue b/apps/user_status/src/components/SetStatusModal.vue
index d7adc99da14..8624ed19e94 100644
--- a/apps/user_status/src/components/SetStatusModal.vue
+++ b/apps/user_status/src/components/SetStatusModal.vue
@@ -1,34 +1,22 @@
<!--
- - @copyright Copyright (c) 2020 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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<NcModal size="normal"
- :title="$t('user_status', 'Set status')"
+ label-id="user_status-set-dialog"
+ dark
+ :set-return-focus="setReturnFocus"
@close="closeModal">
<div class="set-status-modal">
<!-- Status selector -->
- <div class="set-status-modal__header">
- <h2>{{ $t('user_status', 'Online status') }}</h2>
- </div>
- <div class="set-status-modal__online-status">
+ <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"
@@ -36,48 +24,66 @@
@select="changeStatus" />
</div>
- <!-- Status message -->
- <div class="set-status-modal__header">
- <h2>{{ $t('user_status', 'Status message') }}</h2>
- </div>
- <div class="set-status-modal__custom-input">
- <CustomMessageInput ref="customMessageInput"
- :icon="icon"
- :message="message"
- @change="setMessage"
- @submit="saveStatus"
- @select-icon="setIcon" />
- </div>
- <PredefinedStatusesList @select-status="selectPredefinedMessage" />
- <ClearAtSelect :clear-at="clearAt"
- @select-clear-at="setClearAt" />
- <div class="status-buttons">
- <NcButton :wide="true"
- type="tertiary"
- :text="$t('user_status', 'Clear status message')"
- :disabled="isSavingStatus"
- @click="clearStatus">
- {{ $t('user_status', 'Clear status message') }}
- </NcButton>
- <NcButton :wide="true"
- type="primary"
- :text="$t('user_status', 'Set status message')"
- :disabled="isSavingStatus"
- @click="saveStatus">
- {{ $t('user_status', 'Set status message') }}
- </NcButton>
- </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 NcModal from '@nextcloud/vue/dist/Components/NcModal'
-import NcButton from '@nextcloud/vue/dist/Components/NcButton'
+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'
@@ -91,29 +97,100 @@ export default {
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,
- icon: null,
- message: '',
- messageId: '',
+ 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.messageId = this.$store.state.userStatus.messageId
- this.icon = this.$store.state.userStatus.icon
- this.message = this.$store.state.userStatus.message || ''
+ this.$store.dispatch('fetchBackupFromServer')
+ this.predefinedMessageId = this.$store.state.userStatus.messageId
if (this.$store.state.userStatus.clearAt !== null) {
this.clearAt = {
type: '_time',
@@ -134,8 +211,12 @@ export default {
* @param {string} icon The new icon
*/
setIcon(icon) {
- this.messageId = null
- this.icon = icon
+ this.predefinedMessageId = null
+ this.$store.dispatch('setCustomMessage', {
+ message: this.message,
+ icon,
+ clearAt: this.clearAt,
+ })
this.$nextTick(() => {
this.$refs.customMessageInput.focus()
})
@@ -146,8 +227,8 @@ export default {
* @param {string} message The new message
*/
setMessage(message) {
- this.messageId = null
- this.message = message
+ this.predefinedMessageId = null
+ this.editedMessage = message
},
/**
* Sets a new clearAt value
@@ -163,10 +244,12 @@ export default {
* @param {object} status The predefined status object
*/
selectPredefinedMessage(status) {
- this.messageId = status.id
+ this.predefinedMessageId = status.id
this.clearAt = status.clearAt
- this.icon = status.icon
- this.message = status.message
+ this.$store.dispatch('setPredefinedMessage', {
+ messageId: status.id,
+ clearAt: status.clearAt,
+ })
},
/**
* Saves the status and closes the
@@ -181,15 +264,15 @@ export default {
try {
this.isSavingStatus = true
- if (this.messageId !== undefined && this.messageId !== null) {
- await this.$store.dispatch('setPredefinedMessage', {
- messageId: this.messageId,
+ if (this.predefinedMessageId === null) {
+ await this.$store.dispatch('setCustomMessage', {
+ message: this.editedMessage,
+ icon: this.icon,
clearAt: this.clearAt,
})
} else {
- await this.$store.dispatch('setCustomMessage', {
- message: this.message,
- icon: this.icon,
+ this.$store.dispatch('setPredefinedMessage', {
+ messageId: this.predefinedMessageId,
clearAt: this.clearAt,
})
}
@@ -220,8 +303,30 @@ export default {
}
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>
@@ -231,27 +336,48 @@ export default {
.set-status-modal {
padding: 8px 20px 20px 20px;
+ &, & * {
+ box-sizing: border-box;
+ }
+
&__header {
+ font-size: 21px;
text-align: center;
- font-weight: bold;
- margin: 15px 0;
+ 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: grid;
- grid-template-columns: 1fr 1fr;
+ 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-bottom: 10px;
+ margin-block: 0 calc(2 * var(--default-grid-baseline));
+ color: var(--color-text-maxcontrast);
}
.status-buttons {
display: flex;
padding: 3px;
- padding-left:0;
+ padding-inline-start:0;
gap: 3px;
}
}
diff --git a/apps/user_status/src/dashboard.js b/apps/user_status/src/dashboard.js
deleted file mode 100644
index 9c3d94151d0..00000000000
--- a/apps/user_status/src/dashboard.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
- */
-
-import Vue from 'vue'
-import { getRequestToken } from '@nextcloud/auth'
-import { translate, translatePlural } from '@nextcloud/l10n'
-import Dashboard from './views/Dashboard'
-
-// eslint-disable-next-line camelcase
-__webpack_nonce__ = btoa(getRequestToken())
-
-Vue.prototype.t = translate
-Vue.prototype.n = translatePlural
-Vue.prototype.OC = OC
-Vue.prototype.OCA = OCA
-
-document.addEventListener('DOMContentLoaded', function() {
- OCA.Dashboard.register('user_status', (el) => {
- const View = Vue.extend(Dashboard)
- new View({
- propsData: {},
- }).$mount(el)
- })
-
-})
diff --git a/apps/user_status/src/filters/clearAtFilter.js b/apps/user_status/src/filters/clearAtFilter.js
index 9a99b9ec69f..5f62385a978 100644
--- a/apps/user_status/src/filters/clearAtFilter.js
+++ b/apps/user_status/src/filters/clearAtFilter.js
@@ -1,28 +1,11 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { translate as t } from '@nextcloud/l10n'
import moment from '@nextcloud/moment'
-import { dateFactory } from '../services/dateService'
+import { dateFactory } from '../services/dateService.js'
/**
* Formats a clearAt object to be human readable
diff --git a/apps/user_status/src/menu.js b/apps/user_status/src/menu.js
index 66010102f10..34e5e6eabb1 100644
--- a/apps/user_status/src/menu.js
+++ b/apps/user_status/src/menu.js
@@ -1,65 +1,38 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Julius Härtl <jus@bitgrid.net>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
+import { getCSPNonce } from '@nextcloud/auth'
+import { subscribe } from '@nextcloud/event-bus'
import Vue from 'vue'
-import { getRequestToken } from '@nextcloud/auth'
-import UserStatus from './UserStatus'
-import store from './store'
-import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar'
-import { loadState } from '@nextcloud/initial-state'
+
+import UserStatus from './UserStatus.vue'
+import store from './store/index.js'
// eslint-disable-next-line camelcase
-__webpack_nonce__ = btoa(getRequestToken())
+__webpack_nonce__ = getCSPNonce()
Vue.prototype.t = t
Vue.prototype.$t = t
-const avatarDiv = document.getElementById('avatardiv-menu')
-const userStatusData = loadState('user_status', 'status')
-const propsData = {
- preloadedUserStatus: {
- message: userStatusData.message,
- icon: userStatusData.icon,
- status: userStatusData.status,
- },
- user: avatarDiv.dataset.user,
- displayName: avatarDiv.dataset.displayname,
- disableMenu: true,
- disableTooltip: true,
-}
+const mountPoint = document.getElementById('user_status-menu-entry')
-const NcAvatarInMenu = Vue.extend(NcAvatar)
-new NcAvatarInMenu({ propsData }).$mount('#avatardiv-menu')
+const mountMenuEntry = () => {
+ const mountPoint = document.getElementById('user_status-menu-entry')
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: mountPoint,
+ render: h => h(UserStatus),
+ store,
+ })
+}
-// Register settings menu entry
-export default new Vue({
- el: 'li[data-id="user_status-menuitem"]',
- // eslint-disable-next-line vue/match-component-file-name
- name: 'UserStatusRoot',
- render: h => h(UserStatus),
- store,
-})
+if (mountPoint) {
+ mountMenuEntry()
+} else {
+ subscribe('core:user-menu:mounted', mountMenuEntry)
+}
// Register dashboard status
document.addEventListener('DOMContentLoaded', function() {
diff --git a/apps/user_status/src/mixins/OnlineStatusMixin.js b/apps/user_status/src/mixins/OnlineStatusMixin.js
index d1e3a9111fa..5670eb4dc06 100644
--- a/apps/user_status/src/mixins/OnlineStatusMixin.js
+++ b/apps/user_status/src/mixins/OnlineStatusMixin.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { mapState } from 'vuex'
@@ -52,6 +35,7 @@ export default {
return this.$t('user_status', 'Online')
case 'away':
+ case 'busy':
return this.$t('user_status', 'Away')
case 'dnd':
@@ -67,30 +51,6 @@ export default {
return this.$t('user_status', 'Set status')
},
-
- /**
- * The status indicator icon
- *
- * @return {string | null}
- */
- statusIcon() {
- switch (this.statusType) {
- case 'online':
- return 'icon-user-status-online'
-
- case 'away':
- return 'icon-user-status-away'
-
- case 'dnd':
- return 'icon-user-status-dnd'
-
- case 'invisible':
- case 'offline':
- return 'icon-user-status-invisible'
- }
-
- return ''
- },
},
methods: {
diff --git a/apps/user_status/src/services/clearAtOptionsService.js b/apps/user_status/src/services/clearAtOptionsService.js
index 2849e6170f2..af0059bfb7f 100644
--- a/apps/user_status/src/services/clearAtOptionsService.js
+++ b/apps/user_status/src/services/clearAtOptionsService.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { translate as t } from '@nextcloud/l10n'
diff --git a/apps/user_status/src/services/clearAtService.js b/apps/user_status/src/services/clearAtService.js
index a02905b90c9..f23d267ad02 100644
--- a/apps/user_status/src/services/clearAtService.js
+++ b/apps/user_status/src/services/clearAtService.js
@@ -1,28 +1,11 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import {
dateFactory,
-} from './dateService'
+} from './dateService.js'
import moment from '@nextcloud/moment'
/**
diff --git a/apps/user_status/src/services/dateService.js b/apps/user_status/src/services/dateService.js
index 2d110db5998..26a61d4a3e2 100644
--- a/apps/user_status/src/services/dateService.js
+++ b/apps/user_status/src/services/dateService.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
const dateFactory = () => {
diff --git a/apps/user_status/src/services/heartbeatService.js b/apps/user_status/src/services/heartbeatService.js
index 6a033f7789b..fda1a1ffc9f 100644
--- a/apps/user_status/src/services/heartbeatService.js
+++ b/apps/user_status/src/services/heartbeatService.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import HttpClient from '@nextcloud/axios'
diff --git a/apps/user_status/src/services/predefinedStatusService.js b/apps/user_status/src/services/predefinedStatusService.js
index 0a4cb55b573..b423c6e0cc4 100644
--- a/apps/user_status/src/services/predefinedStatusService.js
+++ b/apps/user_status/src/services/predefinedStatusService.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import HttpClient from '@nextcloud/axios'
diff --git a/apps/user_status/src/services/statusOptionsService.js b/apps/user_status/src/services/statusOptionsService.js
index 7bd50bf7aff..6c23645e5be 100644
--- a/apps/user_status/src/services/statusOptionsService.js
+++ b/apps/user_status/src/services/statusOptionsService.js
@@ -1,24 +1,6 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Jan C. Borchardt <hey@jancborchardt.net>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { translate as t } from '@nextcloud/l10n'
@@ -32,22 +14,20 @@ const getAllStatusOptions = () => {
return [{
type: 'online',
label: t('user_status', 'Online'),
- icon: 'icon-user-status-online',
}, {
type: 'away',
label: t('user_status', 'Away'),
- icon: 'icon-user-status-away',
+ }, {
+ type: 'busy',
+ label: t('user_status', 'Busy'),
}, {
type: 'dnd',
label: t('user_status', 'Do not disturb'),
subline: t('user_status', 'Mute all notifications'),
- icon: 'icon-user-status-dnd',
-
}, {
type: 'invisible',
label: t('user_status', 'Invisible'),
subline: t('user_status', 'Appear offline'),
- icon: 'icon-user-status-invisible',
}]
}
diff --git a/apps/user_status/src/services/statusService.js b/apps/user_status/src/services/statusService.js
index f4bda930303..6504411c996 100644
--- a/apps/user_status/src/services/statusService.js
+++ b/apps/user_status/src/services/statusService.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import HttpClient from '@nextcloud/axios'
@@ -36,6 +19,19 @@ const fetchCurrentStatus = async () => {
}
/**
+ * Fetches the current user-status
+ *
+ * @param {string} userId Id of the user to fetch the status
+ * @return {Promise<object>}
+ */
+const fetchBackupStatus = async (userId) => {
+ const url = generateOcsUrl('apps/user_status/api/v1/statuses/{userId}', { userId: '_' + userId })
+ const response = await HttpClient.get(url)
+
+ return response.data.ocs.data
+}
+
+/**
* Sets the status
*
* @param {string} statusType The status (online / away / dnd / invisible)
@@ -90,10 +86,25 @@ const clearMessage = async () => {
await HttpClient.delete(url)
}
+/**
+ * Revert the automated status
+ *
+ * @param {string} messageId ID of the message to revert
+ * @return {Promise<object>}
+ */
+const revertToBackupStatus = async (messageId) => {
+ const url = generateOcsUrl('apps/user_status/api/v1/user_status/revert/{messageId}', { messageId })
+ const response = await HttpClient.delete(url)
+
+ return response.data.ocs.data
+}
+
export {
fetchCurrentStatus,
+ fetchBackupStatus,
setStatus,
setCustomMessage,
setPredefinedMessage,
clearMessage,
+ revertToBackupStatus,
}
diff --git a/apps/user_status/src/store/index.js b/apps/user_status/src/store/index.js
index c2c270d14d8..d9cfe674165 100644
--- a/apps/user_status/src/store/index.js
+++ b/apps/user_status/src/store/index.js
@@ -1,29 +1,13 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import Vue from 'vue'
import Vuex, { Store } from 'vuex'
-import predefinedStatuses from './predefinedStatuses'
-import userStatus from './userStatus'
+import predefinedStatuses from './predefinedStatuses.js'
+import userStatus from './userStatus.js'
+import userBackupStatus from './userBackupStatus.js'
Vue.use(Vuex)
@@ -31,6 +15,7 @@ export default new Store({
modules: {
predefinedStatuses,
userStatus,
+ userBackupStatus,
},
strict: true,
})
diff --git a/apps/user_status/src/store/predefinedStatuses.js b/apps/user_status/src/store/predefinedStatuses.js
index e41ba24f7dd..6d592ca627e 100644
--- a/apps/user_status/src/store/predefinedStatuses.js
+++ b/apps/user_status/src/store/predefinedStatuses.js
@@ -1,26 +1,9 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-import { fetchAllPredefinedStatuses } from '../services/predefinedStatusService'
+import { fetchAllPredefinedStatuses } from '../services/predefinedStatusService.js'
const state = {
predefinedStatuses: [],
@@ -35,11 +18,15 @@ const mutations = {
* @param {object} status The status to add
*/
addPredefinedStatus(state, status) {
- state.predefinedStatuses.push(status)
+ state.predefinedStatuses = [...state.predefinedStatuses, status]
},
}
-const getters = {}
+const getters = {
+ statusesHaveLoaded(state) {
+ return state.predefinedStatuses.length > 0
+ },
+}
const actions = {
diff --git a/apps/user_status/src/store/userBackupStatus.js b/apps/user_status/src/store/userBackupStatus.js
new file mode 100644
index 00000000000..78e5318de9d
--- /dev/null
+++ b/apps/user_status/src/store/userBackupStatus.js
@@ -0,0 +1,102 @@
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {
+ fetchBackupStatus,
+ revertToBackupStatus,
+} from '../services/statusService.js'
+import { getCurrentUser } from '@nextcloud/auth'
+import { emit } from '@nextcloud/event-bus'
+
+const state = {
+ // Status (online / away / dnd / invisible / offline)
+ status: null,
+ // Whether the status is user-defined
+ statusIsUserDefined: null,
+ // A custom message set by the user
+ message: null,
+ // The icon selected by the user
+ icon: null,
+ // When to automatically clean the status
+ clearAt: null,
+ // Whether the message is predefined
+ // (and can automatically be translated by Nextcloud)
+ messageIsPredefined: null,
+ // The id of the message in case it's predefined
+ messageId: null,
+}
+
+const mutations = {
+ /**
+ * Loads the status from initial state
+ *
+ * @param {object} state The Vuex state
+ * @param {object} data The destructuring object
+ * @param {string} data.status The status type
+ * @param {boolean} data.statusIsUserDefined Whether or not this status is user-defined
+ * @param {string} data.message The message
+ * @param {string} data.icon The icon
+ * @param {number} data.clearAt When to automatically clear the status
+ * @param {boolean} data.messageIsPredefined Whether or not the message is predefined
+ * @param {string} data.messageId The id of the predefined message
+ */
+ loadBackupStatusFromServer(state, { status, statusIsUserDefined, message, icon, clearAt, messageIsPredefined, messageId }) {
+ state.status = status
+ state.message = message
+ state.icon = icon
+
+ // Don't overwrite certain values if the refreshing comes in via short updates
+ // E.g. from talk participant list which only has the status, message and icon
+ if (typeof statusIsUserDefined !== 'undefined') {
+ state.statusIsUserDefined = statusIsUserDefined
+ }
+ if (typeof clearAt !== 'undefined') {
+ state.clearAt = clearAt
+ }
+ if (typeof messageIsPredefined !== 'undefined') {
+ state.messageIsPredefined = messageIsPredefined
+ }
+ if (typeof messageId !== 'undefined') {
+ state.messageId = messageId
+ }
+ },
+}
+
+const getters = {}
+
+const actions = {
+ /**
+ * Re-fetches the status from the server
+ *
+ * @param {object} vuex The Vuex destructuring object
+ * @param {Function} vuex.commit The Vuex commit function
+ * @return {Promise<void>}
+ */
+ async fetchBackupFromServer({ commit }) {
+ try {
+ const status = await fetchBackupStatus(getCurrentUser()?.uid)
+ commit('loadBackupStatusFromServer', status)
+ } catch (e) {
+ // Ignore missing user backup status
+ }
+ },
+
+ async revertBackupFromServer({ commit }, { messageId }) {
+ const status = await revertToBackupStatus(messageId)
+ if (status) {
+ commit('loadBackupStatusFromServer', {})
+ commit('loadStatusFromServer', status)
+ emit('user_status:status.updated', {
+ status: status.status,
+ message: status.message,
+ icon: status.icon,
+ clearAt: status.clearAt,
+ userId: getCurrentUser()?.uid,
+ })
+ }
+ },
+}
+
+export default { state, mutations, getters, actions }
diff --git a/apps/user_status/src/store/userStatus.js b/apps/user_status/src/store/userStatus.js
index 6d8b5bd1e1f..9bc86ab5062 100644
--- a/apps/user_status/src/store/userStatus.js
+++ b/apps/user_status/src/store/userStatus.js
@@ -1,23 +1,6 @@
/**
- * @copyright Copyright (c) 2020 Georg Ehrke
- *
- * @author Georg Ehrke <oc.list@georgehrke.com>
- *
- * @license AGPL-3.0-or-later
- *
- * 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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
import {
@@ -26,16 +9,16 @@ import {
setPredefinedMessage,
setCustomMessage,
clearMessage,
-} from '../services/statusService'
+} from '../services/statusService.js'
import { loadState } from '@nextcloud/initial-state'
import { getCurrentUser } from '@nextcloud/auth'
-import { getTimestampForClearAt } from '../services/clearAtService'
+import { getTimestampForClearAt } from '../services/clearAtService.js'
import { emit } from '@nextcloud/event-bus'
const state = {
// Status (online / away / dnd / invisible / offline)
status: null,
- // Whether or not the status is user-defined
+ // Whether the status is user-defined
statusIsUserDefined: null,
// A custom message set by the user
message: null,
@@ -43,7 +26,7 @@ const state = {
icon: null,
// When to automatically clean the status
clearAt: null,
- // Whether or not the message is predefined
+ // Whether the message is predefined
// (and can automatically be translated by Nextcloud)
messageIsPredefined: null,
// The id of the message in case it's predefined
diff --git a/apps/user_status/src/views/Dashboard.vue b/apps/user_status/src/views/Dashboard.vue
deleted file mode 100644
index ef5a9832ebc..00000000000
--- a/apps/user_status/src/views/Dashboard.vue
+++ /dev/null
@@ -1,121 +0,0 @@
-<!--
- - @copyright Copyright (c) 2020 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>
- <NcDashboardWidget id="user-status_panel"
- :items="items"
- :loading="loading"
- :empty-content-message="t('user_status', 'No recent status changes')">
- <template #default="{ item }">
- <NcDashboardWidgetItem :main-text="item.mainText"
- :sub-text="item.subText">
- <template #avatar>
- <NcAvatar class="item-avatar"
- :size="44"
- :user="item.avatarUsername"
- :display-name="item.mainText"
- :show-user-status="false"
- :show-user-status-compact="false" />
- </template>
- </NcDashboardWidgetItem>
- </template>
- <template #emptyContentIcon>
- <div class="icon-user-status-dark" />
- </template>
- </NcDashboardWidget>
-</template>
-
-<script>
-import { loadState } from '@nextcloud/initial-state'
-import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar'
-import NcDashboardWidget from '@nextcloud/vue/dist/Components/NcDashboardWidget'
-import NcDashboardWidgetItem from '@nextcloud/vue/dist/Components/NcDashboardWidgetItem'
-import moment from '@nextcloud/moment'
-
-export default {
- name: 'Dashboard',
- components: {
- NcAvatar,
- NcDashboardWidget,
- NcDashboardWidgetItem,
- },
- data() {
- return {
- statuses: [],
- loading: true,
- }
- },
- computed: {
- items() {
- return this.statuses.map((item) => {
- const icon = item.icon || ''
- let message = item.message || ''
- if (message === '') {
- if (item.status === 'away') {
- message = t('user_status', 'Away')
- }
- if (item.status === 'dnd') {
- message = t('user_status', 'Do not disturb')
- }
- }
- const status = item.icon !== '' ? `${icon} ${message}` : message
-
- let subText
- if (item.icon === null && message === '' && item.timestamp === null) {
- subText = ''
- } else if (item.icon === null && message === '' && item.timestamp !== null) {
- subText = moment(item.timestamp, 'X').fromNow()
- } else if (item.timestamp !== null) {
- subText = this.t('user_status', '{status}, {timestamp}', {
- status,
- timestamp: moment(item.timestamp, 'X').fromNow(),
- }, null, { escape: false, sanitize: false })
- } else {
- subText = status
- }
-
- return {
- mainText: item.displayName,
- subText,
- avatarUsername: item.userId,
- }
- })
- },
- },
- mounted() {
- try {
- this.statuses = loadState('user_status', 'dashboard_data')
- this.loading = false
- } catch (e) {
- console.error(e)
- }
- },
-}
-</script>
-
-<style lang="scss">
-.icon-user-status-dark {
- width: 64px;
- height: 64px;
- background-size: 64px;
- filter: var(--background-invert-if-dark);
-}
-</style>