From fd831a27f4e9c6bcd5c40f3449d63546fba2560d Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Fri, 20 May 2022 00:56:23 +0300
Subject: experimental disjointed popups

---
 src/components/popover/popover.vue | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index c2a3e801..8f6ef2e6 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -11,19 +11,21 @@
     >
       <slot name="trigger" />
     </button>
-    <div
-      v-if="!hidden"
-      ref="content"
-      :style="styles"
-      class="popover"
-      :class="popoverClass || 'popover-default'"
-    >
-      <slot
-        name="content"
-        class="popover-inner"
-        :close="hidePopover"
-      />
-    </div>
+    <teleport to="#popovers">
+      <div
+        v-if="!hidden"
+        ref="content"
+        :style="styles"
+        class="popover"
+        :class="popoverClass || 'popover-default'"
+      >
+        <slot
+          name="content"
+          class="popover-inner"
+          :close="hidePopover"
+        />
+      </div>
+    </teleport>
   </div>
 </template>
 
-- 
cgit v1.2.3-70-g09d2


From 61d63b0e616392d841b78095ae8045e98d2b2fa6 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 12 Jun 2022 15:23:43 +0300
Subject: fix popovers so that all of them have shadows

---
 src/components/popover/popover.vue               | 2 ++
 src/components/status_popover/status_popover.vue | 2 --
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 8f6ef2e6..6103fbdc 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -42,6 +42,8 @@
   z-index: 500;
   position: absolute;
   min-width: 0;
+  box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
+  box-shadow: var(--popupShadow);
 }
 
 .popover-default {
diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue
index fdca8c9c..b6a3be32 100644
--- a/src/components/status_popover/status_popover.vue
+++ b/src/components/status_popover/status_popover.vue
@@ -52,8 +52,6 @@
   border-width: 1px;
   border-radius: $fallback--tooltipRadius;
   border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
-  box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
-  box-shadow: var(--popupShadow);
 
   /* TODO cleanup this */
   .Status.Status {
-- 
cgit v1.2.3-70-g09d2


From 55adcd822e194be8aaeb9d6b649de90e9d5e1e45 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 12 Jun 2022 16:31:56 +0300
Subject: fix animations, replace ugly old mentionlink tooltips with new
 usercard ones

---
 src/App.scss                                       |   2 +-
 src/components/mention_link/mention_link.js        |   8 +-
 src/components/mention_link/mention_link.scss      |   7 +-
 src/components/mention_link/mention_link.vue       | 133 ++++++++++++---------
 src/components/popover/popover.js                  |  40 +++++--
 src/components/popover/popover.vue                 |  36 +++---
 src/components/settings_modal/tabs/general_tab.vue |   2 +-
 src/i18n/en.json                                   |   2 +-
 8 files changed, 140 insertions(+), 90 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/App.scss b/src/App.scss
index 5cd0b96e..79c07683 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -829,7 +829,7 @@ option {
 // Vue transitions
 .fade-enter-active,
 .fade-leave-active {
-  transition: opacity 0.2s;
+  transition: opacity 0.3s;
 }
 
 .fade-enter-from,
diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js
index 55eea195..4b088f88 100644
--- a/src/components/mention_link/mention_link.js
+++ b/src/components/mention_link/mention_link.js
@@ -2,6 +2,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p
 import { mapGetters, mapState } from 'vuex'
 import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
 import UserAvatar from '../user_avatar/user_avatar.vue'
+import { defineAsyncComponent } from 'vue'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
   faAt
@@ -14,7 +15,9 @@ library.add(
 const MentionLink = {
   name: 'MentionLink',
   components: {
-    UserAvatar
+    UserAvatar,
+    Popover: defineAsyncComponent(() => import('../popover/popover.vue')),
+    UserCard: defineAsyncComponent(() => import('../user_card/user_card.vue'))
   },
   props: {
     url: {
@@ -36,6 +39,7 @@ const MentionLink = {
   },
   methods: {
     onClick () {
+      if (this.shouldShowTooltip) return
       const link = generateProfileLink(
         this.userId || this.user.id,
         this.userScreenName || this.user.screen_name
@@ -110,7 +114,7 @@ const MentionLink = {
       }
     },
     shouldShowTooltip () {
-      return this.mergedConfig.mentionLinkShowTooltip && this.mergedConfig.mentionLinkDisplay === 'short' && this.isRemote
+      return this.mergedConfig.mentionLinkShowTooltip
     },
     shouldShowAvatar () {
       return this.mergedConfig.mentionLinkShowAvatar
diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss
index 1d856ff9..3b3a05b1 100644
--- a/src/components/mention_link/mention_link.scss
+++ b/src/components/mention_link/mention_link.scss
@@ -101,7 +101,6 @@
   }
 
   &:hover .new .full {
-    opacity: 1;
     pointer-events: initial;
   }
 
@@ -113,3 +112,9 @@
     color: var(--faint, $fallback--faint);
   }
 }
+
+.mention-link-popover {
+  max-width: 70ch;
+  max-height: 20rem;
+  overflow: hidden;
+}
diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue
index 022f04c7..686ad27f 100644
--- a/src/components/mention_link/mention_link.vue
+++ b/src/components/mention_link/mention_link.vue
@@ -9,66 +9,85 @@
       class="original"
       target="_blank"
       v-html="content"
-    /><!-- eslint-enable vue/no-v-html --><span
-      v-if="user"
-      class="new"
-      :style="style"
-      :class="classnames"
+    /><!-- eslint-enable vue/no-v-html -->
+    <Popover
+      trigger="click"
+      :bound-to="{ x: 'container'}"
+      bound-to-selector=".column"
+      popover-class="popover-default mention-popover"
+      :disabled="!shouldShowTooltip"
     >
-      <a
-        class="short button-unstyled"
-        :class="{ '-with-tooltip': shouldShowTooltip }"
-        :href="url"
-        @click.prevent="onClick"
-      >
-        <!-- eslint-disable vue/no-v-html -->
-        <UserAvatar
-          v-if="shouldShowAvatar"
-          class="mention-avatar"
-          :user="user"
-        /><span
-          class="shortName"
-        ><FAIcon
-          v-if="useAtIcon"
-          size="sm"
-          icon="at"
-          class="at"
-        />{{ !useAtIcon ? '@' : '' }}<span
-          class="userName"
-          v-html="userName"
-        /><span
-          v-if="shouldShowFullUserName"
-          class="serverName"
-          :class="{ '-faded': shouldFadeDomain }"
-          v-html="'@' + serverName"
-        />
-        </span>
-        <span
-          v-if="isYou && shouldShowYous"
-          :class="{ '-you': shouldBoldenYou }"
-        > {{ ' ' + $t('status.you') }}</span>
-        <!-- eslint-enable vue/no-v-html -->
-      </a><span
-        v-if="shouldShowTooltip"
-        class="full popover-default"
-        :class="[highlightType]"
-      >
+      <template v-slot:trigger>
         <span
-          class="userNameFull"
+          v-if="user"
+          class="new"
+          :style="style"
+          :class="classnames"
         >
-          <!-- eslint-disable vue/no-v-html -->
-          @<span
-            class="userName"
-            v-html="userName"
-          /><span
-            class="serverName"
-            :class="{ '-faded': shouldFadeDomain }"
-            v-html="'@' + serverName"
-          />
-          <!-- eslint-enable vue/no-v-html -->
-        </span>
-      </span>
-    </span>
+          <a
+            class="short button-unstyled"
+            :class="{ '-with-tooltip': shouldShowTooltip }"
+            :href="url"
+            @click.prevent="onClick"
+          >
+            <!-- eslint-disable vue/no-v-html -->
+            <UserAvatar
+              v-if="shouldShowAvatar"
+              class="mention-avatar"
+              :user="user"
+            /><span
+              class="shortName"
+            ><FAIcon
+              v-if="useAtIcon"
+              size="sm"
+              icon="at"
+              class="at"
+            />{{ !useAtIcon ? '@' : '' }}<span
+              class="userName"
+              v-html="userName"
+            /><span
+              v-if="shouldShowFullUserName"
+              class="serverName"
+              :class="{ '-faded': shouldFadeDomain }"
+              v-html="'@' + serverName"
+            />
+            </span>
+            <span
+              v-if="isYou && shouldShowYous"
+              :class="{ '-you': shouldBoldenYou }"
+            > {{ ' ' + $t('status.you') }}</span>
+            <!-- eslint-enable vue/no-v-html -->
+          </a><span
+            v-if="shouldShowTooltip"
+            class="full"
+          >
+            <span
+              class="userNameFull"
+            >
+              <!-- eslint-disable vue/no-v-html -->
+              @<span
+                class="userName"
+                v-html="userName"
+              /><span
+                class="serverName"
+                :class="{ '-faded': shouldFadeDomain }"
+                v-html="'@' + serverName"
+              />
+              <!-- eslint-enable vue/no-v-html -->
+            </span>
+          </span>
+        </span></template>
+      <template v-slot:content>
+        <UserCard
+          class="mention-link-popover"
+          :user-id="user.id"
+          :hide-bio="true"
+          :bordered="false"
+          :allow-zooming-avatar="true"
+          :rounded="true"
+        />
+      </template>
+    </Popover>
   </span>
 </template>
 
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index a91c084f..3c3a95bd 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -31,13 +31,18 @@ const Popover = {
 
     // If true, subtract padding when calculating position for the popover,
     // use it when popover offset looks to be different on top vs bottom.
-    removePadding: Boolean
+    removePadding: Boolean,
+
+    // self-explanatory (i hope)
+    disabled: Boolean
   },
   data () {
     return {
       hidden: true,
-      styles: { opacity: 0 },
-      oldSize: { width: 0, height: 0 }
+      styles: {},
+      oldSize: { width: 0, height: 0 },
+      // used to avoid blinking if hovered onto popover
+      graceTimeout: null
     }
   },
   methods: {
@@ -47,9 +52,7 @@ const Popover = {
     },
     updateStyles () {
       if (this.hidden) {
-        this.styles = {
-          opacity: 0
-        }
+        this.styles = {}
         return
       }
 
@@ -132,7 +135,6 @@ const Popover = {
       // Note, separate translateX and translateY avoids blurry text on chromium,
       // single translate or translate3d resulted in blurry text.
       this.styles = {
-        opacity: 1,
         left: `${Math.round(translateX)}px`,
         top: `${Math.round(translateY)}px`,
         position: 'fixed'
@@ -143,6 +145,7 @@ const Popover = {
       }
     },
     showPopover () {
+      if (this.disabled) return
       const wasHidden = this.hidden
       this.hidden = false
       this.$nextTick(() => {
@@ -153,13 +156,30 @@ const Popover = {
     hidePopover () {
       if (!this.hidden) this.$emit('close')
       this.hidden = true
-      this.styles = { opacity: 0 }
     },
     onMouseenter (e) {
-      if (this.trigger === 'hover') this.showPopover()
+      if (this.trigger === 'hover') {
+        clearTimeout(this.graceTimeout)
+        this.graceTimeout = null
+        this.showPopover()
+      }
     },
     onMouseleave (e) {
-      if (this.trigger === 'hover') this.hidePopover()
+      if (this.trigger === 'hover') {
+        this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
+      }
+    },
+    onMouseenterContent (e) {
+      if (this.trigger === 'hover') {
+        clearTimeout(this.graceTimeout)
+        this.graceTimeout = null
+        this.showPopover()
+      }
+    },
+    onMouseleaveContent (e) {
+      if (this.trigger === 'hover') {
+        this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
+      }
     },
     onClick (e) {
       if (this.trigger === 'click') {
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 6103fbdc..528c4fb2 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -1,5 +1,5 @@
 <template>
-  <div
+  <span
     @mouseenter="onMouseenter"
     @mouseleave="onMouseleave"
   >
@@ -12,21 +12,25 @@
       <slot name="trigger" />
     </button>
     <teleport to="#popovers">
-      <div
-        v-if="!hidden"
-        ref="content"
-        :style="styles"
-        class="popover"
-        :class="popoverClass || 'popover-default'"
-      >
-        <slot
-          name="content"
-          class="popover-inner"
-          :close="hidePopover"
-        />
-      </div>
+      <transition name="fade">
+        <div
+          v-if="!hidden"
+          ref="content"
+          :style="styles"
+          class="popover"
+          :class="popoverClass || 'popover-default'"
+          @mouseenter="onMouseenterContent"
+          @mouseleave="onMouseleaveContent"
+        >
+          <slot
+            name="content"
+            class="popover-inner"
+            :close="hidePopover"
+          />
+        </div>
+      </transition>
     </teleport>
-  </div>
+  </span>
 </template>
 
 <script src="./popover.js" />
@@ -47,8 +51,6 @@
 }
 
 .popover-default {
-  transition: opacity 0.3s;
-
   &:after {
     content: '';
     position: absolute;
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 1fe51b6d..5b4a126e 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -269,7 +269,7 @@
               path="mentionLinkShowTooltip"
               expert="1"
             >
-              {{ $t('settings.mention_link_show_tooltip') }}
+              {{ $t('settings.mention_link_use_tooltip') }}
             </BooleanSetting>
           </li>
         </ul>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 0cfda804..053e91ff 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -523,7 +523,7 @@
     "mention_link_display_short": "always as short names (e.g. {'@'}foo)",
     "mention_link_display_full_for_remote": "as full names only for remote users (e.g. {'@'}foo{'@'}example.org)",
     "mention_link_display_full": "always as full names (e.g. {'@'}foo{'@'}example.org)",
-    "mention_link_show_tooltip": "Show full user names as tooltip for remote users",
+    "mention_link_use_tooltip": "Show user card when clicking mention links",
     "mention_link_show_avatar": "Show user avatar beside the link",
     "mention_link_fade_domain": "Fade domains (e.g. {'@'}example.org in {'@'}foo{'@'}example.org)",
     "mention_link_bolden_you": "Highlight mention of you when you are mentioned",
-- 
cgit v1.2.3-70-g09d2


From 97e4a3d8e52bc2255f4be23967a2223f5e458a67 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 13 Jun 2022 01:36:41 +0300
Subject: fix popovers in modals

---
 src/App.js                         | 3 +--
 src/App.vue                        | 2 +-
 src/components/popover/popover.vue | 2 +-
 3 files changed, 3 insertions(+), 4 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/App.js b/src/App.js
index f01f8788..d5967685 100644
--- a/src/App.js
+++ b/src/App.js
@@ -4,7 +4,6 @@ import InstanceSpecificPanel from './components/instance_specific_panel/instance
 import FeaturesPanel from './components/features_panel/features_panel.vue'
 import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
 import ShoutPanel from './components/shout_panel/shout_panel.vue'
-import SettingsModal from './components/settings_modal/settings_modal.vue'
 import MediaModal from './components/media_modal/media_modal.vue'
 import SideDrawer from './components/side_drawer/side_drawer.vue'
 import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
@@ -32,7 +31,7 @@ export default {
     MobilePostStatusButton,
     MobileNav,
     DesktopNav,
-    SettingsModal,
+    SettingsModal: defineAsyncComponent(() => import('./components/settings_modal/settings_modal.vue')),
     UserReportingModal,
     PostStatusModal,
     GlobalNoticeList
diff --git a/src/App.vue b/src/App.vue
index c217ade5..5d7bb4e1 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -54,8 +54,8 @@
     <PostStatusModal />
     <SettingsModal />
     <div id="modal" />
-    <div id="popovers" />
     <GlobalNoticeList />
+    <div id="popovers" />
   </div>
 </template>
 
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 528c4fb2..25ce32fb 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -43,7 +43,7 @@
 }
 
 .popover {
-  z-index: 500;
+  z-index: 90000;
   position: absolute;
   min-width: 0;
   box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
-- 
cgit v1.2.3-70-g09d2


From 7c633aa9525c8a0533281947c587ff1a5ded75ec Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 13 Jun 2022 13:45:04 +0300
Subject: user popovers WIP

---
 src/components/popover/popover.js  | 91 ++++++++++++++++++++++----------------
 src/components/popover/popover.vue |  2 +-
 src/components/status/status.js    |  4 +-
 src/components/status/status.vue   | 38 +++++++++++-----
 4 files changed, 84 insertions(+), 51 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index dbd446a5..233572a4 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -34,7 +34,13 @@ const Popover = {
     removePadding: Boolean,
 
     // self-explanatory (i hope)
-    disabled: Boolean
+    disabled: Boolean,
+
+    // Instead of putting popover next to anchor, overlay popover's center on top of anchor's center
+    overlayCenters: Boolean,
+
+    // What selector (witin popover!) to use for determining center of popover
+    overlayCentersSelector: String
   },
   data () {
     return {
@@ -73,7 +79,9 @@ const Popover = {
         x: anchorScreenBox.left + anchorWidth * 0.5,
         y: anchorScreenBox.top + anchorHeight * 0.5
       }
-      const content = this.$refs.content
+      const content = this.overlayCentersSelector
+        ? this.$refs.content.querySelector(this.overlayCentersSelector)
+        : this.$refs.content
 
       // Minor optimization, don't call a slow reflow call if we don't have to
       const parentScreenBox = this.boundTo &&
@@ -100,44 +108,51 @@ const Popover = {
         max: window.innerHeight - (margin.bottom || 5)
       }
 
-      let horizOffset = content.offsetWidth * -0.5
-      const leftBorder = origin.x + horizOffset
-      const rightBorder = origin.x - horizOffset
-      // If overflowing from left, move it so that it doesn't
-      if (leftBorder < xBounds.min) {
-        horizOffset += xBounds.min - leftBorder
-      }
+      if (!this.overlayCenters) {
+        let horizOffset = content.offsetWidth * -0.5
+        const leftBorder = origin.x + horizOffset
+        const rightBorder = origin.x - horizOffset
+        // If overflowing from left, move it so that it doesn't
+        if (leftBorder < xBounds.min) {
+          horizOffset += xBounds.min - leftBorder
+        }
 
-      // If overflowing from right, move it so that it doesn't
-      if (rightBorder > xBounds.max) {
-        horizOffset -= rightBorder - xBounds.max
-      }
+        // If overflowing from right, move it so that it doesn't
+        if (rightBorder > xBounds.max) {
+          horizOffset -= rightBorder - xBounds.max
+        }
+
+        // Default to whatever user wished with placement prop
+        let usingTop = this.placement !== 'bottom'
 
-      // Default to whatever user wished with placement prop
-      let usingTop = this.placement !== 'bottom'
-
-      // Handle special cases, first force to displaying on top if there's not space on bottom,
-      // regardless of what placement value was. Then check if there's not space on top, and
-      // force to bottom, again regardless of what placement value was.
-      const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0)
-      const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0)
-      if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true
-      if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false
-
-      const yOffset = (this.offset && this.offset.y) || 0
-      const translateY = usingTop
-        ? topBoundary - yOffset - content.offsetHeight
-        : bottomBoundary + yOffset
-
-      const xOffset = (this.offset && this.offset.x) || 0
-      const translateX = origin.x + horizOffset + xOffset
-
-      // Note, separate translateX and translateY avoids blurry text on chromium,
-      // single translate or translate3d resulted in blurry text.
-      this.styles = {
-        left: `${Math.round(translateX)}px`,
-        top: `${Math.round(translateY)}px`,
-        position: 'fixed'
+        // Handle special cases, first force to displaying on top if there's not space on bottom,
+        // regardless of what placement value was. Then check if there's not space on top, and
+        // force to bottom, again regardless of what placement value was.
+        const topBoundary = origin.y - anchorHeight * 0.5 + (this.removePadding ? topPadding : 0)
+        const bottomBoundary = origin.y + anchorHeight * 0.5 - (this.removePadding ? bottomPadding : 0)
+        if (bottomBoundary + content.offsetHeight > yBounds.max) usingTop = true
+        if (topBoundary - content.offsetHeight < yBounds.min) usingTop = false
+
+        const yOffset = (this.offset && this.offset.y) || 0
+        const translateY = usingTop
+          ? topBoundary - yOffset - content.offsetHeight
+          : bottomBoundary + yOffset
+
+        const xOffset = (this.offset && this.offset.x) || 0
+        const translateX = origin.x + horizOffset + xOffset
+
+        this.styles = {
+          left: `${Math.round(translateX)}px`,
+          top: `${Math.round(translateY)}px`
+        }
+      } else {
+        const translateY = origin.y - content.offsetHeight
+        const translateX = origin.x - content.offsetWidth
+
+        this.styles = {
+          left: `${Math.round(translateX)}px`,
+          top: `${Math.round(translateY)}px`
+        }
       }
 
       if (parentScreenBox) {
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 25ce32fb..977c3559 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -44,7 +44,7 @@
 
 .popover {
   z-index: 90000;
-  position: absolute;
+  position: fixed;
   min-width: 0;
   box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
   box-shadow: var(--popupShadow);
diff --git a/src/components/status/status.js b/src/components/status/status.js
index a925f30b..06496258 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -11,6 +11,7 @@ import Timeago from '../timeago/timeago.vue'
 import StatusContent from '../status_content/status_content.vue'
 import RichContent from 'src/components/rich_content/rich_content.jsx'
 import StatusPopover from '../status_popover/status_popover.vue'
+import Popover from '../popover/popover.vue'
 import UserListPopover from '../user_list_popover/user_list_popover.vue'
 import EmojiReactions from '../emoji_reactions/emoji_reactions.vue'
 import MentionsLine from 'src/components/mentions_line/mentions_line.vue'
@@ -115,7 +116,8 @@ const Status = {
     StatusContent,
     RichContent,
     MentionLink,
-    MentionsLine
+    MentionsLine,
+    Popover
   },
   props: [
     'statusoid',
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 67ce999a..624960fe 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -122,17 +122,33 @@
           v-if="!noHeading"
           class="left-side"
         >
-          <a
-            :href="$router.resolve(userProfileLink).href"
-            @click.stop.prevent.capture="toggleUserExpanded"
-          >
-            <UserAvatar
-              class="post-avatar"
-              :bot="botIndicator"
-              :compact="compact"
-              :better-shadow="betterShadow"
-              :user="status.user"
-            />
+          <a :href="$router.resolve(userProfileLink).href" @click.prevent>
+            <Popover
+              trigger="click"
+              popover-class="popover-default user-popover"
+              :overlay-centers="true"
+              overlay-centers-selector=".user-info-avatar-link .Avatar"
+            >
+              <template v-slot:trigger>
+                <UserAvatar
+                  class="post-avatar"
+                  :bot="botIndicator"
+                  :compact="compact"
+                  :better-shadow="betterShadow"
+                  :user="status.user"
+                />
+              </template>
+              <template v-slot:content>
+                <UserCard
+                  class="mention-link-popover"
+                  :user-id="status.user.id"
+                  :hide-bio="true"
+                  :bordered="false"
+                  :allow-zooming-avatar="true"
+                  :rounded="true"
+                />
+              </template>
+            </Popover>
           </a>
         </div>
         <div class="right-side">
-- 
cgit v1.2.3-70-g09d2


From 770d12f7adda5659ff01b83ad3e172c27bae818d Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 15 Jun 2022 04:03:18 +0300
Subject: limit width of popover to slightly lesser than screen width

---
 src/components/popover/popover.vue | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 977c3559..5c45be73 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -46,6 +46,7 @@
   z-index: 90000;
   position: fixed;
   min-width: 0;
+  max-width: 90vw;
   box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
   box-shadow: var(--popupShadow);
 }
-- 
cgit v1.2.3-70-g09d2


From 78ced0c901d255bf99e096339da945b1b3cc0216 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 21 Jun 2022 01:05:42 +0300
Subject: proper horizontal limit

---
 src/components/popover/popover.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 5c45be73..0d37328e 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -46,7 +46,7 @@
   z-index: 90000;
   position: fixed;
   min-width: 0;
-  max-width: 90vw;
+  max-width: calc(100vw - 20px);
   box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
   box-shadow: var(--popupShadow);
 }
-- 
cgit v1.2.3-70-g09d2


From 872db65fd86aaa605789383e629321e32447a997 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Wed, 22 Jun 2022 00:30:10 +0300
Subject: slight z-index refactor and attempt at organizing it

---
 src/App.scss                                             |  9 ++++++++-
 src/boot/after_store.js                                  |  3 +++
 src/components/desktop_nav/desktop_nav.scss              |  1 +
 src/components/emoji_picker/emoji_picker.scss            |  3 ++-
 src/components/global_notice_list/global_notice_list.vue |  2 +-
 src/components/media_modal/media_modal.vue               |  2 +-
 src/components/mobile_nav/mobile_nav.vue                 |  6 ++++--
 src/components/modal/modal.vue                           |  5 ++++-
 src/components/notifications/notifications.js            | 10 ++++++++++
 src/components/popover/popover.js                        |  4 ++++
 src/components/popover/popover.vue                       |  4 ++--
 src/components/shout_panel/shout_panel.vue               |  2 +-
 src/components/side_drawer/side_drawer.vue               |  2 +-
 13 files changed, 42 insertions(+), 11 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/App.scss b/src/App.scss
index 79c07683..78835c61 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -4,6 +4,13 @@
 :root {
   --navbar-height: 3.5rem;
   --post-line-height: 1.4;
+  // Z-Index stuff
+  --ZI_media_modal: 90000;
+  --ZI_navbar_popovers: 85000;
+  --ZI_navbar: 80000;
+  --ZI_modals_popovers: 75000;
+  --ZI_modals: 70000;
+  --ZI_popovers: 60000;
 }
 
 html {
@@ -117,7 +124,7 @@ i[class*=icon-],
 }
 
 nav {
-  z-index: 1000;
+  z-index: var(--ZI_navbar);
   color: var(--topBarText);
   background-color: $fallback--fg;
   background-color: var(--topBar, $fallback--fg);
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index f655c38f..894a68e1 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -396,6 +396,9 @@ const afterStoreSetup = async ({ store, i18n }) => {
   app.component('FAIcon', FontAwesomeIcon)
   app.component('FALayers', FontAwesomeLayers)
 
+  // remove after vue 3.3
+  app.config.unwrapInjectedRef = true
+
   app.mount('#app')
 
   return app
diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss
index 0ca9802e..e9a47632 100644
--- a/src/components/desktop_nav/desktop_nav.scss
+++ b/src/components/desktop_nav/desktop_nav.scss
@@ -2,6 +2,7 @@
 
 .DesktopNav {
   width: 100%;
+  z-index: var(--ZI_navbar);
 
   a {
     color: var(--topBarLink, $fallback--link);
diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss
index 2055e02e..a2f17c51 100644
--- a/src/components/emoji_picker/emoji_picker.scss
+++ b/src/components/emoji_picker/emoji_picker.scss
@@ -7,7 +7,8 @@
   right: 0;
   left: 0;
   margin: 0 !important;
-  z-index: 100;
+  // TODO: actually use popover in emoji picker
+  z-index: var(--ZI_popovers);
   background-color: $fallback--bg;
   background-color: var(--popover, $fallback--bg);
   color: $fallback--link;
diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue
index ddc45b81..09904761 100644
--- a/src/components/global_notice_list/global_notice_list.vue
+++ b/src/components/global_notice_list/global_notice_list.vue
@@ -32,7 +32,7 @@
   top: 50px;
   width: 100%;
   pointer-events: none;
-  z-index: 1001;
+  z-index: var(--ZI_popovers);
   display: flex;
   flex-direction: column;
   align-items: center;
diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue
index b93d3d84..d59055b3 100644
--- a/src/components/media_modal/media_modal.vue
+++ b/src/components/media_modal/media_modal.vue
@@ -121,7 +121,7 @@ $modal-view-button-icon-width: 3em;
 $modal-view-button-icon-margin: 0.5em;
 
 .modal-view.media-modal-view {
-  z-index: 900000;
+  z-index: var(--ZI_media_modal);
   flex-direction: column;
 
   .modal-view-button-arrow,
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index d2d48a03..c58d9e7c 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -86,6 +86,8 @@
 @import '../../_variables.scss';
 
 .MobileNav {
+  z-index: var(--ZI_navbar);
+
   .mobile-nav {
     display: grid;
     line-height: var(--navbar-height);
@@ -147,7 +149,7 @@
     transition-property: transform;
     transition-duration: 0.25s;
     transform: translateX(0);
-    z-index: 1001;
+    z-index: var(--ZI_navbar);
     -webkit-overflow-scrolling: touch;
 
     &.-closed {
@@ -160,7 +162,7 @@
     display: flex;
     align-items: center;
     justify-content: space-between;
-    z-index: 1;
+    z-index: calc(var(--ZI_navbar) + 100);
     width: 100%;
     height: 50px;
     line-height: 50px;
diff --git a/src/components/modal/modal.vue b/src/components/modal/modal.vue
index 9394efff..52d8d27e 100644
--- a/src/components/modal/modal.vue
+++ b/src/components/modal/modal.vue
@@ -22,6 +22,9 @@ export default {
       default: false
     }
   },
+  provide: {
+    popoversZLayer: 'modals'
+  },
   computed: {
     classes () {
       return {
@@ -35,7 +38,7 @@ export default {
 
 <style lang="scss">
 .modal-view {
-  z-index: 2000;
+  z-index: var(--ZI_modals);
   position: fixed;
   top: 0;
   left: 0;
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index fb2579a5..63d827d8 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -1,3 +1,4 @@
+import { computed } from 'vue'
 import { mapGetters } from 'vuex'
 import Notification from '../notification/notification.vue'
 import NotificationFilters from './notification_filters.vue'
@@ -38,6 +39,11 @@ const Notifications = {
       seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
     }
   },
+  provide () {
+    return {
+      popoversZLayer: computed(() => this.popoversZLayer)
+    }
+  },
   computed: {
     mainClass () {
       return this.minimalMode ? '' : 'panel panel-default'
@@ -75,6 +81,10 @@ const Notifications = {
       }
       return map[layoutType] || '#notifs-sidebar'
     },
+    popoversZLayer () {
+      const { layoutType } = this.$store.state.interface
+      return layoutType === 'mobile' ? 'navbar' : null
+    },
     notificationsToDisplay () {
       return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
     },
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index cff9b174..5dfcf810 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -42,6 +42,7 @@ const Popover = {
     // What selector (witin popover!) to use for determining center of popover
     overlayCentersSelector: String
   },
+  inject: ['popoversZLayer'], // override popover z layer
   data () {
     return {
       hidden: true,
@@ -168,6 +169,9 @@ const Popover = {
         top: `${Math.round(translateY)}px`
       }
 
+      if (this.popoversZLayer) {
+        this.styles['--ZI_popover_override'] = `var(--ZI_${this.popoversZLayer}_popovers)`
+      }
       if (parentScreenBox) {
         this.styles.maxWidth = `${Math.round(parentScreenBox.width)}px`
       }
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 0d37328e..f854515c 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -43,7 +43,7 @@
 }
 
 .popover {
-  z-index: 90000;
+  z-index: var(--ZI_popover_override, var(--ZI_popovers));
   position: fixed;
   min-width: 0;
   max-width: calc(100vw - 20px);
@@ -87,7 +87,7 @@
   text-align: left;
   list-style: none;
   max-width: 100vw;
-  z-index: 200;
+  z-index: var(--ZI_popover_override, var(--ZI_popovers));
   white-space: nowrap;
 
   .dropdown-divider {
diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue
index 1eca88a7..688c2d61 100644
--- a/src/components/shout_panel/shout_panel.vue
+++ b/src/components/shout_panel/shout_panel.vue
@@ -80,7 +80,7 @@
 .floating-shout {
   position: fixed;
   bottom: 0.5em;
-  z-index: 1000;
+  z-index: var(--ZI_popovers);
   max-width: 25em;
 
   &.-left {
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index dd88de7d..aefd3915 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -211,7 +211,7 @@
 
 .side-drawer-container {
   position: fixed;
-  z-index: 1000;
+  z-index: var(--ZI_navbar);
   top: 0;
   left: 0;
   width: 100%;
-- 
cgit v1.2.3-70-g09d2


From d5bc825616858f8eccb4fb9786f0178bce44e979 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 31 Jul 2022 00:05:26 +0300
Subject: add stay-on-click prop to solve case of clicking user avatar in
 status popover

---
 src/components/popover/popover.js                | 18 ++++++++++++++----
 src/components/popover/popover.vue               | 21 +++++++++++++++++++++
 src/components/status_popover/status_popover.vue |  1 +
 3 files changed, 36 insertions(+), 4 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index c19e6904..843b22f3 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -40,7 +40,10 @@ const Popover = {
     overlayCenters: Boolean,
 
     // What selector (witin popover!) to use for determining center of popover
-    overlayCentersSelector: String
+    overlayCentersSelector: String,
+
+    // Lets hover popover stay when clicking inside of it
+    stayOnClick: Boolean
   },
   inject: ['popoversZLayer'], // override popover z layer
   data () {
@@ -50,6 +53,7 @@ const Popover = {
       // with popovers refusing to be hidden when user wants to interact with something in below popover
       lockReEntry: false,
       hidden: true,
+      pinned: false,
       styles: {},
       oldSize: { width: 0, height: 0 },
       scrollable: null,
@@ -196,9 +200,10 @@ const Popover = {
     },
     showPopover () {
       if (this.disabled) return
+      this.pinned = false
       const wasHidden = this.hidden
       this.hidden = false
-      if (this.trigger === 'click') {
+      if (this.trigger === 'click' || this.stayOnClick) {
         document.addEventListener('click', this.onClickOutside)
       }
       this.scrollable.addEventListener('scroll', this.onScroll)
@@ -227,7 +232,7 @@ const Popover = {
       }
     },
     onMouseleave (e) {
-      if (this.trigger === 'hover') {
+      if (this.trigger === 'hover' && !this.pinned) {
         this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
       }
     },
@@ -240,7 +245,7 @@ const Popover = {
       }
     },
     onMouseleaveContent (e) {
-      if (this.trigger === 'hover') {
+      if (this.trigger === 'hover' && !this.pinned) {
         this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
       }
     },
@@ -259,6 +264,11 @@ const Popover = {
       if (this.$el.contains(e.target)) return
       this.hidePopover()
     },
+    onClickContent (e) {
+      if (this.trigger === 'hover' && this.stayOnClick) {
+        this.pinned = true
+      }
+    },
     onScroll (e) {
       this.updateStyles()
     },
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index f854515c..9b67f400 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -21,12 +21,22 @@
           :class="popoverClass || 'popover-default'"
           @mouseenter="onMouseenterContent"
           @mouseleave="onMouseleaveContent"
+          @click="onClickContent"
         >
           <slot
             name="content"
             class="popover-inner"
             :close="hidePopover"
           />
+          <div
+            v-if="stayOnClick && pinned"
+            class="pinned-tooltip-icon popover popover-default"
+          >
+            <FAIcon
+              icon="thumbtack"
+              class="faint"
+            />
+          </div>
         </div>
       </transition>
     </teleport>
@@ -51,6 +61,17 @@
   box-shadow: var(--popupShadow);
 }
 
+.pinned-tooltip-icon {
+  position: absolute;
+  top: -1em;
+  left: -1em;
+  width: 2em;
+  height: 2em;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
 .popover-default {
   &:after {
     content: '';
diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue
index 2c436d04..9cabb751 100644
--- a/src/components/status_popover/status_popover.vue
+++ b/src/components/status_popover/status_popover.vue
@@ -1,6 +1,7 @@
 <template>
   <Popover
     trigger="hover"
+    :stay-on-click="true"
     popover-class="popover-default status-popover"
     :bound-to="{ x: 'container' }"
     @show="enter"
-- 
cgit v1.2.3-70-g09d2


From 8ab30545b2b792ddae9cc439c3e617195e22d3ca Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 31 Jul 2022 01:45:38 +0300
Subject: pinned no longer needed

---
 src/components/popover/popover.js  | 11 ++---------
 src/components/popover/popover.vue | 20 --------------------
 2 files changed, 2 insertions(+), 29 deletions(-)

(limited to 'src/components/popover/popover.vue')

diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index e477051f..f23df826 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -53,7 +53,6 @@ const Popover = {
       // with popovers refusing to be hidden when user wants to interact with something in below popover
       lockReEntry: false,
       hidden: true,
-      pinned: false,
       styles: {},
       oldSize: { width: 0, height: 0 },
       scrollable: null,
@@ -202,7 +201,6 @@ const Popover = {
     },
     showPopover () {
       if (this.disabled) return
-      this.pinned = false
       const wasHidden = this.hidden
       this.hidden = false
       this.parentPopover && this.parentPopover.onChildPopoverState(this, true)
@@ -236,7 +234,7 @@ const Popover = {
       }
     },
     onMouseleave (e) {
-      if (this.trigger === 'hover' && !this.pinned && this.childrenShown.size > 0) {
+      if (this.trigger === 'hover' && this.childrenShown.size > 0) {
         this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
       }
     },
@@ -249,7 +247,7 @@ const Popover = {
       }
     },
     onMouseleaveContent (e) {
-      if (this.trigger === 'hover' && !this.pinned && this.childrenShown.size > 0) {
+      if (this.trigger === 'hover' && this.childrenShown.size === 0) {
         this.graceTimeout = setTimeout(() => this.hidePopover(), 1)
       }
     },
@@ -270,11 +268,6 @@ const Popover = {
       this.hidePopover()
       if (this.parentPopover) this.parentPopover.onClickOutside(e)
     },
-    onClickContent (e) {
-      if (this.trigger === 'hover' && this.stayOnClick) {
-        this.pinned = true
-      }
-    },
     onScroll (e) {
       this.updateStyles()
     },
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 9b67f400..bd59cade 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -28,15 +28,6 @@
             class="popover-inner"
             :close="hidePopover"
           />
-          <div
-            v-if="stayOnClick && pinned"
-            class="pinned-tooltip-icon popover popover-default"
-          >
-            <FAIcon
-              icon="thumbtack"
-              class="faint"
-            />
-          </div>
         </div>
       </transition>
     </teleport>
@@ -61,17 +52,6 @@
   box-shadow: var(--popupShadow);
 }
 
-.pinned-tooltip-icon {
-  position: absolute;
-  top: -1em;
-  left: -1em;
-  width: 2em;
-  height: 2em;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-
 .popover-default {
   &:after {
     content: '';
-- 
cgit v1.2.3-70-g09d2

