mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-15 05:12:49 +00:00
perf: 优化代码
This commit is contained in:
parent
71f48a4f7c
commit
73261da19b
@ -243,7 +243,7 @@ export default {
|
||||
|| /meet\.google\.com/i.test(url)) {
|
||||
return true;
|
||||
}
|
||||
if ($A.getDomain(url) == $A.getDomain($A.apiUrl('../'))) {
|
||||
if ($A.getDomain(url) == $A.getDomain($A.mainUrl())) {
|
||||
try {
|
||||
if (/^\/uploads\//i.test(new URL(url).pathname)) {
|
||||
return true;
|
||||
|
||||
@ -81,7 +81,7 @@ export default {
|
||||
if (this.$Electron) {
|
||||
this.url = $A.originUrl(`drawio/webapp/index.html${query}`);
|
||||
} else {
|
||||
this.url = $A.apiUrl(`../drawio/webapp/${query}`);
|
||||
this.url = $A.mainUrl(`drawio/webapp/${query}`);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@ -366,7 +366,7 @@
|
||||
|
||||
browseStyle(thumb) {
|
||||
if (!/https*:\/\//.test(thumb) && !/^\//.test(thumb)) {
|
||||
thumb = $A.apiUrl(`../${thumb}`);
|
||||
thumb = $A.mainUrl(thumb);
|
||||
}
|
||||
return {
|
||||
'background-image': `url("${thumb}")`
|
||||
|
||||
@ -24,7 +24,7 @@ export default {
|
||||
src.substring(0, 1) === "/") {
|
||||
return src;
|
||||
}
|
||||
return $A.apiUrl(`../${src}`)
|
||||
return $A.mainUrl(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +307,7 @@
|
||||
},
|
||||
computed: {
|
||||
url() {
|
||||
return $A.apiUrl(`../minder/index.html?type=manual&readonly=${this.readOnly ? 'yes' : 'no'}`)
|
||||
return $A.mainUrl(`minder/index.html?type=manual&readonly=${this.readOnly ? 'yes' : 'no'}`)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
@ -156,7 +156,7 @@ export default {
|
||||
}
|
||||
this.loading = true;
|
||||
this.loadError = false;
|
||||
$A.loadScript($A.apiUrl("../office/web-apps/apps/api/documents/api.js")).then(_ => {
|
||||
$A.loadScript($A.mainUrl("office/web-apps/apps/api/documents/api.js")).then(_ => {
|
||||
if (!this.documentKey) {
|
||||
this.handleClose();
|
||||
return
|
||||
|
||||
@ -154,7 +154,7 @@ export default {
|
||||
if (this.compareVersion(this.apiVersion, '0.19.0') === -1) {
|
||||
$A.modalWarning({
|
||||
title: '温馨提示',
|
||||
message: `服务器(${$A.getDomain($A.apiUrl('../'))})接口版本过低,部分功能可能无法正常使用。`,
|
||||
message: `服务器(${$A.getDomain($A.mainUrl())})接口版本过低,部分功能可能无法正常使用。`,
|
||||
});
|
||||
}
|
||||
if (this.$Electron) {
|
||||
|
||||
27
resources/assets/js/functions/web.js
vendored
27
resources/assets/js/functions/web.js
vendored
@ -42,6 +42,25 @@ import {MarkdownPreview} from "../store/markdown";
|
||||
return str
|
||||
},
|
||||
|
||||
/**
|
||||
* 主页地址
|
||||
* @param str
|
||||
* @returns {string}
|
||||
*/
|
||||
mainUrl(str = null) {
|
||||
if (!str) {
|
||||
str = ""
|
||||
}
|
||||
if (str.substring(0, 2) === "//" ||
|
||||
str.substring(0, 7) === "http://" ||
|
||||
str.substring(0, 8) === "https://" ||
|
||||
str.substring(0, 6) === "ftp://" ||
|
||||
str.substring(0, 1) === "/") {
|
||||
return str;
|
||||
}
|
||||
return $A.apiUrl(`../${str}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 服务地址
|
||||
* @param str
|
||||
@ -73,7 +92,7 @@ import {MarkdownPreview} from "../store/markdown";
|
||||
* @returns {*}
|
||||
*/
|
||||
onlinePreviewUrl(name, key) {
|
||||
return $A.apiUrl(`../online/preview/${name}?key=${key}&version=${window.systemInfo.version}&__=${new Date().getTime()}`)
|
||||
return $A.mainUrl(`online/preview/${name}?key=${key}&version=${window.systemInfo.version}&__=${new Date().getTime()}`)
|
||||
},
|
||||
|
||||
/**
|
||||
@ -759,7 +778,7 @@ import {MarkdownPreview} from "../store/markdown";
|
||||
text = text.trim().replace(/(\n\x20*){3,}/g, "\n\n");
|
||||
text = text.replace(/ /g, ' ')
|
||||
text = text.replace(/<p><\/p>/g, '<p><br/></p>')
|
||||
text = text.replace(/\{\{RemoteURL\}\}/g, $A.apiUrl('../'))
|
||||
text = text.replace(/\{\{RemoteURL\}\}/g, $A.mainUrl())
|
||||
text = text.replace(atReg, `<span class="mention me" data-id="${userid}">`)
|
||||
// 处理内容连接
|
||||
if (/https*:\/\//.test(text)) {
|
||||
@ -801,7 +820,7 @@ import {MarkdownPreview} from "../store/markdown";
|
||||
* @returns {*[]}
|
||||
*/
|
||||
getTextImagesInfo(text) {
|
||||
const baseUrl = $A.apiUrl('../');
|
||||
const baseUrl = $A.mainUrl();
|
||||
const array = text.match(new RegExp(`<img[^>]*?>`, "g"));
|
||||
const list = [];
|
||||
if (array) {
|
||||
@ -890,7 +909,7 @@ import {MarkdownPreview} from "../store/markdown";
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isDooServer() {
|
||||
const u = $A.getDomain($A.apiUrl('../'))
|
||||
const u = $A.getDomain($A.mainUrl())
|
||||
return /dootask\.com$/.test(u)
|
||||
|| /hitosea\.com$/.test(u)
|
||||
|| /^127\.0\.0\.1/.test(u)
|
||||
|
||||
@ -54,9 +54,9 @@ export default {
|
||||
|
||||
goIndex() {
|
||||
if (languageName === "zh" || languageName === "zh-CHT") {
|
||||
window.location.href = $A.apiUrl("../site/zh/index.html")
|
||||
window.location.href = $A.mainUrl("site/zh/index.html")
|
||||
} else {
|
||||
window.location.href = $A.apiUrl("../site/en/index.html")
|
||||
window.location.href = $A.mainUrl("site/en/index.html")
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -291,7 +291,7 @@ export default {
|
||||
},
|
||||
|
||||
qrcodeUrl() {
|
||||
return $A.apiUrl('../login?qrcode=' + this.qrcodeVal)
|
||||
return $A.mainUrl('login?qrcode=' + this.qrcodeVal)
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@ -472,7 +472,7 @@ export default {
|
||||
|
||||
// okr路由
|
||||
okrUrl() {
|
||||
return import.meta.env.VITE_OKR_WEB_URL || $A.apiUrl("../apps/okr")
|
||||
return import.meta.env.VITE_OKR_WEB_URL || $A.mainUrl("apps/okr")
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@ -248,31 +248,31 @@ export default {
|
||||
{
|
||||
value: "openai",
|
||||
label: "ChatGPT",
|
||||
src: $A.apiUrl('../images/avatar/default_openai.png'),
|
||||
src: $A.mainUrl('images/avatar/default_openai.png'),
|
||||
desc: this.$L('我是一个人工智能助手,为用户提供问题解答和指导。我没有具体的身份,只是一个程序。您有什么问题可以问我哦?')
|
||||
},
|
||||
{
|
||||
value: "gemini",
|
||||
label: "Gemini",
|
||||
src: $A.apiUrl('../images/avatar/default_gemini.png'),
|
||||
src: $A.mainUrl('images/avatar/default_gemini.png'),
|
||||
desc: `${this.$L('我是由Google开发的生成式人工智能聊天机器人。')}${this.$L('它基于同名的Gemini系列大型语言模型。')}${this.$L('是应对OpenAI公司开发的ChatGPT聊天机器人的崛起而开发的。')}`
|
||||
},
|
||||
{
|
||||
value: "claude",
|
||||
label: "Claude",
|
||||
src: $A.apiUrl('../images/avatar/default_claude.png'),
|
||||
src: $A.mainUrl('images/avatar/default_claude.png'),
|
||||
desc: this.$L('我是Claude,一个由Anthropic公司创造出来的AI助手机器人。我的工作是帮助人类,与人对话并给出解答。')
|
||||
},
|
||||
{
|
||||
value: "wenxin",
|
||||
label: "Wenxin",
|
||||
src: $A.apiUrl('../avatar/%E6%96%87%E5%BF%83.png'),
|
||||
src: $A.mainUrl('avatar/%E6%96%87%E5%BF%83.png'),
|
||||
desc: this.$L('我是文心一言,英文名是ERNIE Bot。我能够与人对话互动,回答问题,协助创作,高效便捷地帮助人们获取信息、知识和灵感。')
|
||||
},
|
||||
{
|
||||
value: "qianwen",
|
||||
label: "Qianwen",
|
||||
src: $A.apiUrl('../avatar/%E9%80%9A%E4%B9%89%E5%8D%83%E9%97%AE.png'),
|
||||
src: $A.mainUrl('avatar/%E9%80%9A%E4%B9%89%E5%8D%83%E9%97%AE.png'),
|
||||
desc: this.$L('我是达摩院自主研发的超大规模语言模型,能够回答问题、创作文字,还能表达观点、撰写代码。')
|
||||
},
|
||||
],
|
||||
@ -520,10 +520,10 @@ export default {
|
||||
const arr = (text + "").match(/^https*:\/\/(.*?)\/login\?qrcode=(.*?)$/)
|
||||
if (arr) {
|
||||
// 扫码登录
|
||||
if ($A.getDomain(text) != $A.getDomain($A.apiUrl('../'))) {
|
||||
if ($A.getDomain(text) != $A.getDomain($A.mainUrl())) {
|
||||
let content = this.$L('请确认扫码的服务器与当前服务器一致')
|
||||
content += `<br/>${this.$L('二维码服务器')}: ${$A.getDomain(text)}`
|
||||
content += `<br/>${this.$L('当前服务器')}: ${$A.getDomain($A.apiUrl('../'))}`
|
||||
content += `<br/>${this.$L('当前服务器')}: ${$A.getDomain($A.mainUrl())}`
|
||||
$A.modalWarning({
|
||||
language: false,
|
||||
title: this.$L('扫码登录'),
|
||||
|
||||
@ -117,7 +117,7 @@
|
||||
<TimelineItem :key="key" :color="item.is_finished ? 'green' : '#ccc'" v-if="item.type == 'notifier' && item._show">
|
||||
<p class="timeline-title">{{$L('抄送')}}</p>
|
||||
<div class="timeline-body">
|
||||
<Avatar :src="$A.apiUrl('../images/avatar/default_approval.png')" size="38"/>
|
||||
<Avatar :src="$A.mainUrl('images/avatar/default_approval.png')" size="38"/>
|
||||
<div class="approve-process-left">
|
||||
<p class="approve-process-name">{{$L('系统')}}</p>
|
||||
<p class="approve-process-desc">{{$L('自动抄送')}}
|
||||
@ -134,7 +134,7 @@
|
||||
<TimelineItem class="finish" :key="key" :color="item.is_finished ? 'green' : '#ccc'" v-if="item.aprover_type == 'end'">
|
||||
<p class="timeline-title">{{$L('结束')}}</p>
|
||||
<div class="timeline-body">
|
||||
<Avatar :src="$A.apiUrl('../images/avatar/default_approval.png')" size="38"/>
|
||||
<Avatar :src="$A.mainUrl('images/avatar/default_approval.png')" size="38"/>
|
||||
<div class="approve-process-left">
|
||||
<p class="approve-process-name">{{$L('系统')}}</p>
|
||||
<p class="approve-process-desc"> {{ datas.is_finished ? $L('已结束') : $L('未结束') }}</p>
|
||||
@ -476,7 +476,7 @@ export default {
|
||||
},
|
||||
// 打开图片
|
||||
onViewPicture(currentUrl) {
|
||||
this.$store.dispatch("previewImage", $A.apiUrl('../' + currentUrl))
|
||||
this.$store.dispatch("previewImage", $A.mainUrl(currentUrl))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ export default {
|
||||
watch: {
|
||||
approvalSettingShow(val) {
|
||||
if (val) {
|
||||
this.iframeSrc = $A.apiUrl(`../approve/#/?name=${this.name}&token=${store.userToken}&lang=${languageName}`)
|
||||
this.iframeSrc = $A.mainUrl(`approve/#/?name=${this.name}&token=${store.userToken}&lang=${languageName}`)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -26,7 +26,7 @@ export default {
|
||||
if (to.name == 'manage-apps') {
|
||||
this.$nextTick(() => {
|
||||
this.loading = false;
|
||||
this.appUrl = import.meta.env.VITE_OKR_WEB_URL || $A.apiUrl("../apps/okr")
|
||||
this.appUrl = import.meta.env.VITE_OKR_WEB_URL || $A.mainUrl("apps/okr")
|
||||
this.path = this.$route.query.path || '';
|
||||
})
|
||||
}else{
|
||||
|
||||
@ -138,7 +138,7 @@ export default {
|
||||
'js/emoji.all.js',
|
||||
'js/emoticon.all.js',
|
||||
]).then(_ => {
|
||||
const baseUrl = $A.apiUrl("../images/emoticon")
|
||||
const baseUrl = $A.mainUrl("images/emoticon")
|
||||
if ($A.isArray(window.emojiData)) {
|
||||
this.emojiData = window.emojiData.sort(function (a, b) {
|
||||
return a.emoji_order - b.emoji_order;
|
||||
@ -228,7 +228,7 @@ export default {
|
||||
const container = this.$refs['chatEmojiMenuRef'];
|
||||
container?.addEventListener("wheel", (event) =>{
|
||||
event.preventDefault();
|
||||
container.scrollLeft += event.deltaY;
|
||||
container.scrollLeft = container.scrollLeft + event.deltaY;
|
||||
});
|
||||
},
|
||||
|
||||
@ -242,7 +242,6 @@ export default {
|
||||
onHandleScroll(event) {
|
||||
this.emojiMenuScrollLeft = event.target.scrollLeft;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -903,7 +903,7 @@ export default {
|
||||
&& $A.isArray(window.emoticonData)) {
|
||||
// 显示快捷选择表情窗口
|
||||
this.emojiQuickItems = [];
|
||||
const baseUrl = $A.apiUrl("../images/emoticon")
|
||||
const baseUrl = $A.mainUrl("images/emoticon")
|
||||
window.emoticonData.some(data => {
|
||||
let j = 0
|
||||
data.list.some(item => {
|
||||
|
||||
@ -92,9 +92,9 @@ export default {
|
||||
ext = 'ppt'
|
||||
}
|
||||
if (["ai", "avi", "bmp", "cdr", "doc", "eps", "gif", "mov", "mp3", "mp4", "pdf", "ppt", "pr", "psd", "rar", "svg", "tif", "txt", "xls", "zip"].includes(ext)) {
|
||||
data.thumb = $A.apiUrl(`../images/ext/${ext}.png`)
|
||||
data.thumb = $A.mainUrl(`images/ext/${ext}.png`)
|
||||
} else {
|
||||
data.thumb = $A.apiUrl(`../images/ext/file.png`)
|
||||
data.thumb = $A.mainUrl(`images/ext/file.png`)
|
||||
}
|
||||
this.fileMsgCaches[cacheName] = data
|
||||
},
|
||||
|
||||
@ -1503,7 +1503,7 @@ export default {
|
||||
if (this.dialogData.extra_quote_type === 'update') {
|
||||
// 修改
|
||||
if (textType === "text") {
|
||||
textBody = textBody.replace(new RegExp(`src=(["'])${$A.apiUrl('../')}`, "g"), "src=$1{{RemoteURL}}")
|
||||
textBody = textBody.replace(new RegExp(`src=(["'])${$A.mainUrl()}`, "g"), "src=$1{{RemoteURL}}")
|
||||
}
|
||||
const update_id = this.quoteId
|
||||
this.$store.dispatch("setLoad", {
|
||||
@ -3180,7 +3180,7 @@ export default {
|
||||
},
|
||||
})
|
||||
} else {
|
||||
window.open($A.apiUrl(`..${path}`))
|
||||
window.open($A.mainUrl(path.substring(1)))
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -209,7 +209,7 @@ export default {
|
||||
},
|
||||
})
|
||||
} else {
|
||||
window.open($A.apiUrl(`..${path}`))
|
||||
window.open($A.mainUrl(path.substring(1)))
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ export default {
|
||||
vNode.push(h('span', ': '))
|
||||
vNode.push(h('a', {
|
||||
attrs: {
|
||||
href: $A.baseUrl(url),
|
||||
href: $A.mainUrl(url),
|
||||
target: '_blank'
|
||||
}
|
||||
}, this.$L(title)))
|
||||
|
||||
@ -168,7 +168,20 @@ export default {
|
||||
case 'preview':
|
||||
const title = (this.taskName || `ID: ${this.taskId}`) + ` [${row.created_at}]`;
|
||||
const path = `/single/task/content/${this.taskId}?history_id=${row.id}&history_title=${title}`;
|
||||
if (this.$isEEUiApp) {
|
||||
if (this.$Electron) {
|
||||
this.$store.dispatch('openChildWindow', {
|
||||
name: `task-content-${this.taskId}-${row.id}`,
|
||||
path: path,
|
||||
force: false,
|
||||
config: {
|
||||
title: title,
|
||||
titleFixed: true,
|
||||
parent: null,
|
||||
width: Math.min(window.screen.availWidth, 1440),
|
||||
height: Math.min(window.screen.availHeight, 900),
|
||||
},
|
||||
});
|
||||
} else if (this.$isEEUiApp) {
|
||||
this.$store.dispatch('openAppChildPage', {
|
||||
pageType: 'app',
|
||||
pageTitle: title,
|
||||
@ -180,7 +193,7 @@ export default {
|
||||
},
|
||||
})
|
||||
} else {
|
||||
window.open($A.apiUrl(`..${path}`))
|
||||
window.open($A.mainUrl(path.substring(1)))
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1660,7 +1660,7 @@ export default {
|
||||
},
|
||||
});
|
||||
} else {
|
||||
window.open($A.apiUrl(`..${path}`))
|
||||
window.open($A.mainUrl(path.substring(1)))
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -1162,7 +1162,7 @@ export default {
|
||||
},
|
||||
});
|
||||
} else {
|
||||
window.open($A.apiUrl(`..${path}`))
|
||||
window.open($A.mainUrl(path.substring(1)))
|
||||
}
|
||||
this.browseFile(0);
|
||||
},
|
||||
|
||||
@ -209,7 +209,7 @@ export default {
|
||||
onVersion() {
|
||||
const array = []
|
||||
this.getServerVersion().then(version => {
|
||||
array.push(`${this.$L('服务器')}: ${$A.getDomain($A.apiUrl('../'))}`)
|
||||
array.push(`${this.$L('服务器')}: ${$A.getDomain($A.mainUrl())}`)
|
||||
array.push(`${this.$L('服务器版本')}: v${version}`)
|
||||
array.push(`${this.$L('客户端版本')}: v${this.version}`)
|
||||
$A.modalInfo({
|
||||
|
||||
@ -7,9 +7,9 @@ import {languageName} from "../language";
|
||||
export default {
|
||||
mounted() {
|
||||
if (languageName === "zh" || languageName === "zh-CHT") {
|
||||
window.location.href = $A.apiUrl("../site/zh/price.html")
|
||||
window.location.href = $A.mainUrl("site/zh/price.html")
|
||||
} else {
|
||||
window.location.href = $A.apiUrl("../site/en/price.html")
|
||||
window.location.href = $A.mainUrl("site/en/price.html")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export default {
|
||||
if (to.name == 'single-apps') {
|
||||
this.$nextTick(() => {
|
||||
this.loading = false;
|
||||
this.appUrl = import.meta.env.VITE_OKR_WEB_URL || $A.apiUrl("../apps/okr")
|
||||
this.appUrl = import.meta.env.VITE_OKR_WEB_URL || $A.mainUrl("apps/okr")
|
||||
this.path = this.$route.query.path || '';
|
||||
})
|
||||
}else{
|
||||
|
||||
12
resources/assets/js/store/actions.js
vendored
12
resources/assets/js/store/actions.js
vendored
@ -644,13 +644,13 @@ export default {
|
||||
$A.eeuiAppSendMessage({
|
||||
action: 'userChatList',
|
||||
token: state.userToken,
|
||||
url: $A.apiUrl('../api/users/share/list') + `?token=${state.userToken}`
|
||||
url: $A.mainUrl('api/users/share/list') + `?token=${state.userToken}`
|
||||
});
|
||||
$A.eeuiAppSendMessage({
|
||||
action:"userUploadUrl",
|
||||
token: state.userToken,
|
||||
dirUrl: $A.apiUrl('../api/file/content/upload') + `?token=${state.userToken}`,
|
||||
chatUrl: $A.apiUrl('../api/dialog/msg/sendfiles') + `?token=${state.userToken}`,
|
||||
dirUrl: $A.mainUrl('api/file/content/upload') + `?token=${state.userToken}`,
|
||||
chatUrl: $A.mainUrl('api/dialog/msg/sendfiles') + `?token=${state.userToken}`,
|
||||
});
|
||||
//
|
||||
resolve()
|
||||
@ -991,7 +991,7 @@ export default {
|
||||
* @param url
|
||||
*/
|
||||
openWebTabWindow({dispatch}, url) {
|
||||
if ($A.getDomain(url) != $A.getDomain($A.apiUrl('../'))) {
|
||||
if ($A.getDomain(url) != $A.getDomain($A.mainUrl())) {
|
||||
$A.Electron.sendMessage('openWebTabWindow', {url})
|
||||
return
|
||||
}
|
||||
@ -3298,7 +3298,7 @@ export default {
|
||||
*/
|
||||
streamDialogMsg({state, dispatch}, streamUrl) {
|
||||
if (!/^https*:\/\//i.test(streamUrl)) {
|
||||
streamUrl = $A.apiUrl(`..${streamUrl}`)
|
||||
streamUrl = $A.mainUrl(streamUrl.substring(1))
|
||||
}
|
||||
if (state.dialogSseList.find(item => item.streamUrl == streamUrl)) {
|
||||
return
|
||||
@ -3488,7 +3488,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
//
|
||||
let url = $A.apiUrl('../ws');
|
||||
let url = $A.mainUrl('ws');
|
||||
url = url.replace("https://", "wss://");
|
||||
url = url.replace("http://", "ws://");
|
||||
url += `?action=web&token=${state.userToken}&language=${languageName}`;
|
||||
|
||||
7
resources/assets/sass/pages/common.scss
vendored
7
resources/assets/sass/pages/common.scss
vendored
@ -127,6 +127,13 @@ body {
|
||||
.ivu-table-cell {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
|
||||
.common-avatar {
|
||||
.avatar-name {
|
||||
width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thead {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user