Render custom emojis in display names
This commit is contained in:
parent
0b99590069
commit
8321999d2b
16 changed files with 118 additions and 42 deletions
|
@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Render custom emojis in display names.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Use `/@username` routes by default.
|
- Use `/@username` routes by default.
|
||||||
|
|
17
src/api/emojis.ts
Normal file
17
src/api/emojis.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
export interface CustomEmoji {
|
||||||
|
shortcode: string,
|
||||||
|
url: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceShortcodes(text: string, emojis: CustomEmoji[]): string {
|
||||||
|
return text.replace(/:([\w.]+):/g, (match, shortcode) => {
|
||||||
|
const emoji = emojis.find((emoji) => {
|
||||||
|
return emoji.shortcode === shortcode
|
||||||
|
})
|
||||||
|
if (emoji) {
|
||||||
|
return `<img class="emoji" title=":${emoji.shortcode}:" alt=":${emoji.shortcode}:" src="${emoji.url}">`
|
||||||
|
} else {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import { BACKEND_URL } from "@/constants"
|
import { BACKEND_URL } from "@/constants"
|
||||||
import { PAGE_SIZE, http } from "./common"
|
import { PAGE_SIZE, http } from "./common"
|
||||||
|
import { CustomEmoji } from "./emojis"
|
||||||
import { defaultProfile, Profile } from "./users"
|
import { defaultProfile, Profile } from "./users"
|
||||||
|
|
||||||
export interface Attachment {
|
export interface Attachment {
|
||||||
|
@ -52,11 +53,6 @@ export interface Tag {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CustomEmoji {
|
|
||||||
shortcode: string,
|
|
||||||
url: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Post {
|
export interface Post {
|
||||||
id: string;
|
id: string;
|
||||||
uri: string;
|
uri: string;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { RouteLocationRaw } from "vue-router"
|
||||||
import { BACKEND_URL } from "@/constants"
|
import { BACKEND_URL } from "@/constants"
|
||||||
import { createDidFromEthereumAddress } from "@/utils/did"
|
import { createDidFromEthereumAddress } from "@/utils/did"
|
||||||
import { PAGE_SIZE, http } from "./common"
|
import { PAGE_SIZE, http } from "./common"
|
||||||
|
import { CustomEmoji } from "./emojis"
|
||||||
|
|
||||||
export const EXTRA_FIELD_COUNT_MAX = 10
|
export const EXTRA_FIELD_COUNT_MAX = 10
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ export interface Profile {
|
||||||
identity_proofs: ProfileField[];
|
identity_proofs: ProfileField[];
|
||||||
payment_options: ProfilePaymentOption[];
|
payment_options: ProfilePaymentOption[];
|
||||||
fields: ProfileField[];
|
fields: ProfileField[];
|
||||||
|
emojis: CustomEmoji[],
|
||||||
|
|
||||||
followers_count: number;
|
followers_count: number;
|
||||||
following_count: number;
|
following_count: number;
|
||||||
|
@ -69,6 +71,7 @@ export function defaultProfile(): Profile {
|
||||||
identity_proofs: [],
|
identity_proofs: [],
|
||||||
payment_options: [],
|
payment_options: [],
|
||||||
fields: [],
|
fields: [],
|
||||||
|
emojis: [],
|
||||||
followers_count: 0,
|
followers_count: 0,
|
||||||
following_count: 0,
|
following_count: 0,
|
||||||
subscribers_count: 0,
|
subscribers_count: 0,
|
||||||
|
|
|
@ -15,12 +15,12 @@
|
||||||
<avatar :profile="post.account"></avatar>
|
<avatar :profile="post.account"></avatar>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="display-name"
|
class="display-name-link"
|
||||||
:href="post.account.url"
|
:href="post.account.url"
|
||||||
:title="author.getDisplayName()"
|
:title="author.getDisplayName()"
|
||||||
@click="openProfile($event, post.account)"
|
@click="openProfile($event, post.account)"
|
||||||
>
|
>
|
||||||
{{ author.getDisplayName() }}
|
<profile-display-name :profile="author"></profile-display-name>
|
||||||
</a>
|
</a>
|
||||||
<div class="actor-address" :title="'@' + getActorAddress(post.account)">
|
<div class="actor-address" :title="'@' + getActorAddress(post.account)">
|
||||||
@{{ getActorAddress(post.account) }}
|
@{{ getActorAddress(post.account) }}
|
||||||
|
@ -80,9 +80,8 @@
|
||||||
>
|
>
|
||||||
<div class="quote-header">
|
<div class="quote-header">
|
||||||
<avatar :profile="linkedPost.account"></avatar>
|
<avatar :profile="linkedPost.account"></avatar>
|
||||||
<span class="display-name">
|
<profile-display-name :profile="getQuoteAuthor(linkedPost)">
|
||||||
{{ getQuoteAuthorDisplayName(linkedPost) }}
|
</profile-display-name>
|
||||||
</span>
|
|
||||||
<span class="actor-address">
|
<span class="actor-address">
|
||||||
@{{ getActorAddress(linkedPost.account) }}
|
@{{ getActorAddress(linkedPost.account) }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -270,6 +269,7 @@ import CryptoAddress from "@/components/CryptoAddress.vue"
|
||||||
import PostAttachment from "@/components/PostAttachment.vue"
|
import PostAttachment from "@/components/PostAttachment.vue"
|
||||||
import PostContent from "@/components/PostContent.vue"
|
import PostContent from "@/components/PostContent.vue"
|
||||||
import PostEditor from "@/components/PostEditor.vue"
|
import PostEditor from "@/components/PostEditor.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import VisibilityIcon from "@/components/VisibilityIcon.vue"
|
import VisibilityIcon from "@/components/VisibilityIcon.vue"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
import { useCurrentUser } from "@/store/user"
|
import { useCurrentUser } from "@/store/user"
|
||||||
|
@ -351,9 +351,8 @@ function getReplyMentions(): Mention[] {
|
||||||
return props.post.mentions
|
return props.post.mentions
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQuoteAuthorDisplayName(post: Post): string | null {
|
function getQuoteAuthor(post: Post): ProfileWrapper {
|
||||||
const profile = new ProfileWrapper(post.account)
|
return new ProfileWrapper(post.account)
|
||||||
return profile.getDisplayName()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function canReply(): boolean {
|
function canReply(): boolean {
|
||||||
|
@ -607,7 +606,7 @@ async function onMintToken() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-name {
|
.display-name-link {
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { onMounted } from "vue"
|
||||||
import { $, $ref } from "vue/macros"
|
import { $, $ref } from "vue/macros"
|
||||||
import { useRouter } from "vue-router"
|
import { useRouter } from "vue-router"
|
||||||
|
|
||||||
|
import { replaceShortcodes } from "@/api/emojis"
|
||||||
import { Post } from "@/api/posts"
|
import { Post } from "@/api/posts"
|
||||||
import { useCurrentUser } from "@/store/user"
|
import { useCurrentUser } from "@/store/user"
|
||||||
import { addGreentext } from "@/utils/greentext"
|
import { addGreentext } from "@/utils/greentext"
|
||||||
|
@ -72,16 +73,7 @@ function configureInlineLinks() {
|
||||||
function getContent(): string {
|
function getContent(): string {
|
||||||
let content = addGreentext(props.post.content)
|
let content = addGreentext(props.post.content)
|
||||||
// Replace emoji shortcodes
|
// Replace emoji shortcodes
|
||||||
content = content.replace(/:([\w.]+):/g, (match, shortcode) => {
|
content = replaceShortcodes(content, props.post.emojis)
|
||||||
const emoji = props.post.emojis.find((emoji) => {
|
|
||||||
return emoji.shortcode === shortcode
|
|
||||||
})
|
|
||||||
if (emoji) {
|
|
||||||
return `<img class="emoji" title=":${emoji.shortcode}:" alt=":${emoji.shortcode}:" src="${emoji.url}">`
|
|
||||||
} else {
|
|
||||||
return match
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -92,6 +84,8 @@ function getContent(): string {
|
||||||
@import "../styles/mixins";
|
@import "../styles/mixins";
|
||||||
|
|
||||||
.post-content {
|
.post-content {
|
||||||
|
@include ugc-emoji;
|
||||||
|
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
padding: $block-inner-padding;
|
padding: $block-inner-padding;
|
||||||
|
@ -168,10 +162,6 @@ function getContent(): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.emoji) {
|
:deep(.emoji) {
|
||||||
height: 24px;
|
|
||||||
vertical-align: text-bottom;
|
|
||||||
width: 24px;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
transition: 100ms linear;
|
transition: 100ms linear;
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'profile-by-acct', params: { acct: post.account.acct }}"
|
:to="{ name: 'profile-by-acct', params: { acct: post.account.acct }}"
|
||||||
:title="getActorAddress(post.account)"
|
:title="getActorAddress(post.account)"
|
||||||
|
class="display-name-link"
|
||||||
>
|
>
|
||||||
{{ author.getDisplayName() }}
|
<profile-display-name :profile="author"></profile-display-name>
|
||||||
</router-link>
|
</router-link>
|
||||||
<span>reposted</span>
|
<span>reposted</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +33,7 @@ import { $, $computed } from "vue/macros"
|
||||||
import type { Post as PostObject } from "@/api/posts"
|
import type { Post as PostObject } from "@/api/posts"
|
||||||
import { ProfileWrapper } from "@/api/users"
|
import { ProfileWrapper } from "@/api/users"
|
||||||
import Post from "@/components/Post.vue"
|
import Post from "@/components/Post.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
|
|
||||||
/* eslint-disable-next-line no-undef */
|
/* eslint-disable-next-line no-undef */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<div class="avatar-row">
|
<div class="avatar-row">
|
||||||
<avatar :profile="profile"></avatar>
|
<avatar :profile="profile"></avatar>
|
||||||
<div class="name-group">
|
<div class="name-group">
|
||||||
<div class="display-name">{{ profile.getDisplayName() }}</div>
|
<profile-display-name :profile="profile"></profile-display-name>
|
||||||
<div class="actor-address">@{{ getActorAddress(profile) }}</div>
|
<div class="actor-address">@{{ getActorAddress(profile) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,6 +27,7 @@ import { $computed } from "vue/macros"
|
||||||
|
|
||||||
import { Profile, ProfileWrapper } from "@/api/users"
|
import { Profile, ProfileWrapper } from "@/api/users"
|
||||||
import Avatar from "@/components/Avatar.vue"
|
import Avatar from "@/components/Avatar.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
|
|
||||||
/* eslint-disable-next-line no-undef */
|
/* eslint-disable-next-line no-undef */
|
||||||
|
|
32
src/components/ProfileDisplayName.vue
Normal file
32
src/components/ProfileDisplayName.vue
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<template>
|
||||||
|
<span
|
||||||
|
class="display-name"
|
||||||
|
v-html="getDisplayNameHtml()"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { replaceShortcodes } from "@/api/emojis"
|
||||||
|
import { ProfileWrapper } from "@/api/users"
|
||||||
|
|
||||||
|
/* eslint-disable-next-line no-undef */
|
||||||
|
const props = defineProps<{
|
||||||
|
profile: ProfileWrapper,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function getDisplayNameHtml(): string {
|
||||||
|
const profile = props.profile
|
||||||
|
const escaped = new Option(profile.getDisplayName()).innerHTML
|
||||||
|
return replaceShortcodes(escaped, profile.emojis)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "../styles/mixins";
|
||||||
|
@import "../styles/theme";
|
||||||
|
|
||||||
|
.display-name {
|
||||||
|
@include ugc-emoji;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="profile">
|
<div class="profile">
|
||||||
<avatar :profile="profile"></avatar>
|
<avatar :profile="profile"></avatar>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<div class="display-name">{{ profile.getDisplayName() }}</div>
|
<profile-display-name :profile="profile"></profile-display-name>
|
||||||
<div class="actor-address">@{{ getActorAddress(profile) }}</div>
|
<div class="actor-address">@{{ getActorAddress(profile) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,6 +13,7 @@ import { $computed } from "vue/macros"
|
||||||
|
|
||||||
import { Profile, ProfileWrapper } from "@/api/users"
|
import { Profile, ProfileWrapper } from "@/api/users"
|
||||||
import Avatar from "@/components/Avatar.vue"
|
import Avatar from "@/components/Avatar.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
|
|
||||||
const { getActorAddress } = useInstanceInfo()
|
const { getActorAddress } = useInstanceInfo()
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
:to="{ name: 'profile-by-acct', params: { acct: sender.acct }}"
|
:to="{ name: 'profile-by-acct', params: { acct: sender.acct }}"
|
||||||
>
|
>
|
||||||
<avatar :profile="sender"></avatar>
|
<avatar :profile="sender"></avatar>
|
||||||
<div class="display-name">{{ sender.getDisplayName() }}</div>
|
<profile-display-name :profile="sender"></profile-display-name>
|
||||||
<div class="wallet-address">{{ walletAddress ? walletAddress.toLowerCase() : '?' }}</div>
|
<div class="wallet-address">{{ walletAddress ? walletAddress.toLowerCase() : '?' }}</div>
|
||||||
</component>
|
</component>
|
||||||
<div class="separator">
|
<div class="separator">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
:to="{ name: 'profile-by-acct', params: { acct: recipient.acct }}"
|
:to="{ name: 'profile-by-acct', params: { acct: recipient.acct }}"
|
||||||
>
|
>
|
||||||
<avatar :profile="recipient"></avatar>
|
<avatar :profile="recipient"></avatar>
|
||||||
<div class="display-name">{{ recipient.getDisplayName() }}</div>
|
<profile-display-name :profile="recipient"></profile-display-name>
|
||||||
<div class="wallet-address">{{ recipientEthereumAddress }}</div>
|
<div class="wallet-address">{{ recipientEthereumAddress }}</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,6 +125,7 @@ import {
|
||||||
} from "@/api/subscriptions-ethereum"
|
} from "@/api/subscriptions-ethereum"
|
||||||
import Avatar from "@/components/Avatar.vue"
|
import Avatar from "@/components/Avatar.vue"
|
||||||
import Loader from "@/components/Loader.vue"
|
import Loader from "@/components/Loader.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import { useWallet } from "@/composables/wallet"
|
import { useWallet } from "@/composables/wallet"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
import { useCurrentUser } from "@/store/user"
|
import { useCurrentUser } from "@/store/user"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
:to="{ name: 'profile-by-acct', params: { acct: sender.acct } }"
|
:to="{ name: 'profile-by-acct', params: { acct: sender.acct } }"
|
||||||
>
|
>
|
||||||
<avatar :profile="sender"></avatar>
|
<avatar :profile="sender"></avatar>
|
||||||
<div class="display-name">{{ sender.getDisplayName() }}</div>
|
<profile-display-name :profile="sender"></profile-display-name>
|
||||||
</component>
|
</component>
|
||||||
<div class="separator">
|
<div class="separator">
|
||||||
<img :src="require('@/assets/feather/arrow-right.svg')">
|
<img :src="require('@/assets/feather/arrow-right.svg')">
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
:to="{ name: 'profile-by-acct', params: { acct: recipient.acct }}"
|
:to="{ name: 'profile-by-acct', params: { acct: recipient.acct }}"
|
||||||
>
|
>
|
||||||
<avatar :profile="recipient"></avatar>
|
<avatar :profile="recipient"></avatar>
|
||||||
<div class="display-name">{{ recipient.getDisplayName() }}</div>
|
<profile-display-name :profile="recipient"></profile-display-name>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<form class="sender" v-if="sender.id === ''">
|
<form class="sender" v-if="sender.id === ''">
|
||||||
|
@ -131,6 +131,7 @@ import { defaultProfile, Profile, ProfilePaymentOption, ProfileWrapper } from "@
|
||||||
import Avatar from "@/components/Avatar.vue"
|
import Avatar from "@/components/Avatar.vue"
|
||||||
import Loader from "@/components/Loader.vue"
|
import Loader from "@/components/Loader.vue"
|
||||||
import QrCode from "@/components/QrCode.vue"
|
import QrCode from "@/components/QrCode.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import { useCurrentUser } from "@/store/user"
|
import { useCurrentUser } from "@/store/user"
|
||||||
import { formatDate } from "@/utils/dates"
|
import { formatDate } from "@/utils/dates"
|
||||||
import { createMoneroPaymentUri } from "@/utils/monero"
|
import { createMoneroPaymentUri } from "@/utils/monero"
|
||||||
|
|
|
@ -158,3 +158,11 @@
|
||||||
color: $error-color;
|
color: $error-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin ugc-emoji {
|
||||||
|
:deep(.emoji) {
|
||||||
|
height: 24px;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,8 +20,10 @@
|
||||||
<router-link
|
<router-link
|
||||||
:title="getSenderInfo(notification)"
|
:title="getSenderInfo(notification)"
|
||||||
:to="{ name: 'profile-by-acct', params: { acct: notification.account.acct } }"
|
:to="{ name: 'profile-by-acct', params: { acct: notification.account.acct } }"
|
||||||
|
class="display-name-link"
|
||||||
>
|
>
|
||||||
{{ getSenderName(notification) }}
|
<profile-display-name :profile="getSender(notification)">
|
||||||
|
</profile-display-name>
|
||||||
</router-link>
|
</router-link>
|
||||||
<span v-if="notification.type === 'follow'">followed you</span>
|
<span v-if="notification.type === 'follow'">followed you</span>
|
||||||
<span v-else-if="notification.type === 'reply'">replied to your post</span>
|
<span v-else-if="notification.type === 'reply'">replied to your post</span>
|
||||||
|
@ -47,7 +49,8 @@
|
||||||
<div class="floating-avatar">
|
<div class="floating-avatar">
|
||||||
<avatar :profile="notification.account"></avatar>
|
<avatar :profile="notification.account"></avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="display-name">{{ getSenderName(notification) }}</div>
|
<profile-display-name :profile="getSender(notification)">
|
||||||
|
</profile-display-name>
|
||||||
<div class="actor-address">@{{ getActorAddress(notification.account) }}</div>
|
<div class="actor-address">@{{ getActorAddress(notification.account) }}</div>
|
||||||
<div class="timestamp">{{ humanizeDate(notification.created_at) }}</div>
|
<div class="timestamp">{{ humanizeDate(notification.created_at) }}</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -73,6 +76,7 @@ import { getNotifications, Notification } from "@/api/notifications"
|
||||||
import { ProfileWrapper } from "@/api/users"
|
import { ProfileWrapper } from "@/api/users"
|
||||||
import Avatar from "@/components/Avatar.vue"
|
import Avatar from "@/components/Avatar.vue"
|
||||||
import Post from "@/components/Post.vue"
|
import Post from "@/components/Post.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||||
import { useInstanceInfo } from "@/store/instance"
|
import { useInstanceInfo } from "@/store/instance"
|
||||||
import { useNotifications } from "@/store/notifications"
|
import { useNotifications } from "@/store/notifications"
|
||||||
|
@ -94,13 +98,13 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function getSenderName(notification: Notification): string {
|
function getSender(notification: Notification): ProfileWrapper {
|
||||||
const sender = new ProfileWrapper(notification.account)
|
return new ProfileWrapper(notification.account)
|
||||||
return sender.getDisplayName()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSenderInfo(notification: Notification): string {
|
function getSenderInfo(notification: Notification): string {
|
||||||
return `${getSenderName(notification)} (${getActorAddress(notification.account)})`
|
const senderName = getSender(notification).getDisplayName()
|
||||||
|
return `${senderName} (${getActorAddress(notification.account)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPostDeleted(notificationIndex: number) {
|
function onPostDeleted(notificationIndex: number) {
|
||||||
|
|
|
@ -105,7 +105,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="name-buttons-group">
|
<div class="name-buttons-group">
|
||||||
<div class="name-group">
|
<div class="name-group">
|
||||||
<div class="display-name">{{ profile.getDisplayName() }}</div>
|
<profile-display-name :profile="profile">
|
||||||
|
</profile-display-name>
|
||||||
<div class="actor-address">@{{ actorAddress }}</div>
|
<div class="actor-address">@{{ actorAddress }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
|
@ -280,6 +281,7 @@ import {
|
||||||
import Avatar from "@/components/Avatar.vue"
|
import Avatar from "@/components/Avatar.vue"
|
||||||
import Loader from "@/components/Loader.vue"
|
import Loader from "@/components/Loader.vue"
|
||||||
import PostList from "@/components/PostList.vue"
|
import PostList from "@/components/PostList.vue"
|
||||||
|
import ProfileDisplayName from "@/components/ProfileDisplayName.vue"
|
||||||
import ProfileListItem from "@/components/ProfileListItem.vue"
|
import ProfileListItem from "@/components/ProfileListItem.vue"
|
||||||
import SidebarLayout from "@/components/SidebarLayout.vue"
|
import SidebarLayout from "@/components/SidebarLayout.vue"
|
||||||
import { useEthereumAddressVerification } from "@/composables/ethereum-address-verification"
|
import { useEthereumAddressVerification } from "@/composables/ethereum-address-verification"
|
||||||
|
|
15
tests/unit/profile-wrapper.spec.ts
Normal file
15
tests/unit/profile-wrapper.spec.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { expect } from "chai"
|
||||||
|
import { defaultProfile, ProfileWrapper } from "@/api/users"
|
||||||
|
|
||||||
|
describe("ProfileWrapper", () => {
|
||||||
|
it("Replace invisible characters", () => {
|
||||||
|
const hidden = ""
|
||||||
|
expect(hidden.length).to.equal(1)
|
||||||
|
const profile = new ProfileWrapper({
|
||||||
|
...defaultProfile(),
|
||||||
|
username: "test",
|
||||||
|
display_name: hidden,
|
||||||
|
})
|
||||||
|
expect(profile.getDisplayName()).to.equal("test")
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue