diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js
new file mode 100644
index 00000000..ade598d8
--- /dev/null
+++ b/src/components/mention_link/mention_link.js
@@ -0,0 +1,71 @@
+import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
+import { getTextColor, rgb2hex } from 'src/services/color_convert/color_convert.js'
+import { mapGetters, mapState } from 'vuex'
+import { convert } from 'chromatism'
+
+const MentionLink = {
+ name: 'MentionLink',
+ props: {
+ url: {
+ required: true,
+ type: String
+ },
+ content: {
+ required: true,
+ type: String
+ },
+ origattrs: {
+ required: true,
+ type: Object
+ }
+ },
+ methods: {
+ onClick () {
+ const link = generateProfileLink(this.user.id, this.user.screen_name)
+ this.$router.push(link)
+ }
+ },
+ computed: {
+ user () {
+ return this.$store.getters.findUserByUrl(this.url)
+ },
+ isYou () {
+ // FIXME why user !== currentUser???
+ return this.user.screen_name === this.currentUser.screen_name
+ },
+ userName () {
+ return this.userNameFullUi.split('@')[0]
+ },
+ userNameFull () {
+ return this.user.screen_name
+ },
+ userNameFullUi () {
+ return this.user.screen_name_ui
+ },
+ highlight () {
+ return this.mergedConfig.highlight[this.user.screen_name]
+ },
+ bg () {
+ if (this.highlight) return this.highlight.color
+ },
+ text () {
+ if (this.bg) {
+ const linkColor = this.mergedConfig.customTheme.colors.link
+ const color = getTextColor(convert(this.bg).rgb, convert(linkColor).rgb)
+ return rgb2hex(color)
+ }
+ },
+ style () {
+ return [
+ this.bg && `--mention-bg: ${this.bg}`,
+ this.text && `--mention-text: ${this.text}`
+ ].filter(_ => _).join('; ')
+ },
+ ...mapGetters(['mergedConfig']),
+ ...mapState({
+ currentUser: state => state.users.currentUser
+ })
+ }
+}
+
+export default MentionLink
diff --git a/src/components/mention_link/mention_link.scss b/src/components/mention_link/mention_link.scss
new file mode 100644
index 00000000..237d9205
--- /dev/null
+++ b/src/components/mention_link/mention_link.scss
@@ -0,0 +1,49 @@
+.MentionLink {
+ position: relative;
+ white-space: normal;
+ display: inline-block;
+
+ & .new,
+ & .original,
+ & .full {
+ padding: 0 2px;
+ margin: 0 -2px;
+ display: inline-block;
+ border-radius: 2px;
+ }
+
+ .original {
+ opacity: 0.5;
+ }
+
+ .full {
+ pointer-events: none;
+ position: absolute;
+ opacity: 0;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ word-wrap: normal;
+ white-space: nowrap;
+ transition: opacity 0.2s ease;
+ background-color: var(--mention-bg, var(--popover));
+ color: var(--mention-text, var(--link));
+ z-index: 1;
+ }
+
+ .new {
+ background-color: var(--mention-bg);
+ color: var(--mention-text, var(--link));
+
+ &.-you {
+ & .shortName,
+ & .full {
+ font-weight: 600;
+ }
+ }
+ }
+
+ &:hover .new .full {
+ opacity: 1;
+ }
+}
diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue
new file mode 100644
index 00000000..84ed3205
--- /dev/null
+++ b/src/components/mention_link/mention_link.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ @
+
+
+
+
+
+
+
+
diff --git a/src/components/rich_content/rich_content.jsx b/src/components/rich_content/rich_content.jsx
index 7bff23cd..00cf6623 100644
--- a/src/components/rich_content/rich_content.jsx
+++ b/src/components/rich_content/rich_content.jsx
@@ -1,7 +1,8 @@
import Vue from 'vue'
-import { unescape } from 'lodash'
+import { unescape, flattenDeep } from 'lodash'
import { convertHtml, getTagName, processTextForEmoji, getAttrs } from 'src/services/mini_html_converter/mini_html_converter.service.js'
import StillImage from 'src/components/still-image/still-image.vue'
+import MentionLink from 'src/components/mention_link/mention_link.vue'
import './rich_content.scss'
@@ -22,6 +23,9 @@ export default Vue.component('RichContent', {
const attrs = getAttrs(tag)
return
}
+ const renderMention = (attrs, children) => {
+ return
+ }
const structure = convertHtml(this.html)
const processItem = (item) => {
if (typeof item === 'string') {
@@ -45,8 +49,14 @@ export default Vue.component('RichContent', {
if (Array.isArray(item)) {
const [opener, children] = item
const Tag = getTagName(opener)
- if (Tag === 'img') {
- return renderImage(opener)
+ switch (Tag) {
+ case 'img':
+ return renderImage(opener)
+ case 'a':
+ const attrs = getAttrs(opener)
+ if (attrs['class'] && attrs['class'].includes('mention')) {
+ return renderMention(attrs, children)
+ }
}
if (children !== undefined) {
return
diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js
index 571f1a78..79408e38 100644
--- a/src/components/status_content/status_content.js
+++ b/src/components/status_content/status_content.js
@@ -168,6 +168,13 @@ const StatusContent = {
LinkPreview,
RichContent
},
+ mounted () {
+ const { attentions } = this.status
+ attentions.forEach(attn => {
+ const { id } = attn
+ this.$store.state.api.backendInteractor.fetchUserIfMissing(this.$store, id)
+ })
+ },
methods: {
linkClicked (event) {
const target = event.target.closest('.status-content a')
diff --git a/src/modules/users.js b/src/modules/users.js
index 2b416f94..9ed06897 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -246,6 +246,10 @@ export const getters = {
}
return result
},
+ findUserByUrl: state => query => {
+ return state.users
+ .find(u => u.statusnet_profile_url.toLowerCase() === query.toLowerCase())
+ },
relationship: state => id => {
const rel = id && state.relationships[id]
return rel || { id, loading: true }