Ocelot-Social/webapp/components/UserTeaser/UserTeaserNonAnonymous.vue
Max 33274e5b9a
feat(webapp): user teaser popover (#8450)
* calculate distance between current user and queried user

* fix query for unset location

* use database to calculate distance

* rename distance to distance to me, 100% calculation done in DB

* distanceToMe tests

* lint fixes

* remove comments

* Show user teaser popover with badges, Desktop

* Refactor UserTeaser and add mobile popover support

* Avoid click propagation (WIP)

* Prevent event propagation

* Adjust alignment and font sizes

* More spacing for statistics

* Add distance, simplify user link

* Refactor location info into own component

* Add tests for UserTeaserPopup

* Refactor and test LocationInfo

* Query distanceToMe, rename distance to distanceToMe

* Update test

* Improve tests for UserTeaser, WIP

* Fix tests

* DistanceToMe on User instead of Location

* Revert "DistanceToMe on User instead of Location"

This reverts commit 96c9db00a44cd120e47bfe9534d3e066a194744c.

* Fix notifications

* Refactor UserTeaser and fix location info

* Fix group member crash

* Show 0 distance

* Fit in popover on small screens

* Allow access to profile on desktop

* Revert backend changes

* Load user teaser popover data only when needed

* Fix type mismatch

* Refactor for clarity and accessibility

* Litte refactorings and improvements

* Fix popover test

* Adapt and fix tests

* Fix tests and bugs

* Add placeholder

* cypress: adapt user teaser locator to changes

* Remove delays and scrolling

* Disable popovers in notification list and fix layout

* Remove flickering

* Make overlay catch all pointer events on touch devices

* Re-add attribute for E2E test

* Fix test, return to mouseover

* fix snapshot

---------

Co-authored-by: Ulf Gebhardt <ulf.gebhardt@webcraft-media.de>
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
Co-authored-by: mahula <lenzmath@posteo.de>
2025-05-05 23:54:13 +00:00

124 lines
3.6 KiB
Vue

<template>
<dropdown class="user-teaser">
<template #default="{ openMenu, closeMenu }">
<user-teaser-helper
v-if="showAvatar"
:link-to-profile="linkToProfile"
:show-popover="showPopover"
:user-link="userLink"
@open-menu="openMenu(false)"
@close-menu="closeMenu(false)"
data-test="avatarUserLink"
>
<profile-avatar :profile="user" size="small" />
</user-teaser-helper>
<div class="info flex-direction-column">
<div :class="wide ? 'flex-direction-row' : 'flex-direction-column'">
<user-teaser-helper
:link-to-profile="linkToProfile"
:show-popover="showPopover"
:user-link="userLink"
@open-menu="openMenu(false)"
@close-menu="closeMenu(false)"
>
<span class="slug">{{ userSlug }}</span>
<span class="name">{{ userName }}</span>
</user-teaser-helper>
<span v-if="wide">&nbsp;</span>
<span v-if="group">
<span class="text">
{{ $t('group.in') }}
</span>
<nuxt-link :to="groupLink">
<span class="text">
<span class="slug">{{ groupSlug }}</span>
<span v-if="!userOnly" class="name">{{ groupName }}</span>
</span>
</nuxt-link>
</span>
</div>
<span v-if="!userOnly && dateTime" class="text">
<base-icon name="clock" />
<date-time :date-time="dateTime" />
<slot name="dateTime"></slot>
</span>
</div>
</template>
<template #popover="{ isOpen }" v-if="showPopover">
<user-teaser-popover
v-if="isOpen"
:user-id="user.id"
:user-link="linkToProfile ? userLink : null"
/>
</template>
</dropdown>
</template>
<script>
import { mapGetters } from 'vuex'
import DateTime from '~/components/DateTime'
import Dropdown from '~/components/Dropdown'
import ProfileAvatar from '~/components/_new/generic/ProfileAvatar/ProfileAvatar'
import UserTeaserPopover from './UserTeaserPopover'
import UserTeaserHelper from './UserTeaserHelper.vue'
export default {
name: 'UserTeaser',
components: {
ProfileAvatar,
UserTeaserPopover,
UserTeaserHelper,
Dropdown,
DateTime,
},
props: {
linkToProfile: { type: Boolean, default: true },
user: { type: Object, default: null },
group: { type: Object, default: null },
wide: { type: Boolean, default: false },
showAvatar: { type: Boolean, default: true },
dateTime: { type: [Date, String], default: null },
showPopover: { type: Boolean, default: true },
},
computed: {
...mapGetters({
isModerator: 'auth/isModerator',
}),
itsMe() {
return this.user.slug === this.$store.getters['auth/user'].slug
},
userLink() {
const { id, slug } = this.user
if (!(id && slug)) return null
return { name: 'profile-id-slug', params: { slug, id } }
},
userSlug() {
const { slug } = this.user || {}
return slug && `@${slug}`
},
userName() {
const { name } = this.user || {}
return name || this.$t('profile.userAnonym')
},
userOnly() {
return !this.dateTime && !this.group
},
groupLink() {
const { id, slug } = this.group
if (!(id && slug)) return ''
return { name: 'groups-id-slug', params: { slug, id } }
},
groupSlug() {
const { slug } = this.group || {}
return slug && `&${slug}`
},
groupName() {
const { name } = this.group || {}
return name || this.$t('profile.userAnonym')
},
},
}
</script>