Skip to content

Commit

Permalink
Merge pull request #14099 from nextcloud/feat/11697/compact-list-ui
Browse files Browse the repository at this point in the history
feat(conversations):  compact view
  • Loading branch information
nickvergessen authored Jan 10, 2025
2 parents 5408fe2 + 2647b89 commit f441b4a
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 32 deletions.
7 changes: 6 additions & 1 deletion src/components/AvatarWrapper/AvatarWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
:preloaded-user-status="preloadedUserStatus"
:size="size" />
<!-- Override user status for federated users -->
<span v-if="showUserStatus && isFederatedUser"
<span v-if="showUserStatus && isFederatedUser && !compact"
class="avatar-wrapper__user-status"
role="img"
aria-hidden="false"
Expand Down Expand Up @@ -132,6 +132,11 @@ export default {
type: Boolean,
default: false,
},

compact: {
type: Boolean,
default: false,
},
},

setup() {
Expand Down
11 changes: 8 additions & 3 deletions src/components/ConversationIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
:height="size"
:alt="item.displayName"
class="avatar icon">
<span v-if="!hideUserStatus && conversationType"
<span v-if="!hideUserStatus && conversationType && !compact"
class="conversation-icon__type"
role="img"
aria-hidden="false"
Expand Down Expand Up @@ -131,6 +131,11 @@ export default {
type: Number,
default: AVATAR.SIZE.DEFAULT,
},

compact: {
type: Boolean,
default: false,
}
},

setup() {
Expand All @@ -143,11 +148,11 @@ export default {

computed: {
showCall() {
return !this.hideCall && this.item.hasCall
return !this.hideCall && this.item.hasCall && !this.compact
},

showFavorite() {
return !this.hideFavorite && this.item.isFavorite
return !this.hideFavorite && this.item.isFavorite && !this.compact
},

preloadedUserStatus() {
Expand Down
66 changes: 62 additions & 4 deletions src/components/LeftSidebar/ConversationsList/Conversation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,36 @@
:name="item.displayName"
:title="item.displayName"
:data-nav-id="`conversation_${item.token}`"
:class="['conversation', { 'conversation--active': isActive }]"
class="conversation"
:class="{
'conversation--active': isActive,
'conversation--compact': compact,
'conversation--compact__read': compact && !item.unreadMessages
}"
:actions-aria-label="t('spreed', 'Conversation actions')"
:to="to"
:bold="!!item.unreadMessages"
:counter-number="item.unreadMessages"
:counter-type="counterType"
force-menu
:compact="compact"
@click="onClick">
<template #name>
<template v-if="compact && iconType">
<component :is="iconType.component" :size="15" :fill-color="iconType.color" />
<span class="hidden-visually">{{ iconType.text }}</span>
</template>
<span>{{ item.displayName }}</span>
</template>
<template #icon>
<ConversationIcon :item="item" :hide-favorite="false" :hide-call="false" />
<ConversationIcon :item="item"
:hide-favorite="false"
:hide-call="false"
:compact="compact"
:show-user-online-status="compact"
:size="compact? AVATAR.SIZE.COMPACT : AVATAR.SIZE.DEFAULT" />
</template>
<template #subname>
<template v-if="!compact" #subname>
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="conversationInformation" />
</template>
Expand Down Expand Up @@ -210,6 +228,7 @@ import IconEye from 'vue-material-design-icons/Eye.vue'
import IconEyeOff from 'vue-material-design-icons/EyeOff.vue'
import IconPhoneRing from 'vue-material-design-icons/PhoneRing.vue'
import IconStar from 'vue-material-design-icons/Star.vue'
import IconVideo from 'vue-material-design-icons/Video.vue'
import IconVolumeHigh from 'vue-material-design-icons/VolumeHigh.vue'
import IconVolumeOff from 'vue-material-design-icons/VolumeOff.vue'

Expand All @@ -226,7 +245,7 @@ import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
import ConversationIcon from './../../ConversationIcon.vue'

import { useConversationInfo } from '../../../composables/useConversationInfo.js'
import { PARTICIPANT } from '../../../constants.js'
import { PARTICIPANT, AVATAR } from '../../../constants.js'
import { hasTalkFeature } from '../../../services/CapabilitiesManager.ts'
import { copyConversationLinkToClipboard } from '../../../utils/handleUrl.ts'

Expand Down Expand Up @@ -259,6 +278,7 @@ export default {
IconStar,
IconVolumeHigh,
IconVolumeOff,
IconVideo,
NcActionButton,
NcActionSeparator,
NcButton,
Expand Down Expand Up @@ -288,9 +308,15 @@ export default {
notificationCalls: PARTICIPANT.NOTIFY_CALLS.ON,
canDeleteConversation: false,
canLeaveConversation: false,
hasCall: false,
}
},
},

compact: {
type: Boolean,
default: false,
},
},

emits: ['click'],
Expand All @@ -303,6 +329,7 @@ export default {
const { counterType, conversationInformation } = useConversationInfo({ item, isSearchResult })

return {
AVATAR,
supportsArchive,
submenu,
isLeaveDialogOpen,
Expand Down Expand Up @@ -363,6 +390,23 @@ export default {
notificationCalls() {
return this.item.notificationCalls === PARTICIPANT.NOTIFY_CALLS.ON
},

iconType() {
if (this.item.hasCall) {
return {
component: 'IconVideo',
color: '#E9322D',
text: t('spreed', 'Call in progress')
}
} else if (this.item.isFavorite) {
return {
component: 'IconStar',
color: '#FFCC00',
text: t('spreed', 'Favorite')
}
}
return null
},
},

methods: {
Expand Down Expand Up @@ -510,6 +554,20 @@ export default {
border-color: var(--color-primary-element-hover);
}
}

&--compact {
padding-block: 2px !important; // Overwrite list-item 4px padding
&:deep(.list-item-content__name) {
display: flex;
gap: calc(var(--default-grid-baseline) / 2);
}
&__read {
&:deep(.list-item-content__name) {
font-weight: 400;
}
}

}
}

:deep(.dialog) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
:item-size="CONVERSATION_ITEM_SIZE"
key-field="token">
<template #default="{ item }">
<Conversation :item="item" />
<Conversation :item="item" :compact="compact" />
</template>
<template #after>
<LoadingPlaceholder v-if="loading" type="conversations" />
Expand All @@ -19,6 +19,7 @@
</template>

<script>
import { computed } from 'vue'
import { RecycleScroller } from 'vue-virtual-scroller'

import Conversation from './Conversation.vue'
Expand All @@ -28,13 +29,6 @@ import { AVATAR } from '../../../constants.js'

import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

/* Consider:
* avatar size (and two lines of text)
* list-item padding
* list-item__wrapper padding
*/
const CONVERSATION_ITEM_SIZE = AVATAR.SIZE.DEFAULT + 2 * 4 + 2 * 2

export default {
name: 'ConversationsListVirtual',

Expand All @@ -54,9 +48,20 @@ export default {
type: Boolean,
default: false,
},

compact: {
type: Boolean,
default: false,
},
},

setup() {
setup(props) {
/* Consider:
* avatar size (and two lines of text) or compact mode (34px)
* list-item padding
* list-item__wrapper padding
*/
const CONVERSATION_ITEM_SIZE = computed(() => props.compact ? 34 + 2 * 2 + 0 * 2 : AVATAR.SIZE.DEFAULT + 2 * 4 + 2 * 2)
return {
CONVERSATION_ITEM_SIZE,
}
Expand All @@ -71,7 +76,7 @@ export default {
*/
getFirstItemInViewportIndex() {
// (ceil to include partially) of (absolute number of items above viewport) + 1 (next item is in viewport) - 1 (index starts from 0)
return Math.ceil(this.$refs.scroller.$el.scrollTop / CONVERSATION_ITEM_SIZE)
return Math.ceil(this.$refs.scroller.$el.scrollTop / this.CONVERSATION_ITEM_SIZE)
},

/**
Expand All @@ -82,7 +87,7 @@ export default {
*/
getLastItemInViewportIndex() {
// (floor to include only fully visible) of (absolute number of items below and in viewport) - 1 (index starts from 0)
return Math.floor((this.$refs.scroller.$el.scrollTop + this.$refs.scroller.$el.clientHeight) / CONVERSATION_ITEM_SIZE) - 1
return Math.floor((this.$refs.scroller.$el.scrollTop + this.$refs.scroller.$el.clientHeight) / this.CONVERSATION_ITEM_SIZE) - 1
},

/**
Expand All @@ -106,7 +111,7 @@ export default {
*/
const doScroll = (to) => {
const ITEMS_TO_BORDER_AFTER_SCROLL = 1
const padding = ITEMS_TO_BORDER_AFTER_SCROLL * CONVERSATION_ITEM_SIZE
const padding = ITEMS_TO_BORDER_AFTER_SCROLL * this.CONVERSATION_ITEM_SIZE
const from = this.$refs.scroller.$el.scrollTop
const direction = from < to ? 1 : -1

Expand All @@ -123,10 +128,10 @@ export default {
}

if (index < firstItemIndex) { // Item is above
await doScroll(index * CONVERSATION_ITEM_SIZE)
await doScroll(index * this.CONVERSATION_ITEM_SIZE)
} else if (index > lastItemIndex) { // Item is below
// Position of item + item's height and move to bottom
await doScroll((index + 1) * CONVERSATION_ITEM_SIZE - viewportHeight)
await doScroll((index + 1) * this.CONVERSATION_ITEM_SIZE - viewportHeight)
}
},

Expand Down
Loading

0 comments on commit f441b4a

Please sign in to comment.