diff --git a/open-isle-cli/package-lock.json b/open-isle-cli/package-lock.json index e2e900c42..53f445be1 100644 --- a/open-isle-cli/package-lock.json +++ b/open-isle-cli/package-lock.json @@ -12,7 +12,7 @@ "echarts": "^5.6.0", "ldrs": "^1.1.7", "markdown-it": "^14.1.0", - "vditor": "^3.8.7", + "vditor": "^3.11.1", "vue": "^3.2.13", "vue-easy-lightbox": "^1.19.0", "vue-echarts": "^7.0.3", @@ -11330,9 +11330,9 @@ } }, "node_modules/vditor": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/vditor/-/vditor-3.8.7.tgz", - "integrity": "sha512-7loYmcj1TCkJV86qwstxzAy33GweFKdJnpiSzGGPqUzDaOcUAiaiFAjIQjxwJ3/sx7pNZf1NWLhXKabXEN7xzQ==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/vditor/-/vditor-3.11.1.tgz", + "integrity": "sha512-7rjNSXYVyZG0mVZpUG2tfxwnoNtkcRCnwdSju+Zvpjf/r72iQa6kLpeThFMIKPuQ5CRnQQv6gnR3eNU6UGbC2Q==", "license": "MIT", "dependencies": { "diff-match-patch": "^1.0.5" diff --git a/open-isle-cli/package.json b/open-isle-cli/package.json index 979bcfd71..ed41c14b4 100644 --- a/open-isle-cli/package.json +++ b/open-isle-cli/package.json @@ -9,15 +9,15 @@ }, "dependencies": { "core-js": "^3.8.3", + "echarts": "^5.6.0", "ldrs": "^1.1.7", "markdown-it": "^14.1.0", - "vditor": "^3.8.7", + "vditor": "^3.11.1", "vue": "^3.2.13", - "vue-router": "^4.5.1", - "vue-toastification": "^2.0.0-rc.5", "vue-easy-lightbox": "^1.19.0", - "echarts": "^5.6.0", - "vue-echarts": "^7.0.3" + "vue-echarts": "^7.0.3", + "vue-router": "^4.5.1", + "vue-toastification": "^2.0.0-rc.5" }, "devDependencies": { "@babel/core": "^7.12.16", diff --git a/open-isle-cli/src/utils/markdown.js b/open-isle-cli/src/utils/markdown.js index 227c751ff..a3ed32ec7 100644 --- a/open-isle-cli/src/utils/markdown.js +++ b/open-isle-cli/src/utils/markdown.js @@ -3,6 +3,30 @@ import hljs from 'highlight.js' import 'highlight.js/styles/github.css' import { toast } from '../main' +function mentionPlugin(md) { + const mentionReg = /^@\[([^\]]+)\]/ + function mention(state, silent) { + const pos = state.pos + if (state.src.charCodeAt(pos) !== 0x40) return false + const match = mentionReg.exec(state.src.slice(pos)) + if (!match) return false + if (!silent) { + const tokenOpen = state.push('link_open', 'a', 1) + tokenOpen.attrs = [ + ['href', `/users/${match[1]}`], + ['target', '_blank'], + ['class', 'mention-link'] + ] + const text = state.push('text', '', 0) + text.content = `@${match[1]}` + state.push('link_close', 'a', -1) + } + state.pos += match[0].length + return true + } + md.inline.ruler.before('emphasis', 'mention', mention) +} + const md = new MarkdownIt({ html: false, linkify: true, @@ -18,6 +42,8 @@ const md = new MarkdownIt({ } }) +md.use(mentionPlugin) + export function renderMarkdown(text) { return md.render(text || '') } diff --git a/open-isle-cli/src/utils/user.js b/open-isle-cli/src/utils/user.js new file mode 100644 index 000000000..a7469067e --- /dev/null +++ b/open-isle-cli/src/utils/user.js @@ -0,0 +1,30 @@ +import { API_BASE_URL } from '../main' + +export async function fetchFollowings(username) { + if (!username) return [] + try { + const res = await fetch(`${API_BASE_URL}/api/users/${username}/following`) + return res.ok ? await res.json() : [] + } catch (e) { + return [] + } +} + +export async function fetchAdmins() { + try { + const res = await fetch(`${API_BASE_URL}/api/users/admins`) + return res.ok ? await res.json() : [] + } catch (e) { + return [] + } +} + +export async function searchUsers(keyword) { + if (!keyword) return [] + try { + const res = await fetch(`${API_BASE_URL}/api/search/users?keyword=${encodeURIComponent(keyword)}`) + return res.ok ? await res.json() : [] + } catch (e) { + return [] + } +} diff --git a/open-isle-cli/src/utils/vditor.js b/open-isle-cli/src/utils/vditor.js index 2a46a0a52..5592d017b 100644 --- a/open-isle-cli/src/utils/vditor.js +++ b/open-isle-cli/src/utils/vditor.js @@ -1,7 +1,8 @@ import Vditor from 'vditor' import 'vditor/dist/index.css' import { API_BASE_URL } from '../main' -import { getToken } from './auth' +import { getToken, authState } from './auth' +import { searchUsers, fetchFollowings, fetchAdmins } from './user' export function getEditorTheme() { return document.documentElement.dataset.theme === 'dark' ? 'dark' : 'classic' @@ -19,11 +20,42 @@ export function createVditor(editorId, options = {}) { after } = options + const fetchMentions = async (value) => { + if (!value) { + const [followings, admins] = await Promise.all([ + fetchFollowings(authState.username), + fetchAdmins() + ]) + const combined = [...followings, ...admins] + const seen = new Set() + return combined.filter(u => { + if (seen.has(u.id)) return false + seen.add(u.id) + return true + }) + } + return searchUsers(value) + } + return new Vditor(editorId, { placeholder, height: 'auto', theme: getEditorTheme(), preview: Object.assign({ theme: { current: getPreviewTheme() } }, preview), + hint: { + extend: [ + { + key: '@', + hint: async (key) => { + const list = await fetchMentions(key) + return list.map(u => ({ + value: `@[${u.username}]`, + html: ` @${u.username}` + })) + }, + }, + ], + }, cdn: 'https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/vditor', toolbar: [ 'emoji', diff --git a/open-isle-cli/src/views/MessagePageView.vue b/open-isle-cli/src/views/MessagePageView.vue index c6cd86dc1..906d5f6e1 100644 --- a/open-isle-cli/src/views/MessagePageView.vue +++ b/open-isle-cli/src/views/MessagePageView.vue @@ -147,6 +147,29 @@ + +