123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- <template>
- <div class="profile-hover-card">
- <div class="profile-hover-card-inner">
- <div class="d-flex justify-content-between align-items-start" style="max-width: 240px;">
- <a
- :href="profile.url"
- @click.prevent="goToProfile()">
- <img
- :src="profile.avatar"
- width="50"
- height="50"
- class="avatar"
- onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
- </a>
- <div v-if="user.id == profile.id">
- <a class="btn btn-outline-primary px-3 py-1 font-weight-bold rounded-pill" href="/settings/home">Edit Profile</a>
- </div>
- <div v-if="user.id != profile.id && relationship">
- <button
- v-if="relationship.following"
- class="btn btn-outline-primary px-3 py-1 font-weight-bold rounded-pill"
- :disabled="isLoading"
- @click="performUnfollow()">
- <span v-if="isLoading"><b-spinner small /></span>
- <span v-else>Following</span>
- </button>
- <div v-else>
- <button
- v-if="!relationship.requested"
- class="btn btn-primary primary px-3 py-1 font-weight-bold rounded-pill"
- :disabled="isLoading"
- @click="performFollow()">
- <span v-if="isLoading"><b-spinner small /></span>
- <span v-else>Follow</span>
- </button>
- <button v-else class="btn btn-primary primary px-3 py-1 font-weight-bold rounded-pill" disabled>Follow Requested</button>
- </div>
- </div>
- </div>
- <p class="display-name">
- <a
- :href="profile.url"
- @click.prevent="goToProfile()"
- v-html="getDisplayName()">
- </a>
- </p>
- <div class="username">
- <a
- :href="profile.url"
- class="username-link"
- @click.prevent="goToProfile()">
- @{{ getUsername() }}
- </a>
- <p v-if="user.id != profile.id && relationship && relationship.followed_by" class="username-follows-you">
- <span>Follows You</span>
- </p>
- </div>
- <p
- v-if="profile.hasOwnProperty('pronouns') && profile.pronouns && profile.pronouns.length"
- class="pronouns">
- {{ profile.pronouns.join(', ') }}
- </p>
- <p class="bio" v-html="bio"></p>
- <p class="stats">
- <span class="stats-following">
- <span class="following-count">{{ formatCount(profile.following_count) }}</span> Following
- </span>
- <span class="stats-followers">
- <span class="followers-count">{{ formatCount(profile.followers_count) }}</span> Followers
- </span>
- </p>
- </div>
- </div>
- </template>
- <script type="text/javascript">
- import ReadMore from './../post/ReadMore.vue';
- import { mapGetters } from 'vuex';
- export default {
- props: {
- profile: {
- type: Object
- },
- // relationship: {
- // type: Object
- // }
- },
- components: {
- ReadMore
- },
- data() {
- return {
- user: window._sharedData.user,
- bio: undefined,
- isLoading: false,
- relationship: undefined
- };
- },
- mounted() {
- this.rewriteLinks();
- this.relationship = this.$store.getters.getRelationship(this.profile.id);
- if(!this.relationship && this.profile.id != this.user.id) {
- axios.get('/api/pixelfed/v1/accounts/relationships', {
- params: {
- 'id[]': this.profile.id
- }
- })
- .then(res => {
- this.relationship = res.data[0];
- this.$store.commit('updateRelationship', res.data);
- })
- }
- },
- computed: {
- ...mapGetters([
- 'getCustomEmoji'
- ])
- },
- methods: {
- getDisplayName() {
- let self = this;
- let profile = this.profile;
- let dn = profile.display_name;
- if(!dn) {
- return profile.username;
- }
- if(dn.includes(':')) {
- let re = /(<a?)?:\w+:(\d{18}>)?/g;
- let un = dn.replaceAll(re, function(em) {
- let shortcode = em.slice(1, em.length - 1);
- let emoji = self.getCustomEmoji.filter(e => {
- return e.shortcode == shortcode;
- });
- return emoji.length ? `<img draggable="false" class="emojione custom-emoji" alt="${emoji[0].shortcode}" title="${emoji[0].shortcode}" src="${emoji[0].url}" data-original="${emoji[0].url}" data-static="${emoji[0].static_url}" width="16" height="16" onerror="this.onerror=null;this.src='/storage/emoji/missing.png';" />`: em;
- });
- return un;
- } else {
- return dn;
- }
- },
- getUsername() {
- let profile = this.profile;
- // if(profile.hasOwnProperty('local') && profile.local) {
- // return profile.acct + '@' + window.location.hostname;
- // }
- return profile.acct;
- },
- formatCount(val) {
- return App.util.format.count(val);
- },
- goToProfile() {
- this.$router.push({
- name: 'profile',
- path: `/i/web/profile/${this.profile.id}`,
- params: {
- id: this.profile.id,
- cachedProfile: this.profile,
- cachedUser: this.user
- }
- })
- },
- rewriteLinks() {
- let content = this.profile.note;
- let el = document.createElement('div');
- el.innerHTML = content;
- el.querySelectorAll('a[class*="hashtag"]')
- .forEach(elr => {
- let tag = elr.innerText;
- if(tag.substr(0, 1) == '#') {
- tag = tag.substr(1);
- }
- elr.removeAttribute('target');
- elr.setAttribute('href', '/i/web/hashtag/' + tag);
- })
- el.querySelectorAll('a:not(.hashtag)[class*="mention"], a:not(.hashtag)[class*="list-slug"]')
- .forEach(elr => {
- let name = elr.innerText;
- if(name.substr(0, 1) == '@') {
- name = name.substr(1);
- }
- if(this.profile.local == false && !name.includes('@')) {
- let domain = document.createElement('a');
- domain.href = this.profile.url;
- name = name + '@' + domain.hostname;
- }
- elr.removeAttribute('target');
- elr.setAttribute('href', '/i/web/username/' + name);
- })
- this.bio = el.outerHTML;
- },
- performFollow() {
- this.isLoading = true;
- this.$emit('follow');
- setTimeout(() => {
- this.relationship.following = true;
- this.isLoading = false;
- }, 1000);
- },
- performUnfollow() {
- this.isLoading = true;
- this.$emit('unfollow');
- setTimeout(() => {
- this.relationship.following = false;
- this.isLoading = false;
- }, 1000);
- }
- }
- }
- </script>
- <style lang="scss">
- .profile-hover-card {
- display: block;
- width: 300px;
- overflow: hidden;
- padding: 0.5rem;
- border: none;
- font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
- .avatar {
- border-radius: 15px;
- box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 15%) !important;
- margin-bottom: 0.5rem;
- }
- .display-name {
- max-width: 240px;
- word-break: break-word;
- font-weight: 800;
- margin-top: 5px;
- margin-bottom: 2px;
- line-height: 0.8;
- font-size: 16px;
- font-weight: 800 !important;
- user-select: all;
- font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
- a {
- color: var(--body-color);
- text-decoration: none;
- }
- }
- .username {
- max-width: 240px;
- word-break: break-word;
- font-size: 12px;
- margin-top: 0;
- margin-bottom: 0.6rem;
- user-select: all;
- font-weight: 700;
- overflow: hidden;
- &-link {
- color: var(--text-lighter);
- text-decoration: none;
- margin-right: 4px;
- }
- &-follows-you {
- margin: 4px 0;
- span {
- color: var(--dropdown-item-color);
- background-color: var(--comment-bg);
- font-size: 12px;
- font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
- font-weight: 500;
- padding: 2px 4px;
- line-height: 16px;
- border-radius: 6px;
- }
- }
- }
- .pronouns {
- font-size: 11px;
- color: #9CA3AF;
- margin-top: -0.8rem;
- margin-bottom: 0.6rem;
- font-weight: 600;
- }
- .bio {
- max-width: 240px;
- max-height: 60px;
- word-break: break-word;
- margin-bottom: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- line-height: 1.2;
- font-size: 12px;
- color: var(--body-color);
- .invisible {
- display: none;
- }
- }
- .stats {
- margin-top: 0.5rem;
- margin-bottom: 0;
- font-size: 14px;
- user-select: none;
- color: var(--body-color);
- .stats-following {
- margin-right: 0.8rem;
- }
- .following-count,
- .followers-count {
- font-weight: 800;
- }
- }
- .btn {
- &.rounded-pill {
- min-width: 80px;
- }
- }
- }
- </style>
|