Merge branch 'feat/liujie'

This commit is contained in:
2026-02-07 17:38:28 +08:00
10 changed files with 194 additions and 168 deletions

View File

@@ -34,7 +34,7 @@ function toast(msg) {
interface CommentInputProps { interface CommentInputProps {
onConfirm?: ( onConfirm?: (
value: { content: string } & Partial<CommentInputReplyParamsType> value: { content: string } & Partial<CommentInputReplyParamsType>,
) => void; ) => void;
} }
@@ -49,10 +49,8 @@ interface CommentInputReplyParamsType {
nickname: string; nickname: string;
} }
const CommentInput = forwardRef<CommentInputRef, CommentInputProps>(function ( const CommentInput = forwardRef<CommentInputRef, CommentInputProps>(
props, function (props, ref) {
ref
) {
const { onConfirm } = props; const { onConfirm } = props;
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [value, setValue] = useState(""); const [value, setValue] = useState("");
@@ -73,9 +71,8 @@ const CommentInput = forwardRef<CommentInputRef, CommentInputProps>(function (
initializeKeyboardListener(); initializeKeyboardListener();
// 添加本地监听器 // 添加本地监听器
const removeListener = addListener((height, visible) => { const removeListener = addListener(() => {
console.log("PublishBall 收到键盘变化:", height, visible); // 布局是否响应交由 shouldReactToKeyboard 决定
// 这里只记录或用于其他逻辑,布局是否响应交由 shouldReactToKeyboard 决定
}); });
return () => { return () => {
@@ -112,7 +109,6 @@ const CommentInput = forwardRef<CommentInputRef, CommentInputProps>(function (
setValue(""); setValue("");
inputDomRef.current && inputDomRef.current?.blur(); inputDomRef.current && inputDomRef.current?.blur();
} }
console.log(keyboardHeight, "keyboardHeight");
return ( return (
<CommonPopup <CommonPopup
visible={visible} visible={visible}
@@ -124,7 +120,9 @@ const CommentInput = forwardRef<CommentInputRef, CommentInputProps>(function (
// height: "60px!important", // height: "60px!important",
minHeight: "unset", minHeight: "unset",
bottom: bottom:
isKeyboardVisible && keyboardHeight > 0 ? `${keyboardHeight}px` : "0", isKeyboardVisible && keyboardHeight > 0
? `${keyboardHeight}px`
: "0",
}} }}
enableDragToClose={false} enableDragToClose={false}
> >
@@ -149,7 +147,7 @@ const CommentInput = forwardRef<CommentInputRef, CommentInputProps>(function (
<View <View
className={classnames( className={classnames(
styles.limit, styles.limit,
value.length > 200 ? styles.red : "" value.length > 200 ? styles.red : "",
)} )}
> >
<Text>{value.length}</Text>/<Text>200</Text> <Text>{value.length}</Text>/<Text>200</Text>
@@ -161,7 +159,8 @@ const CommentInput = forwardRef<CommentInputRef, CommentInputProps>(function (
</View> </View>
</CommonPopup> </CommonPopup>
); );
}); },
);
function isReplyComment(item: BaseComment<any>): item is ReplyComment { function isReplyComment(item: BaseComment<any>): item is ReplyComment {
return "reply_to_user" in item; return "reply_to_user" in item;
@@ -208,7 +207,7 @@ function CommentItem(props: {
className={classnames( className={classnames(
styles.commentItem, styles.commentItem,
blink_id === comment.id && styles.blink, blink_id === comment.id && styles.blink,
styles.weight_super styles.weight_super,
)} )}
key={comment.id} key={comment.id}
id={`comment_id_${comment.id}`} id={`comment_id_${comment.id}`}
@@ -293,7 +292,8 @@ function CommentItem(props: {
/> />
))} ))}
{!isReplyComment(comment) && {!isReplyComment(comment) &&
comment.replies.length !== comment.reply_count && ( comment.replies.length !== comment.reply_count &&
comment.replies.length > 3 && (
<View <View
className={styles.viewMore} className={styles.viewMore}
onClick={() => handleLoadMore(comment)} onClick={() => handleLoadMore(comment)}
@@ -313,7 +313,7 @@ export default forwardRef(function Comments(
message_id?: number; message_id?: number;
onScrollTo: (id: string) => void; onScrollTo: (id: string) => void;
}, },
ref ref,
) { ) {
const { game_id, publisher_id, message_id, onScrollTo } = props; const { game_id, publisher_id, message_id, onScrollTo } = props;
const [comments, setComments] = useState<Comment[]>([]); const [comments, setComments] = useState<Comment[]>([]);
@@ -371,7 +371,7 @@ export default forwardRef(function Comments(
replies: [res.data, ...item.replies].sort((a, b) => replies: [res.data, ...item.replies].sort((a, b) =>
dayjs(a.create_time).isAfter(dayjs(b.create_time)) dayjs(a.create_time).isAfter(dayjs(b.create_time))
? 1 ? 1
: -1 : -1,
), ),
}; };
}); });
@@ -435,7 +435,7 @@ export default forwardRef(function Comments(
item.replies.splice( item.replies.splice(
page === 1 ? 0 : page * PAGESIZE - 1, page === 1 ? 0 : page * PAGESIZE - 1,
newReplies.length, newReplies.length,
...newReplies ...newReplies,
); );
item.reply_count = res.data.count; item.reply_count = res.data.count;
} }
@@ -502,7 +502,7 @@ export default forwardRef(function Comments(
return { return {
...item, ...item,
replies: item.replies.filter( replies: item.replies.filter(
(replyItem) => replyItem.id !== id (replyItem) => replyItem.id !== id,
), ),
reply_count: item.reply_count - 1, reply_count: item.reply_count - 1,
}; };

View File

@@ -105,10 +105,10 @@ const NTRPEvaluatePopup = (props: NTRPEvaluatePopupProps, ref) => {
if (match) { if (match) {
setNtrp(match[0]); setNtrp(match[0]);
} else { } else {
setNtrp(""); setNtrp("1.5");
} }
} else { } else {
setNtrp(""); setNtrp("1.5");
} }
} }
}, [visible, userInfo?.ntrp_level]); }, [visible, userInfo?.ntrp_level]);

View File

@@ -87,7 +87,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
gameType: play_type, gameType: play_type,
skillLevel: `NTRP ${genNTRPRequirementText( skillLevel: `NTRP ${genNTRPRequirementText(
skill_level_min, skill_level_min,
skill_level_max skill_level_max,
)}`, )}`,
gameDate: `${startTime.format("M月D日")} (${dayofWeek})`, gameDate: `${startTime.format("M月D日")} (${dayofWeek})`,
gameTime: `${startTime.format("ah")}${gameLength}`, gameTime: `${startTime.format("ah")}${gameLength}`,
@@ -133,9 +133,10 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
page: "game_pages/detail/index", page: "game_pages/detail/index",
scene: `id=${id}`, scene: `id=${id}`,
}); });
const qrCodeUrl = await base64ToTempFilePath( // const qrCodeUrl = await base64ToTempFilePath(
qrCodeUrlRes.data.qr_code_base64 // qrCodeUrlRes.data.qr_code_base64
); // );
const qrCodeUrl = qrCodeUrlRes.data.ossPath;
await delay(100); await delay(100);
// Taro.showLoading({ title: "生成中..." }); // Taro.showLoading({ title: "生成中..." });
const url = await generatePosterImage({ const url = await generatePosterImage({
@@ -165,6 +166,18 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
setVisible(false); setVisible(false);
} }
async function handleCopyLink() {
const linkUrlRes = await DetailService.getLinkUrl({
path: "game_pages/detail/index",
query: `id=${id}`,
});
await Taro.setClipboardData({
data: linkUrlRes.data.url_link,
});
Taro.showToast({ title: "链接已复制到剪贴板", icon: "success" });
setVisible(false);
}
function onClose() { function onClose() {
setVisible(false); setVisible(false);
setPublishFlag(false); setPublishFlag(false);
@@ -201,7 +214,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
catchMove catchMove
className={classnames( className={classnames(
styles.title, styles.title,
publishFlag ? styles.publishTitle : "" publishFlag ? styles.publishTitle : "",
)} )}
> >
{publishFlag ? ( {publishFlag ? (
@@ -255,7 +268,7 @@ export default forwardRef(({ id, from, detail, userInfo }, ref) => {
</View> </View>
</View> </View>
<View className={styles.customBtnWrapper}> <View className={styles.customBtnWrapper}>
<Button className={styles.button}> <Button className={styles.button} onClick={handleCopyLink}>
<View className={styles.icon}> <View className={styles.icon}>
<Image className={styles.linkIcon} src={LinkIcon} /> <Image className={styles.linkIcon} src={LinkIcon} />
</View> </View>

View File

@@ -81,9 +81,9 @@ function Index() {
// 位置更新后,重新获取详情页数据(因为距离等信息可能发生变化) // 位置更新后,重新获取详情页数据(因为距离等信息可能发生变化)
// 注意:这里不调用 fetchDetail避免与 useDidShow 中的调用重复 // 注意:这里不调用 fetchDetail避免与 useDidShow 中的调用重复
// 如果需要更新距离信息,可以在 fetchDetail 成功后根据当前位置重新计算 // 如果需要更新距离信息,可以在 fetchDetail 成功后根据当前位置重新计算
if (from === "publish") { // if (from === "publish") {
handleShare(true); // handleShare(true);
} // }
} catch (error) { } catch (error) {
console.error("用户位置更新失败", error); console.error("用户位置更新失败", error);
} }
@@ -105,6 +105,10 @@ function Index() {
fetchUserInfoById(res.data.publisher_id); fetchUserInfoById(res.data.publisher_id);
} }
if (from === "publish") {
handleShare(true);
}
// Taro.hideLoading(); // Taro.hideLoading();
}; };
@@ -161,7 +165,7 @@ function Index() {
navto( navto(
userId === myInfo.id userId === myInfo.id
? "/user_pages/myself/index" ? "/user_pages/myself/index"
: `/user_pages/other/index?userid=${userId}` : `/user_pages/other/index?userid=${userId}`,
); );
} }
@@ -195,7 +199,7 @@ function Index() {
<View <View
className={classnames( className={classnames(
styles["custom-navbar"], styles["custom-navbar"],
glass ? styles.glass : "" glass ? styles.glass : "",
)} )}
style={{ style={{
height: `${totalHeight}px`, height: `${totalHeight}px`,

View File

@@ -5,7 +5,7 @@ import Taro, { useRouter } from "@tarojs/taro";
import classnames from "classnames"; import classnames from "classnames";
import dayjs from "dayjs"; import dayjs from "dayjs";
import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-cn";
import { generatePosterImage, base64ToTempFilePath, delay } from "@/utils"; import { generatePosterImage, delay } from "@/utils";
import { withAuth } from "@/components"; import { withAuth } from "@/components";
import GeneralNavbar from "@/components/GeneralNavbar"; import GeneralNavbar from "@/components/GeneralNavbar";
import DetailService from "@/services/detailService"; import DetailService from "@/services/detailService";
@@ -59,10 +59,11 @@ function SharePoster(props) {
page: "game_pages/detail/index", page: "game_pages/detail/index",
scene: `id=${id}`, scene: `id=${id}`,
}); });
const qrCodeUrl = await base64ToTempFilePath( const qrCodeUrl = qrCodeUrlRes.data.ossPath;
qrCodeUrlRes.data.qr_code_base64 // const qrCodeUrl = await base64ToTempFilePath(
); // qrCodeUrlRes.data.qr_code_base64
debugger // );
// debugger
await delay(100); await delay(100);
const url = await generatePosterImage({ const url = await generatePosterImage({
playType: play_type, playType: play_type,

View File

@@ -18,7 +18,6 @@ import { formatNtrpDisplay } from "@/utils/helper";
import { waitForAuthInit } from "@/utils/authInit"; import { waitForAuthInit } from "@/utils/authInit";
import httpService from "@/services/httpService"; import httpService from "@/services/httpService";
import DetailService from "@/services/detailService"; import DetailService from "@/services/detailService";
import { base64ToTempFilePath } from "@/utils/genPoster";
import { OSS_BASE_URL } from "@/config/api"; import { OSS_BASE_URL } from "@/config/api";
import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg"; import CloseIcon from "@/static/ntrp/ntrp_close_icon.svg";
import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg"; import DocCopy from "@/static/ntrp/ntrp_doc_copy.svg";
@@ -38,7 +37,7 @@ const sourceTypeToTextMap = new Map([
function adjustRadarLabels( function adjustRadarLabels(
source: [string, number][], source: [string, number][],
topK: number = 4 // 默认挑前4个最长的标签保护 topK: number = 4, // 默认挑前4个最长的标签保护
): [string, number][] { ): [string, number][] {
if (source.length === 0) return source; if (source.length === 0) return source;
@@ -216,7 +215,8 @@ function Intro() {
}); });
} }
Taro.redirectTo({ Taro.redirectTo({
url: `/other_pages/ntrp-evaluate/index?stage=${type}${type === StageType.RESULT ? `&id=${id}` : "" url: `/other_pages/ntrp-evaluate/index?stage=${type}${
type === StageType.RESULT ? `&id=${id}` : ""
}`, }`,
}); });
} }
@@ -380,7 +380,7 @@ function Test() {
prev.map((item, pIndex) => ({ prev.map((item, pIndex) => ({
...item, ...item,
...(pIndex === index ? { choosen: i } : {}), ...(pIndex === index ? { choosen: i } : {}),
})) })),
); );
} }
@@ -522,13 +522,14 @@ function Result() {
page: "other_pages/ntrp-evaluate/index", page: "other_pages/ntrp-evaluate/index",
scene: `stage=${StageType.INTRO}`, scene: `stage=${StageType.INTRO}`,
}); });
if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) { setQrCodeUrl(qrCodeUrlRes.data.ossPath);
// 将 base64 转换为临时文件路径 // if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) {
const tempFilePath = await base64ToTempFilePath( // // 将 base64 转换为临时文件路径
qrCodeUrlRes.data.qr_code_base64 // const tempFilePath = await base64ToTempFilePath(
); // qrCodeUrlRes.data.qr_code_base64
setQrCodeUrl(tempFilePath); // );
} // setQrCodeUrl(tempFilePath);
// }
} catch (error) { } catch (error) {
console.error("获取二维码失败:", error); console.error("获取二维码失败:", error);
} }
@@ -542,13 +543,17 @@ function Result() {
const sortOrder = res.data.sort || []; const sortOrder = res.data.sort || [];
const abilities = res.data.radar_data.abilities; const abilities = res.data.radar_data.abilities;
const sortedKeys = sortOrder.filter((k) => k in abilities); const sortedKeys = sortOrder.filter((k) => k in abilities);
const remainingKeys = Object.keys(abilities).filter((k) => !sortOrder.includes(k)); const remainingKeys = Object.keys(abilities).filter(
(k) => !sortOrder.includes(k),
);
const allKeys = [...sortedKeys, ...remainingKeys]; const allKeys = [...sortedKeys, ...remainingKeys];
let radarData: [string, number][] = allKeys.map((key) => [ let radarData: [string, number][] = allKeys.map((key) => [
key, key,
Math.min( Math.min(
100, 100,
Math.floor((abilities[key].current_score / abilities[key].max_score) * 100) Math.floor(
(abilities[key].current_score / abilities[key].max_score) * 100,
),
), ),
]); ]);
// 直接使用接口 sort 顺序,不经过 adjustRadarLabels 重新排序 // 直接使用接口 sort 顺序,不经过 adjustRadarLabels 重新排序
@@ -590,7 +595,7 @@ function Result() {
if (!userInfo?.phone) { if (!userInfo?.phone) {
Taro.redirectTo({ Taro.redirectTo({
url: `/login_pages/index/index?redirect=${encodeURIComponent( url: `/login_pages/index/index?redirect=${encodeURIComponent(
`/main_pages/index` `/main_pages/index`,
)}`, )}`,
}); });
clear(); clear();
@@ -615,11 +620,12 @@ function Result() {
page: "other_pages/ntrp-evaluate/index", page: "other_pages/ntrp-evaluate/index",
scene: `stage=${StageType.INTRO}`, scene: `stage=${StageType.INTRO}`,
}); });
if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) { finalQrCodeUrl = qrCodeUrlRes.data.ossPath;
finalQrCodeUrl = await base64ToTempFilePath( // if (qrCodeUrlRes.code === 0 && qrCodeUrlRes.data?.qr_code_base64) {
qrCodeUrlRes.data.qr_code_base64 // finalQrCodeUrl = await base64ToTempFilePath(
); // qrCodeUrlRes.data.qr_code_base64
} // );
// }
} }
// 使用 RadarV2 的 generateFullImage 方法生成完整图片 // 使用 RadarV2 的 generateFullImage 方法生成完整图片
@@ -700,12 +706,13 @@ function Result() {
} }
const currentPage = getCurrentFullPath(); const currentPage = getCurrentFullPath();
Taro.redirectTo({ Taro.redirectTo({
url: `/login_pages/index/index${currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : "" url: `/login_pages/index/index${
currentPage ? `?redirect=${encodeURIComponent(currentPage)}` : ""
}`, }`,
}); });
} }
function handleGo() { } function handleGo() {}
return ( return (
<View className={styles.resultContainer}> <View className={styles.resultContainer}>
@@ -763,7 +770,8 @@ function Result() {
{userInfo?.phone ? ( {userInfo?.phone ? (
<View className={styles.updateTip}> <View className={styles.updateTip}>
<Text> <Text>
NTRP {formatNtrpDisplay(result?.ntrp_level || "")}{" "} NTRP {" "}
{formatNtrpDisplay(result?.ntrp_level || "")}{" "}
</Text> </Text>
<Text className={styles.grayTip}>()</Text> <Text className={styles.grayTip}>()</Text>
</View> </View>

View File

@@ -78,9 +78,8 @@ const PublishBall: React.FC = () => {
} = useKeyboardHeight(); } = useKeyboardHeight();
// 获取页面参数并设置导航标题 // 获取页面参数并设置导航标题
const [optionsConfig, setOptionsConfig] = useState<FormFieldConfig[]>( const [optionsConfig, setOptionsConfig] = useState<FormFieldConfig[]>(
publishBallFormSchema publishBallFormSchema,
); );
console.log(userInfo, "userInfo");
const [formData, setFormData] = useState<PublishBallFormData[]>([ const [formData, setFormData] = useState<PublishBallFormData[]>([
defaultFormData, defaultFormData,
]); ]);
@@ -103,13 +102,11 @@ const PublishBall: React.FC = () => {
const updateFormData = ( const updateFormData = (
key: keyof PublishBallFormData, key: keyof PublishBallFormData,
value: any, value: any,
index: number index: number,
) => { ) => {
console.log(key, value, index, "key, value, index");
setFormData((prev) => { setFormData((prev) => {
const newData = [...prev]; const newData = [...prev];
newData[index] = { ...newData[index], [key]: value }; newData[index] = { ...newData[index], [key]: value };
console.log(newData, "newData");
return newData; return newData;
}); });
}; };
@@ -186,7 +183,7 @@ const PublishBall: React.FC = () => {
const confirmDelete = () => { const confirmDelete = () => {
if (deleteConfirm.index >= 0) { if (deleteConfirm.index >= 0) {
setFormData((prev) => setFormData((prev) =>
prev.filter((_, index) => index !== deleteConfirm.index) prev.filter((_, index) => index !== deleteConfirm.index),
); );
closeDeleteConfirm(); closeDeleteConfirm();
Taro.showToast({ Taro.showToast({
@@ -198,7 +195,7 @@ const PublishBall: React.FC = () => {
const validateFormData = ( const validateFormData = (
formData: PublishBallFormData, formData: PublishBallFormData,
isOnSubmit: boolean = false isOnSubmit: boolean = false,
) => { ) => {
const { const {
activityInfo, activityInfo,
@@ -207,7 +204,7 @@ const PublishBall: React.FC = () => {
image_list, image_list,
players, players,
current_players, current_players,
descriptionInfo descriptionInfo,
} = formData; } = formData;
const { play_type, price, location_name } = activityInfo; const { play_type, price, location_name } = activityInfo;
const { description } = descriptionInfo; const { description } = descriptionInfo;
@@ -225,7 +222,7 @@ const PublishBall: React.FC = () => {
// 判断图片是否上传完成 // 判断图片是否上传完成
if (image_list?.length > 0) { if (image_list?.length > 0) {
const uploadInProgress = image_list.some((item) => const uploadInProgress = image_list.some((item) =>
item.url.startsWith("http://tmp/") item?.url?.startsWith?.("http://tmp/"),
); );
if (uploadInProgress) { if (uploadInProgress) {
Taro.showToast({ Taro.showToast({
@@ -368,7 +365,6 @@ const PublishBall: React.FC = () => {
// 提交表单 // 提交表单
const handleSubmit = async () => { const handleSubmit = async () => {
// 基础验证 // 基础验证
console.log(formData, "formData");
const params = getParams(); const params = getParams();
const { republish } = params || {}; const { republish } = params || {};
if (activityType === "individual") { if (activityType === "individual") {
@@ -516,7 +512,7 @@ const PublishBall: React.FC = () => {
const mergeWithDefault = ( const mergeWithDefault = (
data: any, data: any,
isDetail: boolean = false isDetail: boolean = false,
): PublishBallFormData => { ): PublishBallFormData => {
// ai导入与详情数据处理 // ai导入与详情数据处理
const { const {
@@ -741,7 +737,6 @@ const PublishBall: React.FC = () => {
} else { } else {
setIsSubmitDisabled(false); setIsSubmitDisabled(false);
} }
console.log(formData, "formData");
}, [formData]); }, [formData]);
useEffect(() => { useEffect(() => {
@@ -754,9 +749,8 @@ const PublishBall: React.FC = () => {
initializeKeyboardListener(); initializeKeyboardListener();
// 添加本地监听器 // 添加本地监听器
const removeListener = addListener((height, visible) => { const removeListener = addListener(() => {
console.log("PublishBall 收到键盘变化:", height, visible); // 布局是否响应交由 shouldReactToKeyboard 决定
// 这里只记录或用于其他逻辑,布局是否响应交由 shouldReactToKeyboard 决定
}); });
return () => { return () => {

View File

@@ -158,6 +158,7 @@ class GameDetailService {
async getQrCodeUrl(req: { page: string, scene: string }): Promise<ApiResponse<{ async getQrCodeUrl(req: { page: string, scene: string }): Promise<ApiResponse<{
qr_code_base64: string, qr_code_base64: string,
image_size: number, image_size: number,
ossPath: string,
page: string, page: string,
scene: string, scene: string,
width: number width: number
@@ -166,6 +167,10 @@ class GameDetailService {
showLoading: true showLoading: true
}) })
} }
async getLinkUrl(req: { path: string, query: string }): Promise<ApiResponse<{ url_link: string, path: string, query: string }>> {
return httpService.post('/user/generate_url_link', req, { showLoading: true })
}
} }
// 导出认证服务实例 // 导出认证服务实例

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -11,7 +11,8 @@ const mapIcon = `${OSS_BASE_URL}/images/06b994fa-9227-4708-8555-8a07af8d0c3b.jpg
// const logo = `${OSS_BASE_URL}/images/fb732da6-11b9-4022-a524-a377b17635eb.jpg` // const logo = `${OSS_BASE_URL}/images/fb732da6-11b9-4022-a524-a377b17635eb.jpg`
const logoText = `${OSS_BASE_URL}/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png`; // const logoText = `${OSS_BASE_URL}/images/9d8cbc9d-9601-4e2d-ab23-76420a4537d6.png`;
const logoText = `${OSS_BASE_URL}/system/youchang_tip_text.png`
export function base64ToTempFilePath(base64Data: string): Promise<string> { export function base64ToTempFilePath(base64Data: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {