feat: more details for invite link
This commit is contained in:
@@ -54,5 +54,23 @@
|
|||||||
"start_download": "Start by downloading VoceChat mobile app",
|
"start_download": "Start by downloading VoceChat mobile app",
|
||||||
"open": "Open VoceChat",
|
"open": "Open VoceChat",
|
||||||
"have_already": "Have the app already?"
|
"have_already": "Have the app already?"
|
||||||
|
},
|
||||||
|
"invite_expire": {
|
||||||
|
"min30": "30 minutes",
|
||||||
|
"h1": "1 hour",
|
||||||
|
"h6": "6 hours",
|
||||||
|
"h12": "12 hours",
|
||||||
|
"d1": "1 day",
|
||||||
|
"d7": "7 days",
|
||||||
|
"d30": "30 days"
|
||||||
|
},
|
||||||
|
"invite_times": {
|
||||||
|
"no_limit": "no limit",
|
||||||
|
"time1": "1 use",
|
||||||
|
"times5": "5 uses",
|
||||||
|
"times10": "10 uses",
|
||||||
|
"times25": "25 uses",
|
||||||
|
"times50": "50 uses",
|
||||||
|
"times100": "100 uses"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
"send_invite_link": "Or Send invite link to your friends",
|
"send_invite_link": "Or Send invite link to your friends",
|
||||||
"share_invite_link": "Share this link to invite people to this server.",
|
"share_invite_link": "Share this link to invite people to this server.",
|
||||||
"invite_link_faq": "Incorrect Invite Link?",
|
"invite_link_faq": "Incorrect Invite Link?",
|
||||||
"invite_link_expire": "Invite link expires in 48 hours",
|
"invite_link_edit": "Edit invite link",
|
||||||
|
"invite_link_setting_tip": "Invite link expires in {{expire}}, max use times: {{times}}",
|
||||||
"generate_new_link": "Generate New Link",
|
"generate_new_link": "Generate New Link",
|
||||||
|
|
||||||
"send_to": "Send To",
|
"send_to": "Send To",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"send_invite_link": "もしくは、招待用URLをコピーする",
|
"send_invite_link": "もしくは、招待用URLをコピーする",
|
||||||
"share_invite_link": "このリンクを使って、友達を招待する",
|
"share_invite_link": "このリンクを使って、友達を招待する",
|
||||||
|
|
||||||
"invite_link_expire": "招待用URLの有効期限は48時間です",
|
"invite_link_setting_tip": "招待用URLの有効期限は48時間です",
|
||||||
"generate_new_link": "新しいリンクを生成する",
|
"generate_new_link": "新しいリンクを生成する",
|
||||||
|
|
||||||
"send_to": "送信先"
|
"send_to": "送信先"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"send_invite_link": "Veya arkadaşlarınıza davet bağlantısı gönderin",
|
"send_invite_link": "Veya arkadaşlarınıza davet bağlantısı gönderin",
|
||||||
"share_invite_link": "Diğerlerini bu bağlantıyı paylaşarak bu sunucuya davet edebilirsiniz.",
|
"share_invite_link": "Diğerlerini bu bağlantıyı paylaşarak bu sunucuya davet edebilirsiniz.",
|
||||||
"invite_link_faq": "Hatalı davet bağlantısı mı?",
|
"invite_link_faq": "Hatalı davet bağlantısı mı?",
|
||||||
"invite_link_expire": "Davet bağlantısı 48 saat geçerli olacaktır.",
|
"invite_link_setting_tip": "Davet bağlantısı 48 saat geçerli olacaktır.",
|
||||||
"generate_new_link": "Yeni bağlantı yarat",
|
"generate_new_link": "Yeni bağlantı yarat",
|
||||||
|
|
||||||
"send_to": "Şuna gönder",
|
"send_to": "Şuna gönder",
|
||||||
|
|||||||
@@ -48,5 +48,23 @@
|
|||||||
"title": "邀请链接已过期!",
|
"title": "邀请链接已过期!",
|
||||||
"desc": "请重新生成邀请链接",
|
"desc": "请重新生成邀请链接",
|
||||||
"desc_close": "该网页可关闭"
|
"desc_close": "该网页可关闭"
|
||||||
|
},
|
||||||
|
"invite_expire": {
|
||||||
|
"min30": "30 minutes",
|
||||||
|
"h1": "1 hour",
|
||||||
|
"h6": "6 hours",
|
||||||
|
"h12": "12 hours",
|
||||||
|
"d1": "1 day",
|
||||||
|
"d7": "7 days",
|
||||||
|
"d30": "30 days"
|
||||||
|
},
|
||||||
|
"invite_times": {
|
||||||
|
"no_limit": "no limit",
|
||||||
|
"time1": "1 use",
|
||||||
|
"times5": "5 uses",
|
||||||
|
"times10": "10 uses",
|
||||||
|
"times25": "25 uses",
|
||||||
|
"times50": "50 uses",
|
||||||
|
"times100": "100 uses"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
"send_invite_link": "或者,向朋友发送邀请链接",
|
"send_invite_link": "或者,向朋友发送邀请链接",
|
||||||
"share_invite_link": "分享此链接,邀请朋友加入",
|
"share_invite_link": "分享此链接,邀请朋友加入",
|
||||||
"invite_link_faq": "链接异常?",
|
"invite_link_faq": "链接异常?",
|
||||||
|
"invite_link_edit": "编辑邀请链接",
|
||||||
"invite_link_expire": "邀请链接有效期:48小时",
|
"invite_link_setting_tip": "邀请链接有效期:{{expire}},最多使用次数:{{times}}",
|
||||||
"generate_new_link": "生成新邀请链接",
|
"generate_new_link": "生成新邀请链接",
|
||||||
|
|
||||||
"send_to": "发送至",
|
"send_to": "发送至",
|
||||||
|
|||||||
@@ -125,5 +125,65 @@ export const KEY_PWA_INSTALLED = "VOCECHAT_PWA_INSTALLED";
|
|||||||
export const KEY_LOCAL_MAGIC_TOKEN = "VOCECHAT_LOCAL_MAGIC_TOKEN";
|
export const KEY_LOCAL_MAGIC_TOKEN = "VOCECHAT_LOCAL_MAGIC_TOKEN";
|
||||||
export const KEY_LOCAL_TRY_PATH = "VOCECHAT_TRY_PATH";
|
export const KEY_LOCAL_TRY_PATH = "VOCECHAT_TRY_PATH";
|
||||||
export const Emojis = ["👍", "❤️", "😄", "👀", "👎", "🎉", "🙁", "🚀"];
|
export const Emojis = ["👍", "❤️", "😄", "👀", "👎", "🎉", "🙁", "🚀"];
|
||||||
|
export const getInviteLinkExpireList = () => [
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_expire.min30"),
|
||||||
|
value: 30 * 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_expire.h1"),
|
||||||
|
value: 60 * 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_expire.h6"),
|
||||||
|
value: 6 * 60 * 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_expire.h12"),
|
||||||
|
value: 12 * 60 * 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_expire.d1"),
|
||||||
|
value: 24 * 60 * 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_expire.d7"),
|
||||||
|
value: 7 * 24 * 60 * 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_expire.d30"),
|
||||||
|
value: 30 * 24 * 60 * 60
|
||||||
|
}
|
||||||
|
];
|
||||||
|
export const getInviteLinkTimesList = () => [
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_times.no_limit"),
|
||||||
|
value: -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_times.time1"),
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_times.times5"),
|
||||||
|
value: 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_times.times10"),
|
||||||
|
value: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_times.times25"),
|
||||||
|
value: 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_times.times50"),
|
||||||
|
value: 50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.t("auth:invite_times.times100"),
|
||||||
|
value: 100
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default BASE_URL;
|
export default BASE_URL;
|
||||||
|
|||||||
@@ -82,15 +82,13 @@ export const channelApi = createApi({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
createInviteLink: builder.query<string, number | void>({
|
createInviteLink: builder.query<string, { expire: number; times: number }>({
|
||||||
query: (gid) => ({
|
query: (data) => ({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
accept: "text/plain"
|
accept: "text/plain"
|
||||||
},
|
},
|
||||||
url: gid
|
url: `/group/create_reg_magic_link?expired_in=${data.expire}&max_times=${data.times}`,
|
||||||
? `/group/create_reg_magic_link?expired_in=3600&max_times=1&gid=${gid}`
|
|
||||||
: `/group/create_reg_magic_link?expired_in=3600&max_times=1`,
|
|
||||||
responseHandler: "text"
|
responseHandler: "text"
|
||||||
}),
|
}),
|
||||||
transformResponse: (link: string) => {
|
transformResponse: (link: string) => {
|
||||||
@@ -117,14 +115,17 @@ export const channelApi = createApi({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
createPrivateInviteLink: builder.query<string, number | void>({
|
createPrivateInviteLink: builder.query<
|
||||||
query: (gid) => ({
|
string,
|
||||||
|
{ cid: number; expire: number; times: number | null }
|
||||||
|
>({
|
||||||
|
query: (data) => ({
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "text/plain",
|
"content-type": "text/plain",
|
||||||
accept: "text/plain"
|
accept: "text/plain"
|
||||||
},
|
},
|
||||||
// 七天过期
|
// 七天过期
|
||||||
url: `/group/create_invite_private_magic_link?expired_in=604800&max_times=1&gid=${gid}`,
|
url: `/group/create_invite_private_magic_link?expired_in=${data.expire}&max_times=${data.times}&gid=${data.cid}`,
|
||||||
responseHandler: "text"
|
responseHandler: "text"
|
||||||
}),
|
}),
|
||||||
transformResponse: (link: string) => {
|
transformResponse: (link: string) => {
|
||||||
|
|||||||
@@ -21,15 +21,12 @@ import {
|
|||||||
TestEmailDTO
|
TestEmailDTO
|
||||||
} from "@/types/server";
|
} from "@/types/server";
|
||||||
import { User } from "@/types/user";
|
import { User } from "@/types/user";
|
||||||
import { transformInviteLink } from "@/utils";
|
|
||||||
import BASE_URL, { ContentTypes, IS_OFFICIAL_DEMO, PAYMENT_URL_PREFIX } from "../config";
|
import BASE_URL, { ContentTypes, IS_OFFICIAL_DEMO, PAYMENT_URL_PREFIX } from "../config";
|
||||||
import { updateInfo } from "../slices/server";
|
import { updateInfo } from "../slices/server";
|
||||||
import { updateCallInfo, upsertVoiceList } from "../slices/voice";
|
import { updateCallInfo, upsertVoiceList } from "../slices/voice";
|
||||||
import { RootState } from "../store";
|
import { RootState } from "../store";
|
||||||
import baseQuery from "./base.query";
|
import baseQuery from "./base.query";
|
||||||
|
|
||||||
const defaultExpireDuration = 2 * 24 * 60 * 60;
|
|
||||||
|
|
||||||
export const serverApi = createApi({
|
export const serverApi = createApi({
|
||||||
reducerPath: "serverApi",
|
reducerPath: "serverApi",
|
||||||
baseQuery,
|
baseQuery,
|
||||||
@@ -263,19 +260,6 @@ export const serverApi = createApi({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
createInviteLink: builder.query<string, number>({
|
|
||||||
query: (expired_in = defaultExpireDuration) => ({
|
|
||||||
headers: {
|
|
||||||
"content-type": "text/plain",
|
|
||||||
accept: "text/plain"
|
|
||||||
},
|
|
||||||
url: `/admin/system/create_invite_link?expired_in=${expired_in}`,
|
|
||||||
responseHandler: "text"
|
|
||||||
}),
|
|
||||||
transformResponse: (link: string) => {
|
|
||||||
return transformInviteLink(link);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
updateServer: builder.mutation<void, Server>({
|
updateServer: builder.mutation<void, Server>({
|
||||||
query: (data) => ({
|
query: (data) => ({
|
||||||
url: "admin/system/organization",
|
url: "admin/system/organization",
|
||||||
@@ -434,8 +418,6 @@ export const {
|
|||||||
useLazyGetServerQuery,
|
useLazyGetServerQuery,
|
||||||
useUpdateServerMutation,
|
useUpdateServerMutation,
|
||||||
useUpdateLogoMutation,
|
useUpdateLogoMutation,
|
||||||
useCreateInviteLinkQuery,
|
|
||||||
useLazyCreateInviteLinkQuery,
|
|
||||||
useGetThirdPartySecretQuery,
|
useGetThirdPartySecretQuery,
|
||||||
useUpdateThirdPartySecretMutation,
|
useUpdateThirdPartySecretMutation,
|
||||||
useCreateAdminMutation,
|
useCreateAdminMutation,
|
||||||
|
|||||||
@@ -1,22 +1,56 @@
|
|||||||
import { FC } from "react";
|
import { FC, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { getInviteLinkExpireList, getInviteLinkTimesList } from "@/app/config";
|
||||||
import useInviteLink from "@/hooks/useInviteLink";
|
import useInviteLink from "@/hooks/useInviteLink";
|
||||||
import IconQuestion from "@/assets/icons/question.svg";
|
import IconQuestion from "@/assets/icons/question.svg";
|
||||||
|
import Modal from "./Modal";
|
||||||
import QRCode from "./QRCode";
|
import QRCode from "./QRCode";
|
||||||
import Button from "./styled/Button";
|
import Button from "./styled/Button";
|
||||||
import Input from "./styled/Input";
|
import Input from "./styled/Input";
|
||||||
|
import StyledModal from "./styled/Modal";
|
||||||
|
import Select from "./styled/Select";
|
||||||
|
|
||||||
type Props = {};
|
type Props = {
|
||||||
const InviteLink: FC<Props> = () => {
|
context?: "members" | "channel";
|
||||||
const { t } = useTranslation("chat");
|
cid?: number;
|
||||||
const { generating, link, linkCopied, copyLink, generateNewLink } = useInviteLink();
|
|
||||||
const handleNewLink = () => {
|
|
||||||
generateNewLink();
|
|
||||||
};
|
};
|
||||||
|
const InviteLinkExpireList = getInviteLinkExpireList();
|
||||||
|
const InviteLinkTimesList = getInviteLinkTimesList();
|
||||||
|
|
||||||
|
const InviteLink: FC<Props> = ({ context = "members", cid }) => {
|
||||||
|
const [current, setCurrent] = useState({
|
||||||
|
expire: InviteLinkExpireList[4],
|
||||||
|
times: InviteLinkTimesList[0]
|
||||||
|
});
|
||||||
|
const [selectExpire, setSelectExpire] = useState(current.expire);
|
||||||
|
const [selectTimes, setSelectTimes] = useState(current.times);
|
||||||
|
const [editVisible, setEditVisible] = useState(false);
|
||||||
|
const { t } = useTranslation("chat");
|
||||||
|
const { generating, link, linkCopied, copyLink, generateNewLink } = useInviteLink(cid);
|
||||||
|
const handleNewLink = () => {
|
||||||
|
const { expire, times } = current;
|
||||||
|
generateNewLink({ expire: expire.value, times: times.value });
|
||||||
|
};
|
||||||
|
const toggleEditVisible = () => {
|
||||||
|
setEditVisible((prev) => !prev);
|
||||||
|
};
|
||||||
|
const handleUpdate = () => {
|
||||||
|
setCurrent({
|
||||||
|
expire: selectExpire,
|
||||||
|
times: selectTimes
|
||||||
|
});
|
||||||
|
toggleEditVisible();
|
||||||
|
handleNewLink();
|
||||||
|
};
|
||||||
|
// useEffect(() => {
|
||||||
|
// handleNewLink();
|
||||||
|
// }, [current]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="flex flex-col items-start pb-8">
|
<div className="flex flex-col items-start pb-8">
|
||||||
|
{context == "members" && (
|
||||||
<p className="font-semibold text-sm mb-2 text-gray-500 dark:text-gray-50 flex flex-col md:flex-row gap-4">
|
<p className="font-semibold text-sm mb-2 text-gray-500 dark:text-gray-50 flex flex-col md:flex-row gap-4">
|
||||||
{t("share_invite_link")}
|
{t("share_invite_link")}
|
||||||
<a
|
<a
|
||||||
@@ -28,6 +62,7 @@ const InviteLink: FC<Props> = () => {
|
|||||||
<IconQuestion /> {t("invite_link_faq")}
|
<IconQuestion /> {t("invite_link_faq")}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
<div className="w-full md:w-[512px] mb-3 relative">
|
<div className="w-full md:w-[512px] mb-3 relative">
|
||||||
<Input readOnly className={"large !pr-16"} placeholder="Generating" value={link} />
|
<Input readOnly className={"large !pr-16"} placeholder="Generating" value={link} />
|
||||||
<Button
|
<Button
|
||||||
@@ -37,7 +72,15 @@ const InviteLink: FC<Props> = () => {
|
|||||||
{linkCopied ? "Copied" : t("action.copy", { ns: "common" })}
|
{linkCopied ? "Copied" : t("action.copy", { ns: "common" })}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-gray-600 dark:text-gray-100">{t("invite_link_expire")}</span>
|
<span className="text-xs text-gray-600 dark:text-gray-100 flex gap-2">
|
||||||
|
{t("invite_link_setting_tip", {
|
||||||
|
expire: current.expire.label,
|
||||||
|
times: current.times.label
|
||||||
|
})}
|
||||||
|
<button className="text-primary-500 flex gap-1 items-center" onClick={toggleEditVisible}>
|
||||||
|
{t("invite_link_edit")}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
<div className="w-44 h-44 my-2">
|
<div className="w-44 h-44 my-2">
|
||||||
<QRCode size={1200} link={link} />
|
<QRCode size={1200} link={link} />
|
||||||
</div>
|
</div>
|
||||||
@@ -45,6 +88,70 @@ const InviteLink: FC<Props> = () => {
|
|||||||
{generating ? `Generating` : t("generate_new_link")}
|
{generating ? `Generating` : t("generate_new_link")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
{editVisible && (
|
||||||
|
<Modal id="modal-modal">
|
||||||
|
<StyledModal
|
||||||
|
compact
|
||||||
|
title={"Update Invite Link Settings"}
|
||||||
|
buttons={
|
||||||
|
<>
|
||||||
|
<Button onClick={toggleEditVisible} className="cancel small">
|
||||||
|
{t("action.cancel", { ns: "common" })}
|
||||||
|
</Button>
|
||||||
|
<Button className="main small" onClick={handleUpdate}>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="flex flex-col gap-2 items-start">
|
||||||
|
<span className="text-sm dark:text-gray-100">Expire After:</span>
|
||||||
|
<Select
|
||||||
|
options={InviteLinkExpireList.map((item) => {
|
||||||
|
const { label, value } = item;
|
||||||
|
return {
|
||||||
|
title: label,
|
||||||
|
value: `${value}`,
|
||||||
|
selected: selectExpire.value === value
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
current={{
|
||||||
|
title: selectExpire.label
|
||||||
|
}}
|
||||||
|
updateSelect={(option) => {
|
||||||
|
setSelectExpire(
|
||||||
|
InviteLinkExpireList.find((item) => item.value === Number(option.value))!
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
></Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2 items-start">
|
||||||
|
<span className="text-sm dark:text-gray-100">Max Times of Uses:</span>
|
||||||
|
<Select
|
||||||
|
options={InviteLinkTimesList.map((item) => {
|
||||||
|
const { label, value } = item;
|
||||||
|
return {
|
||||||
|
title: label,
|
||||||
|
value: `${value}`,
|
||||||
|
selected: selectTimes.value === value
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
current={{
|
||||||
|
title: selectTimes.label
|
||||||
|
}}
|
||||||
|
updateSelect={(option) => {
|
||||||
|
setSelectTimes(
|
||||||
|
InviteLinkTimesList.find((item) => item.value === Number(option.value))!
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
></Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</StyledModal>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
|
|
||||||
import { useSendLoginMagicLinkMutation } from "@/app/services/auth";
|
import { useSendLoginMagicLinkMutation } from "@/app/services/auth";
|
||||||
import useInviteLink from "@/hooks/useInviteLink";
|
import useInviteLink from "@/hooks/useInviteLink";
|
||||||
import QRCode from "../QRCode";
|
import InviteLink from "../InviteLink";
|
||||||
import Button from "../styled/Button";
|
import Button from "../styled/Button";
|
||||||
import Input from "../styled/Input";
|
import Input from "../styled/Input";
|
||||||
|
|
||||||
@@ -18,13 +18,7 @@ const InviteByEmail: FC<Props> = ({ cid }) => {
|
|||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const formRef = useRef<HTMLFormElement | null>(null);
|
const formRef = useRef<HTMLFormElement | null>(null);
|
||||||
const [sendMagicLinkByEmail, { isSuccess, isLoading }] = useSendLoginMagicLinkMutation();
|
const [sendMagicLinkByEmail, { isSuccess, isLoading }] = useSendLoginMagicLinkMutation();
|
||||||
const { enableSMTP, linkCopied, link, copyLink, generateNewLink, generating } =
|
const { enableSMTP } = useInviteLink(cid);
|
||||||
useInviteLink(cid);
|
|
||||||
useEffect(() => {
|
|
||||||
if (linkCopied) {
|
|
||||||
toast.success("Invite Link Copied!");
|
|
||||||
}
|
|
||||||
}, [linkCopied]);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
toast.success("Email Sent!");
|
toast.success("Email Sent!");
|
||||||
@@ -77,26 +71,7 @@ const InviteByEmail: FC<Props> = ({ cid }) => {
|
|||||||
<label className="text-sm text-gray-400 dark:text-gray-100" htmlFor="">
|
<label className="text-sm text-gray-400 dark:text-gray-100" htmlFor="">
|
||||||
{t("send_invite_link")}
|
{t("send_invite_link")}
|
||||||
</label>
|
</label>
|
||||||
<div className="relative flex items-center gap-2">
|
<InviteLink context="channel" cid={cid} />
|
||||||
<Input readOnly className="!pr-[50px]" placeholder="Generating" value={link} />
|
|
||||||
<button
|
|
||||||
className="absolute right-1 top-1/2 -translate-y-1/2 pr-2 text-sm text-primary-400 hover:text-primary-600"
|
|
||||||
onClick={copyLink}
|
|
||||||
>
|
|
||||||
{ct("action.copy")}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-44 h-44 my-2">{!generating && <QRCode size={1200} link={link} />}</div>
|
|
||||||
<div className="text-xs text-gray-600 dark:text-gray-200">
|
|
||||||
{t("invite_link_expire")}
|
|
||||||
<button
|
|
||||||
disabled={generating}
|
|
||||||
className="text-primary-400 ml-1"
|
|
||||||
onClick={() => generateNewLink()}
|
|
||||||
>
|
|
||||||
{t("generate_new_link")}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const Select: FC<Props> = ({ options = [], updateSelect = null, current = null }
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tippy
|
<Tippy
|
||||||
|
onClickOutside={setOptionsVisible.bind(null, false)}
|
||||||
visible={optionsVisible}
|
visible={optionsVisible}
|
||||||
appendTo={document.body}
|
appendTo={document.body}
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
@@ -59,10 +60,10 @@ const Select: FC<Props> = ({ options = [], updateSelect = null, current = null }
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="select-none border border-solid border-slate-200 p-2 flex items-center gap-2"
|
className="cursor-pointer select-none border border-slate-200 dark:border-slate-800 p-2 flex items-center gap-2"
|
||||||
onClick={toggleVisible}
|
onClick={toggleVisible}
|
||||||
>
|
>
|
||||||
<span className="text-sm text-gray-500 dark:text-gray-200 min-w-[76px]">
|
<span className="text-sm text-gray-500 dark:text-gray-200 min-w-[120px]">
|
||||||
{(current !== null ? current : curr)?.title || t("action.select")}
|
{(current !== null ? current : curr)?.title || t("action.select")}
|
||||||
</span>
|
</span>
|
||||||
<IconArrow className="!w-5 !h-5" />
|
<IconArrow className="!w-5 !h-5" />
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { getInviteLinkExpireList, getInviteLinkTimesList } from "@/app/config";
|
||||||
import {
|
import {
|
||||||
useLazyCreateInviteLinkQuery as useCreateChannelInviteLinkQuery,
|
useLazyCreateInviteLinkQuery,
|
||||||
useLazyCreatePrivateInviteLinkQuery
|
useLazyCreatePrivateInviteLinkQuery
|
||||||
} from "@/app/services/channel";
|
} from "@/app/services/channel";
|
||||||
import { useGetSMTPStatusQuery } from "@/app/services/server";
|
import { useGetSMTPStatusQuery } from "@/app/services/server";
|
||||||
import { useAppSelector } from "@/app/store";
|
import { useAppSelector } from "@/app/store";
|
||||||
import useCopy from "./useCopy";
|
import useCopy from "./useCopy";
|
||||||
|
|
||||||
|
const defaultExpire = getInviteLinkExpireList()[4].value;
|
||||||
|
const defaultTimes = getInviteLinkTimesList()[0].value;
|
||||||
|
type ParamsProps = { expire: number; times: number };
|
||||||
|
const defaultParams: ParamsProps = { expire: defaultExpire, times: defaultTimes };
|
||||||
export default function useInviteLink(cid?: number) {
|
export default function useInviteLink(cid?: number) {
|
||||||
const [finalLink, setFinalLink] = useState("");
|
const [finalLink, setFinalLink] = useState("");
|
||||||
const channel = useAppSelector((store) => store.channels.byId[cid ?? 0]);
|
const channel = useAppSelector((store) => store.channels.byId[cid ?? 0]);
|
||||||
const { data: SMTPEnabled, isSuccess: smtpStatusFetchSuccess } = useGetSMTPStatusQuery();
|
const { data: SMTPEnabled, isSuccess: smtpStatusFetchSuccess } = useGetSMTPStatusQuery();
|
||||||
const [generateChannelInviteLink, { data: channelInviteLink, isLoading: generatingChannelLink }] =
|
const [generateInviteLink, { data: channelInviteLink, isLoading: generatingChannelLink }] =
|
||||||
useCreateChannelInviteLinkQuery();
|
useLazyCreateInviteLinkQuery();
|
||||||
const [generatePrivateInviteLink, { data: privateInviteLink, isLoading: generatingPrivateLink }] =
|
const [generatePrivateInviteLink, { data: privateInviteLink, isLoading: generatingPrivateLink }] =
|
||||||
useLazyCreatePrivateInviteLinkQuery();
|
useLazyCreatePrivateInviteLinkQuery();
|
||||||
|
|
||||||
@@ -23,9 +28,9 @@ export default function useInviteLink(cid?: number) {
|
|||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!cid || channel?.is_public) {
|
if (!cid || channel?.is_public) {
|
||||||
generateChannelInviteLink(cid);
|
generateInviteLink({ expire: defaultExpire, times: defaultTimes });
|
||||||
} else {
|
} else {
|
||||||
generatePrivateInviteLink(cid);
|
generatePrivateInviteLink({ cid, expire: defaultExpire, times: defaultTimes });
|
||||||
}
|
}
|
||||||
}, [cid, channel]);
|
}, [cid, channel]);
|
||||||
|
|
||||||
@@ -35,14 +40,19 @@ export default function useInviteLink(cid?: number) {
|
|||||||
setFinalLink(_link);
|
setFinalLink(_link);
|
||||||
}
|
}
|
||||||
}, [channelInviteLink, privateInviteLink, smtpStatusFetchSuccess]);
|
}, [channelInviteLink, privateInviteLink, smtpStatusFetchSuccess]);
|
||||||
const genServerLink = () => {
|
const generateNewLink = (params = defaultParams) => {
|
||||||
generateChannelInviteLink();
|
const { expire, times } = params;
|
||||||
|
if (!cid || channel?.is_public) {
|
||||||
|
generateInviteLink({ expire, times });
|
||||||
|
} else {
|
||||||
|
generatePrivateInviteLink({ cid, expire, times });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const generating = generatingPrivateLink || generatingChannelLink;
|
const generating = generatingPrivateLink || generatingChannelLink;
|
||||||
return {
|
return {
|
||||||
enableSMTP: SMTPEnabled,
|
enableSMTP: SMTPEnabled,
|
||||||
generating,
|
generating,
|
||||||
generateNewLink: cid ? generateChannelInviteLink.bind(null, cid) : genServerLink,
|
generateNewLink,
|
||||||
link: finalLink,
|
link: finalLink,
|
||||||
linkCopied: copied,
|
linkCopied: copied,
|
||||||
copyLink
|
copyLink
|
||||||
|
|||||||
Reference in New Issue
Block a user