feat: follow-me

This commit is contained in:
老周部落 2024-04-25 20:45:41 +08:00
parent af109b45ef
commit 22b52ac3d3
No known key found for this signature in database
GPG key ID: C72181CD85C6B738
5 changed files with 107 additions and 3 deletions

View file

@ -1011,6 +1011,8 @@ isSystemAccount: "This account is created and automatically operated by the syst
Please do not moderate, edit, delete, or otherwise tamper with this account, or
it may break your server."
typeToConfirm: "Please enter {x} to confirm"
useThisAccountConfirm: "Do you want to continue with this account?"
inputAccountId: "Please input your account (e.g., @firefish@info.firefish.dev )"
deleteAccount: "Delete account"
document: "Documentation"
numberOfPageCache: "Number of cached pages"
@ -1158,6 +1160,8 @@ confirm: "Confirm"
importZip: "Import ZIP"
exportZip: "Export ZIP"
getQrCode: "Get QR code"
remoteFollow: "Remote Follow"
remoteFollowUrl: "Remote Follow URL"
emojiPackCreator: "Emoji pack creator"
indexable: "Indexable"
indexableDescription: "Allow built-in search to show your public posts"

View file

@ -879,6 +879,8 @@ driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。
requireAdminForView: "您需要使用管理员账号登录才能查看。"
isSystemAccount: "该账号由系统自动创建。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。"
typeToConfirm: "输入 {x} 以确认操作"
useThisAccountConfirm: "您想使用此帐户继续执行此操作吗?"
inputAccountId: "请输入您的帐户(例如 @firefish@info.firefish.dev "
deleteAccount: "删除账号"
document: "文档"
numberOfPageCache: "缓存页数"
@ -1975,6 +1977,8 @@ confirm: 确认
importZip: 导入 ZIP
exportZip: 导出 ZIP
getQrCode: "获取二维码"
remoteFollow: "远程关注"
remoteFollowUrl: "远程关注 URL"
emojiPackCreator: 表情包创建工具
objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com/<bucket>/" 而非 "<bucket>.s3.amazonaws.com"
的端点 URL。

View file

@ -0,0 +1,78 @@
<template>
<div class="mk-follow-page"></div>
</template>
<script lang="ts" setup>
import { acct } from "firefish-js";
import * as os from "@/os";
import { i18n } from "@/i18n";
import { host as hostRaw } from "@/config";
import { isSignedIn, me } from "@/me";
import { waiting } from "@/os";
const acctUri = new URL(location.href).searchParams.get("acct");
if (acctUri == null) {
throw new Error("acct required");
}
let useThisAccount = isSignedIn(me) ? true : false;
// If the user is already logged in, ask whether to follow using the current account.
if (useThisAccount) {
const { canceled } = await os.confirm({
type: "question",
text: i18n.ts.useThisAccountConfirm,
});
if (!canceled) {
waiting();
window.location.href = `/authorize-follow?acct=${acctUri}`;
} else {
useThisAccount = false;
}
}
if (!useThisAccount) {
// Ask the user what the account ID is
const remoteAccountId = await os.inputText({
text: i18n.ts.inputAccountId,
});
// If the user do not want enter uri, the user will be redirected to the user page.
if (!remoteAccountId.result) {
waiting();
window.location.href = `/@${acctUri}`;
} else {
const remoteAcctInfo = acct.parse(remoteAccountId.result);
// If the user on this server, redirect directly
if (remoteAcctInfo.host === hostRaw || remoteAcctInfo.host === null) {
waiting();
window.location.href = `/authorize-follow?acct=${acctUri}`;
} else {
waiting();
// If not, find the interaction url through webfinger interface
fetch(
`https://${remoteAcctInfo.host}/.well-known/webfinger?resource=${remoteAcctInfo.username}@${remoteAcctInfo.host}`,
{
method: "GET",
},
)
.then((response) => response.json())
.then((data) => {
const subscribeUri = data.links.find(
(link) => link.rel === "http://ostatus.org/schema/1.0/subscribe",
).template;
window.location.href = subscribeUri.replace(
"{uri}",
acctUri.includes("@") ? acctUri : `${acctUri}@${hostRaw}`,
);
})
.catch((e) => {
// TODO: It would be better to provide more information, but the priority of
// waiting component is too high and the pop-up window will be blocked.
window.location.href = `/@${acctUri}`;
});
}
}
}
</script>

View file

@ -325,6 +325,10 @@ export const routes: RouteDef[] = [
component: page(() => import("./pages/follow.vue")),
loginRequired: true,
},
{
path: "/follow-me",
component: page(() => import("./pages/follow-me.vue")),
},
{
path: "/authorize_interaction",
component: page(() => import("./pages/authorize_interaction.vue")),

View file

@ -281,6 +281,13 @@ export function getUserMenu(user, router: Router = mainRouter) {
copyToClipboard(`https://${host}/@${user.username}.json`);
},
},
{
icon: `${icon("ph-hand-waving")}`,
text: i18n.ts.remoteFollowUrl,
action: () => {
copyToClipboard(`https://${host}/follow-me?acct=${user.username}`);
},
},
],
},
{
@ -290,13 +297,20 @@ export function getUserMenu(user, router: Router = mainRouter) {
os.post({ specified: user });
},
},
!isSignedIn(me)
? {
icon: `${icon("ph-hand-waving")}`,
text: i18n.ts.remoteFollow,
action: () => {
router.push(`/follow-me?acct=${user.username}`);
},
}
: undefined,
{
icon: "ph-qr-code ph-bold ph-lg",
text: i18n.ts.getQrCode,
action: () => {
os.displayQrCode(
`https://${host}/authorize-follow?acct=${user.username}`,
);
os.displayQrCode(`https://${host}/follow-me?acct=${user.username}`);
},
},
isSignedIn(me) && me.id !== user.id