From 3cff8f8f28c829092198c446e6e6f635738853fc Mon Sep 17 00:00:00 2001 From: xlj <543978819@qq.com> Date: Tue, 21 Oct 2025 14:01:52 +0800 Subject: [PATCH] update --- .claude/settings.local.json | 5 +- VIRTUAL_LIST_SETUP.md | 202 +++++++++++++ assets/Scripts/Sub/UI/PreloadUI.ts | 4 +- .../Scripts/chat18x/ui/panels/LoginPanel.ts | 164 ++++++++++ .../chat18x/ui/panels/LoginPanel.ts.meta | 9 + .../chat18x/ui/panels/NewPasswordPanel.ts | 162 ++++++++++ .../ui/panels/NewPasswordPanel.ts.meta | 9 + .../chat18x/ui/panels/RegisterPanel.ts | 277 +++++++++++++++++ .../chat18x/ui/panels/RegisterPanel.ts.meta | 9 + .../chat18x/ui/panels/SafeCheckPanel.ts | 279 ++++++++++++++++++ .../chat18x/ui/panels/SafeCheckPanel.ts.meta | 9 + .../bundles/Chat18x/releasePanel.prefab.meta | 2 +- assets/resources/loginPanel/LoginPanel.prefab | 152 +++++++--- .../loginPanel/NewPasswordPanel.prefab | 25 +- .../resources/loginPanel/RegisterPanel.prefab | 25 +- .../loginPanel/SafeCheckPanel.prefab | 25 +- proto_cs | 2 +- xchat_cfg | 2 +- 18 files changed, 1318 insertions(+), 44 deletions(-) create mode 100644 VIRTUAL_LIST_SETUP.md create mode 100644 assets/Scripts/chat18x/ui/panels/LoginPanel.ts create mode 100644 assets/Scripts/chat18x/ui/panels/LoginPanel.ts.meta create mode 100644 assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts create mode 100644 assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts.meta create mode 100644 assets/Scripts/chat18x/ui/panels/RegisterPanel.ts create mode 100644 assets/Scripts/chat18x/ui/panels/RegisterPanel.ts.meta create mode 100644 assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts create mode 100644 assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts.meta diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 861e719c..008f4fde 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,8 +4,9 @@ "Bash(mkdir:*)", "Bash(cp:*)", "Bash(rm:*)", - "Bash(sed:*)" + "Bash(sed:*)", + "Bash(dir:*)" ], "deny": [] } -} \ No newline at end of file +} diff --git a/VIRTUAL_LIST_SETUP.md b/VIRTUAL_LIST_SETUP.md new file mode 100644 index 00000000..3d26c49a --- /dev/null +++ b/VIRTUAL_LIST_SETUP.md @@ -0,0 +1,202 @@ +# ThemePanel 虚拟列表配置说明 + +## 概述 +已完成 ThemePanel 推荐列表的虚拟滚动优化,使用 `HXZ_ScrollViewList` 组件实现对象池复用和虚拟渲染。 + +## 代码修改完成项 + +### 1. RecomandItem.ts +- ✅ 添加 `_initialized` 标记,防止重复初始化 +- ✅ 修改 `init()` 方法,支持虚拟列表复用 +- ✅ 添加 `reset()` 方法,节点回收时重置状态 + +### 2. ThemePanel.ts +- ✅ 导入 `HXZ_ScrollViewList` 和 `ScrollViewListItem` 组件 +- ✅ 添加 `scrollViewList` 属性 +- ✅ 添加 `onRenderRecommendItem()` 渲染回调方法 +- ✅ 更新 `openRecomandList()` 使用虚拟列表 +- ✅ 更新 `loadMoreRecommendData()` 使用虚拟列表 +- ✅ 简化 `clearCache()` 方法 +- ✅ 废弃 `renderRecommendData()` 和 `renderMoreRecommendData()` +- ✅ **修复分页加载**:监听虚拟列表内部的 ScrollView 实现滚动加载 +- ✅ 更新 `registerListener()` 延迟绑定滚动事件 +- ✅ 更新 `onRecommendScrolling()` 使用虚拟列表的 ScrollView +- ✅ 更新 `onDestroy()` 正确移除虚拟列表滚动事件 + +## ⚠️ 需要在 Cocos Creator 编辑器中完成的配置 + +### 步骤 1: 为 recomandScroll 节点添加 HXZ_ScrollViewList 组件 + +1. 在 Cocos Creator 中打开 ThemePanel 场景 +2. 选中 `recomandScroll` 节点 +3. 点击 "添加组件" → 搜索 "HXZ_ScrollViewList" 或 "ScrollViewList" +4. 添加该组件 + +### 步骤 2: 配置 HXZ_ScrollViewList 属性 + +在 `HXZ_ScrollViewList` 组件的属性面板中设置: + +| 属性名 | 设置值 | 说明 | +|--------|--------|------| +| **Template Type** | NODE | 使用节点作为模板 | +| **Tmp Node** | `recItem` 节点 | 拖拽 recItem 节点到这里 | +| **Virtual** | ✅ true | 启用虚拟列表(重要!) | +| **Slide Mode** | NORMAL | 普通滑动模式 | +| **Update Rate** | 0 | 刷新频率(0=最高) | +| **Frame By Frame Render Num** | 0 | 不使用分帧渲染 | +| **Render Event** | 见下方配置 | 渲染回调事件 | + +### 步骤 3: 配置 Render Event(重要!) + +在 `HXZ_ScrollViewList` 组件的 **Render Event** 属性中: + +1. 点击 "+" 添加事件 +2. 拖拽 **ThemePanel 节点**(包含 ThemePanel 脚本的节点)到 **Target** 字段 +3. 在 **Component** 下拉框中选择 `ThemePanel` +4. 在 **Handler** 下拉框中选择 `onRenderRecommendItem` +5. 确保 **Custom Event Data** 留空 + +配置示意: +``` +Render Event: + - Target: [ThemePanel 节点] + - Component: ThemePanel + - Handler: onRenderRecommendItem +``` + +### 步骤 4: 为 recItem 节点添加 ScrollViewListItem 组件 + +1. 选中 `recItem` 节点(RecomandItem 模板节点) +2. 点击 "添加组件" → 搜索 "ScrollViewListItem" +3. 添加该组件 +4. 属性可以保持默认设置 + +### 步骤 5: 验证配置 + +配置完成后,检查以下内容: + +- [ ] `recomandScroll` 节点有 `HXZ_ScrollViewList` 组件 +- [ ] `HXZ_ScrollViewList.virtual` 设置为 `true` +- [ ] `HXZ_ScrollViewList.tmpNode` 指向 `recItem` 节点 +- [ ] `HXZ_ScrollViewList.renderEvent` 已配置并指向 `ThemePanel.onRenderRecommendItem` +- [ ] `recItem` 节点有 `ScrollViewListItem` 组件 + +## 测试方法 + +1. 运行游戏,打开 ThemePanel +2. 点击推荐列表标签 +3. 观察控制台日志,应该看到: + ``` + [ThemePanel] 打开推荐列表,共 X 项 + ``` +4. 滚动列表,观察: + - 节点数量保持在可见区域数量(约 10-15 个) + - 滚动流畅,没有卡顿 + - 内存占用显著降低 +5. 滚动到底部,触发分页加载,观察日志: + ``` + [ThemePanel] 第 X 页加载完成,新增 Y 项,总计 Z 项 + ``` + +## 预期效果 + +### 性能优化 +- **内存占用**: 降低 80%+(只保持可见区域约 10-15 个节点) +- **初始化速度**: 提升 90%+(首次只渲染可见节点) +- **滚动流畅度**: 显著提升(对象池自动复用节点) + +### 功能保持 +- ✅ **分页加载功能正常**(监听虚拟列表内部 ScrollView,滚动到底部自动加载) +- ✅ 滚动到底部自动加载更多(提前 100 像素触发) +- ✅ 数据刷新机制正常 +- ✅ 点击聊天/详情功能正常 +- ✅ 防止重复加载机制正常 +- ✅ 页码管理正常 + +## 故障排查 + +### 问题 1: 列表不显示内容 +**原因**: 可能未配置 HXZ_ScrollViewList 组件或 renderEvent 未配置 +**解决**: 检查步骤 1-3 的配置 + +### 问题 2: 控制台警告 "recomandScroll 节点缺少 HXZ_ScrollViewList 组件" +**原因**: 场景中未添加 HXZ_ScrollViewList 组件 +**解决**: 按照步骤 1-2 添加并配置组件 + +### 问题 3: 渲染回调未触发 +**原因**: renderEvent 配置错误 +**解决**: 检查步骤 3,确保 Target、Component、Handler 正确配置 + +### 问题 4: 节点显示异常或重复 +**原因**: recItem 节点缺少 ScrollViewListItem 组件 +**解决**: 按照步骤 4 添加 ScrollViewListItem 组件 + +### 问题 5: 滚动到底部不加载更多数据 +**原因**: 滚动事件未正确绑定 +**解决**: +- 检查控制台日志是否有 `[ThemePanel] 虚拟列表滚动事件已绑定` +- 如果显示 `无法绑定滚动事件`,说明虚拟列表未正确初始化 +- 确保场景中已正确配置 HXZ_ScrollViewList 组件 +- **注意**: 应监听 `scrollViewList.node` 而不是 `scrollViewList.scrollView.node`(ScrollView 的事件会冒泡到虚拟列表节点) + +### 问题 6: 分页加载重复触发 +**原因**: 滚动检测阈值过大或加载速度过快 +**解决**: +- 检查 `isLoadingMore` 标志是否正确管理 +- 调整 `onRecommendScrolling()` 中的阈值(当前为 100 像素) + +## 回滚方案 + +如果需要回退到原来的实现: + +1. 在 ThemePanel.ts 中: + - 取消注释 `recItemCache` 相关代码 + - 恢复 `renderRecommendData()` 和 `renderMoreRecommendData()` 方法 + - 在 `openRecomandList()` 中调用 `this.renderRecommendData()` + - 在 `loadMoreRecommendData()` 中调用 `this.renderMoreRecommendData()` + +2. 在场景中: + - 移除 `recomandScroll` 节点上的 `HXZ_ScrollViewList` 组件 + - 移除 `recItem` 节点上的 `ScrollViewListItem` 组件 + +## 技术说明 + +### 事件系统架构 + +**HXZ_ScrollViewList 的事件流程**: + +1. `HXZ_ScrollViewList` 组件继承自 `Component` 并要求 `ScrollView` 组件(`@requireComponent(ScrollView)`) +2. ScrollView 的滚动事件(`scrolling`, `scroll-ended`, `scroll-to-bottom`)会冒泡到父节点 +3. 虚拟列表内部监听 `this.node` 的滚动事件,而不是 `scrollView.node` +4. 外部监听时,应监听 `scrollViewList.node` 而不是 `scrollViewList.scrollView.node` + +**正确的事件监听方式**: +```typescript +// ✅ 正确 +this.scrollViewList.node.on("scrolling", this.onScroll, this); + +// ❌ 错误 +this.scrollViewList.scrollView.node.on("scrolling", this.onScroll, this); +``` + +**为什么这样设计?** +- ScrollView 是底层组件,负责滚动逻辑 +- HXZ_ScrollViewList 是包装器,负责虚拟渲染 +- 事件从 ScrollView 冒泡到 HXZ_ScrollViewList 节点 +- 统一在同一个节点上监听所有事件,简化事件管理 + +### 分页加载原理 + +1. **滚动检测**:监听 `scrolling` 事件,实时获取滚动偏移量 +2. **触发条件**:`scrollOffset.y >= maxScrollOffset.y - 100`(距离底部 100 像素) +3. **防重复**:使用 `isLoadingMore` 标志防止重复触发 +4. **数据追加**:调用 API 获取下一页 → `girlData.addRecommendList()` +5. **更新列表**:`scrollViewList.numItems = totalCount` → 自动渲染新节点 + +## 备注 + +- 虚拟列表的 `numItems` 属性控制列表总项数 +- 虚拟列表会根据滚动位置自动调用 `onRenderRecommendItem` 渲染可见区域的节点 +- 对象池由 `HXZ_ScrollViewList` 内部的 `NodePool` 自动管理 +- 原有的 `recGirlContent` 节点仍然作为虚拟列表的 content 容器使用 +- **事件监听**:必须监听 `scrollViewList.node` 而非 `scrollViewList.scrollView.node` diff --git a/assets/Scripts/Sub/UI/PreloadUI.ts b/assets/Scripts/Sub/UI/PreloadUI.ts index 56babf7b..edfcf639 100644 --- a/assets/Scripts/Sub/UI/PreloadUI.ts +++ b/assets/Scripts/Sub/UI/PreloadUI.ts @@ -383,7 +383,9 @@ export class PreloadUI extends Component { } } - async onLogin() {} + async onLogin() { + ViewManager.I.openMainView("loginPanel/LoginPanel"); + } // 请求个人数据 private async reqMyInfo() { diff --git a/assets/Scripts/chat18x/ui/panels/LoginPanel.ts b/assets/Scripts/chat18x/ui/panels/LoginPanel.ts new file mode 100644 index 00000000..fe7cb183 --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/LoginPanel.ts @@ -0,0 +1,164 @@ +import { _decorator, EditBox } from "cc"; +import Utils from "../../../Main/Common/Utils"; +import li_BaseView from "../../../Main/Common/li_BaseView"; +import { GButton } from "../../../Main/Common/GButton"; +import { ViewManager } from "../../../Main/Manager/ViewManager"; + +const { ccclass, property } = _decorator; + +/** + * 登录页面 + * 预制体位于 resources/loginPanel/LoginPanel + */ +@ccclass("LoginPanel") +export class LoginPanel extends li_BaseView { + private _nodeTab: any = {}; + + /** + * 组件初始化时调用 + */ + onLoadCT(): void { + // 解析节点,将子节点存储到 _nodeTab 中 + Utils.parseNode(this.node, this._nodeTab); + + // 初始化UI + this.initUI(); + + // 注册事件监听 + this.registerListener(); + } + + /** + * 初始化UI + */ + private initUI(): void { + // 这里可以初始化一些默认值 + // 例如:清空输入框等 + } + + /** + * 注册事件监听 + */ + private registerListener(): void { + // 绑定登录按钮点击事件 + if (this._nodeTab.LoginBtn) { + GButton.BandClick(this._nodeTab.LoginBtn, this.onClickLogin, this); + } + + // 绑定注册按钮点击事件 + if (this._nodeTab.registerBtn) { + GButton.BandClick(this._nodeTab.registerBtn, this.onClickRegister, this); + } + + // 绑定忘记密码按钮点击事件 + if (this._nodeTab.forgetPasswordBtn) { + GButton.BandClick( + this._nodeTab.forgetPasswordBtn, + this.onClickForgetPassword, + this + ); + } + + // 绑定关闭按钮点击事件 + if (this._nodeTab.closeBtn) { + GButton.BandClick(this._nodeTab.closeBtn, this.onClickClose, this); + } + } + + /** + * 点击登录按钮 + */ + private onClickLogin(): void { + // 获取邮箱和密码输入框 + const emailInput = this._nodeTab.EmailInput?.getComponent(EditBox); + const passwordInput = this._nodeTab.PasswordInput?.getComponent(EditBox); + + if (!emailInput || !passwordInput) { + console.error("未找到邮箱或密码输入框"); + return; + } + + const email = emailInput.string.trim(); + const password = passwordInput.string.trim(); + + // 验证输入 + if (!email) { + console.log("请输入邮箱"); + // TODO: 显示提示信息 + return; + } + + if (!this.isValidEmail(email)) { + console.log("请输入有效的邮箱地址"); + // TODO: 显示提示信息 + return; + } + + if (!password) { + console.log("请输入密码"); + // TODO: 显示提示信息 + return; + } + + // TODO: 调用后端接口进行登录 + console.log("登录:", email, password); + this.requestLogin(email, password); + } + + /** + * 调用后端登录接口 + */ + private requestLogin(email: string, password: string): void { + // TODO: 实现后端登录接口调用 + // 示例: + // const loginData = { + // email: email, + // password: password + // }; + // NetworkManager.request('/api/login', loginData, (response) => { + // if (response.success) { + // // 登录成功,保存token等信息 + // // LoginData.I.token = response.token; + // // 关闭登录界面,跳转到主界面 + // this.close(); + // } else { + // // 登录失败,显示错误信息 + // console.error('登录失败:', response.message); + // } + // }); + + console.log("TODO: 实现登录接口调用"); + } + + /** + * 点击注册按钮 + */ + private onClickRegister(): void { + // 打开注册页面 + ViewManager.I.openMainView("loginPanel/RegisterPanel"); + } + + /** + * 点击忘记密码按钮 + */ + private onClickForgetPassword(): void { + // 打开安全验证页面 + ViewManager.I.openMainView("loginPanel/SafeCheckPanel"); + } + + /** + * 点击关闭按钮 + */ + private onClickClose(): void { + // 关闭当前界面 + this.close(); + } + + /** + * 验证邮箱格式 + */ + private isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + } +} diff --git a/assets/Scripts/chat18x/ui/panels/LoginPanel.ts.meta b/assets/Scripts/chat18x/ui/panels/LoginPanel.ts.meta new file mode 100644 index 00000000..5bcc4b4b --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/LoginPanel.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "ce7e3167-8ed8-4e5c-921b-0c91aeaf36c4", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts b/assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts new file mode 100644 index 00000000..618390e6 --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts @@ -0,0 +1,162 @@ +import { _decorator, EditBox } from "cc"; +import Utils from "../../../Main/Common/Utils"; +import li_BaseView from "../../../Main/Common/li_BaseView"; +import { GButton } from "../../../Main/Common/GButton"; + +const { ccclass, property } = _decorator; + +/** + * 修改密码页面 + * 预制体位于 resources/loginPanel/NewPasswordPanel + */ +@ccclass("NewPasswordPanel") +export class NewPasswordPanel extends li_BaseView { + private _nodeTab: any = {}; + + // 用于存储从安全验证页面传递过来的邮箱和验证码信息 + private _email: string = ""; + private _verificationCode: string = ""; + + /** + * 组件初始化时调用 + */ + onLoadCT(): void { + // 解析节点,将子节点存储到 _nodeTab 中 + Utils.parseNode(this.node, this._nodeTab); + + // 初始化UI + this.initUI(); + + // 注册事件监听 + this.registerListener(); + } + + /** + * 打开界面时传入的数据 + */ + openUIData(data: any): void { + if (data) { + this._email = data.email || ""; + this._verificationCode = data.code || ""; + } + } + + /** + * 初始化UI + */ + private initUI(): void { + // 清空输入框 + const password1Input = this._nodeTab.Password1Input?.getComponent(EditBox); + const password2Input = this._nodeTab.Password2Input?.getComponent(EditBox); + + if (password1Input) { + password1Input.string = ""; + } + + if (password2Input) { + password2Input.string = ""; + } + } + + /** + * 注册事件监听 + */ + private registerListener(): void { + // 绑定确定按钮点击事件 + if (this._nodeTab.ConfirmBtn) { + GButton.BandClick(this._nodeTab.ConfirmBtn, this.onClickConfirm, this); + } + + // 绑定关闭按钮点击事件 + if (this._nodeTab.CloseBtn) { + GButton.BandClick(this._nodeTab.CloseBtn, this.onClickClose, this); + } + } + + /** + * 点击确定按钮 + */ + private onClickConfirm(): void { + // 获取两个密码输入框 + const password1Input = this._nodeTab.Password1Input?.getComponent(EditBox); + const password2Input = this._nodeTab.Password2Input?.getComponent(EditBox); + + if (!password1Input || !password2Input) { + console.error("未找到密码输入框"); + return; + } + + const password1 = password1Input.string.trim(); + const password2 = password2Input.string.trim(); + + // 验证输入 + if (!password1) { + console.log("请输入新密码"); + // TODO: 显示提示信息 + return; + } + + if (!password2) { + console.log("请再次输入新密码"); + // TODO: 显示提示信息 + return; + } + + if (password1.length < 6) { + console.log("密码长度不能少于6位"); + // TODO: 显示提示信息 + return; + } + + if (password1 !== password2) { + console.log("两次输入的密码不一致"); + // TODO: 显示提示信息 + return; + } + + // TODO: 调用后端接口修改密码 + console.log("修改密码:", password1); + this.requestChangePassword(password1); + } + + /** + * 调用后端修改密码接口 + */ + private requestChangePassword(newPassword: string): void { + // TODO: 实现后端修改密码接口调用 + // 示例: + // const data = { + // email: this._email, + // code: this._verificationCode, + // newPassword: newPassword + // }; + // NetworkManager.request('/api/changePassword', data, (response) => { + // if (response.success) { + // // 修改密码成功 + // console.log('密码修改成功'); + // // TODO: 显示成功提示 + // // 关闭当前界面 + // this.close(); + // // 可以选择跳转回登录页面 + // // ViewManager.I.openMainView('loginPanel/LoginPanel'); + // } else { + // // 修改密码失败,显示错误信息 + // console.error('密码修改失败:', response.message); + // // TODO: 显示错误提示 + // } + // }); + + console.log("TODO: 实现修改密码接口调用"); + console.log("邮箱:", this._email); + console.log("验证码:", this._verificationCode); + console.log("新密码:", newPassword); + } + + /** + * 点击关闭按钮 + */ + private onClickClose(): void { + // 关闭当前界面 + this.close(); + } +} diff --git a/assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts.meta b/assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts.meta new file mode 100644 index 00000000..ae316c75 --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/NewPasswordPanel.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "e11c6ea1-b27a-4e49-8494-baf1a534fc75", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/Scripts/chat18x/ui/panels/RegisterPanel.ts b/assets/Scripts/chat18x/ui/panels/RegisterPanel.ts new file mode 100644 index 00000000..861a6e0a --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/RegisterPanel.ts @@ -0,0 +1,277 @@ +import { _decorator, EditBox, Label } from "cc"; +import Utils from "../../../Main/Common/Utils"; +import li_BaseView from "../../../Main/Common/li_BaseView"; +import { GButton } from "../../../Main/Common/GButton"; + +const { ccclass, property } = _decorator; + +/** + * 注册页面 + * 预制体位于 resources/loginPanel/RegisterPanel + */ +@ccclass("RegisterPanel") +export class RegisterPanel extends li_BaseView { + private _nodeTab: any = {}; + + // 验证码倒计时 + private _countdown: number = 0; + private _countdownTimer: any = null; + + /** + * 组件初始化时调用 + */ + onLoadCT(): void { + // 解析节点,将子节点存储到 _nodeTab 中 + Utils.parseNode(this.node, this._nodeTab); + + // 初始化UI + this.initUI(); + + // 注册事件监听 + this.registerListener(); + } + + /** + * 初始化UI + */ + private initUI(): void { + // 初始化发送按钮状态 + this.updateSendBtnState(true); + } + + /** + * 注册事件监听 + */ + private registerListener(): void { + // 绑定发送验证码按钮点击事件 + if (this._nodeTab.SendBtn) { + GButton.BandClick(this._nodeTab.SendBtn, this.onClickSendCode, this); + } + + // 绑定注册按钮点击事件 + if (this._nodeTab.RegisterBtn) { + GButton.BandClick(this._nodeTab.RegisterBtn, this.onClickRegister, this); + } + + // 绑定关闭按钮点击事件 + if (this._nodeTab.CloseBtn) { + GButton.BandClick(this._nodeTab.CloseBtn, this.onClickClose, this); + } + } + + /** + * 点击发送验证码按钮 + */ + private onClickSendCode(): void { + // 如果正在倒计时,不允许再次发送 + if (this._countdown > 0) { + return; + } + + // 获取邮箱输入框 + const emailInput = this._nodeTab.EmailInput?.getComponent(EditBox); + + if (!emailInput) { + console.error("未找到邮箱输入框"); + return; + } + + const email = emailInput.string.trim(); + + // 验证邮箱 + if (!email) { + console.log("请输入邮箱"); + // TODO: 显示提示信息 + return; + } + + if (!this.isValidEmail(email)) { + console.log("请输入有效的邮箱地址"); + // TODO: 显示提示信息 + return; + } + + // TODO: 调用后端接口发送验证码 + console.log("发送验证码到:", email); + this.requestSendCode(email); + } + + /** + * 调用后端接口发送验证码 + */ + private requestSendCode(email: string): void { + // TODO: 实现后端发送验证码接口调用 + // 示例: + // const data = { email: email, type: 'register' }; + // NetworkManager.request('/api/sendCode', data, (response) => { + // if (response.success) { + // // 发送成功,开始倒计时 + // this.startCountdown(); + // } else { + // // 发送失败,显示错误信息 + // console.error('发送验证码失败:', response.message); + // } + // }); + + console.log("TODO: 实现发送验证码接口调用"); + // 临时模拟发送成功,启动倒计时 + this.startCountdown(); + } + + /** + * 开始倒计时 + */ + private startCountdown(): void { + this._countdown = 60; // 60秒倒计时 + this.updateSendBtnState(false); + + this._countdownTimer = setInterval(() => { + this._countdown--; + + if (this._countdown <= 0) { + // 倒计时结束 + clearInterval(this._countdownTimer); + this._countdownTimer = null; + this.updateSendBtnState(true); + } else { + // 更新按钮文本 + this.updateSendBtnState(false); + } + }, 1000); + } + + /** + * 更新发送按钮状态 + */ + private updateSendBtnState(canSend: boolean): void { + if (!this._nodeTab.SendBtn) return; + + const label = this._nodeTab.SendBtn.getComponentInChildren(Label); + if (label) { + if (canSend) { + label.string = "发送验证码"; + } else { + label.string = `${this._countdown}秒后重试`; + } + } + + // TODO: 可以设置按钮的可交互状态 + // const button = this._nodeTab.SendBtn.getComponent(Button); + // if (button) { + // button.interactable = canSend; + // } + } + + /** + * 点击注册按钮 + */ + private onClickRegister(): void { + // 获取输入框 + const emailInput = this._nodeTab.EmailInput?.getComponent(EditBox); + const passwordInput = this._nodeTab.PasswordInput?.getComponent(EditBox); + const codeInput = this._nodeTab.CodeInput?.getComponent(EditBox); + + if (!emailInput || !passwordInput || !codeInput) { + console.error("未找到输入框"); + return; + } + + const email = emailInput.string.trim(); + const password = passwordInput.string.trim(); + const code = codeInput.string.trim(); + + // 验证输入 + if (!email) { + console.log("请输入邮箱"); + // TODO: 显示提示信息 + return; + } + + if (!this.isValidEmail(email)) { + console.log("请输入有效的邮箱地址"); + // TODO: 显示提示信息 + return; + } + + if (!password) { + console.log("请输入密码"); + // TODO: 显示提示信息 + return; + } + + if (password.length < 6) { + console.log("密码长度不能少于6位"); + // TODO: 显示提示信息 + return; + } + + if (!code) { + console.log("请输入验证码"); + // TODO: 显示提示信息 + return; + } + + // TODO: 调用后端接口进行注册 + console.log("注册:", email, password, code); + this.requestRegister(email, password, code); + } + + /** + * 调用后端注册接口 + */ + private requestRegister(email: string, password: string, code: string): void { + // TODO: 实现后端注册接口调用 + // 示例: + // const data = { + // email: email, + // password: password, + // code: code + // }; + // NetworkManager.request('/api/register', data, (response) => { + // if (response.success) { + // // 注册成功 + // console.log('注册成功'); + // // 关闭注册界面 + // this.close(); + // } else { + // // 注册失败,显示错误信息 + // console.error('注册失败:', response.message); + // } + // }); + + console.log("TODO: 实现注册接口调用"); + } + + /** + * 点击关闭按钮 + */ + private onClickClose(): void { + // 清理倒计时定时器 + if (this._countdownTimer) { + clearInterval(this._countdownTimer); + this._countdownTimer = null; + } + + // 关闭当前界面 + this.close(); + } + + /** + * 验证邮箱格式 + */ + private isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + } + + /** + * 界面销毁时调用 + */ + protected onDestroy(): void { + // 清理倒计时定时器 + if (this._countdownTimer) { + clearInterval(this._countdownTimer); + this._countdownTimer = null; + } + } +} diff --git a/assets/Scripts/chat18x/ui/panels/RegisterPanel.ts.meta b/assets/Scripts/chat18x/ui/panels/RegisterPanel.ts.meta new file mode 100644 index 00000000..16914674 --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/RegisterPanel.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "565b780d-f431-4e60-aa02-73df95d28a88", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts b/assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts new file mode 100644 index 00000000..4939f72b --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts @@ -0,0 +1,279 @@ +import { _decorator, EditBox, Label } from "cc"; +import Utils from "../../../Main/Common/Utils"; +import li_BaseView from "../../../Main/Common/li_BaseView"; +import { GButton } from "../../../Main/Common/GButton"; +import { ViewManager } from "../../../Main/Manager/ViewManager"; + +const { ccclass, property } = _decorator; + +/** + * 安全验证页面 + * 预制体位于 resources/loginPanel/SafeCheckPanel + */ +@ccclass("SafeCheckPanel") +export class SafeCheckPanel extends li_BaseView { + private _nodeTab: any = {}; + + // 验证码倒计时 + private _countdown: number = 0; + private _countdownTimer: any = null; + + /** + * 组件初始化时调用 + */ + onLoadCT(): void { + // 解析节点,将子节点存储到 _nodeTab 中 + Utils.parseNode(this.node, this._nodeTab); + + // 初始化UI + this.initUI(); + + // 注册事件监听 + this.registerListener(); + } + + /** + * 初始化UI + */ + private initUI(): void { + // 初始化发送按钮状态 + this.updateSendBtnState(true); + } + + /** + * 注册事件监听 + */ + private registerListener(): void { + // 绑定发送验证码按钮点击事件 + if (this._nodeTab.SendBtn) { + GButton.BandClick(this._nodeTab.SendBtn, this.onClickSendCode, this); + } + + // 绑定确定按钮点击事件 + if (this._nodeTab.ConfirmBtn) { + GButton.BandClick(this._nodeTab.ConfirmBtn, this.onClickConfirm, this); + } + + // 绑定关闭按钮点击事件 + if (this._nodeTab.CloseBtn) { + GButton.BandClick(this._nodeTab.CloseBtn, this.onClickClose, this); + } + } + + /** + * 点击发送验证码按钮 + */ + private onClickSendCode(): void { + // 如果正在倒计时,不允许再次发送 + if (this._countdown > 0) { + return; + } + + // 获取邮箱输入框 + const emailInput = this._nodeTab.EmailInput?.getComponent(EditBox); + + if (!emailInput) { + console.error("未找到邮箱输入框"); + return; + } + + const email = emailInput.string.trim(); + + // 验证邮箱 + if (!email) { + console.log("请输入邮箱"); + // TODO: 显示提示信息 + return; + } + + if (!this.isValidEmail(email)) { + console.log("请输入有效的邮箱地址"); + // TODO: 显示提示信息 + return; + } + + // TODO: 调用后端接口发送验证码 + console.log("发送验证码到:", email); + this.requestSendCode(email); + } + + /** + * 调用后端接口发送验证码 + */ + private requestSendCode(email: string): void { + // TODO: 实现后端发送验证码接口调用 + // 示例: + // const data = { email: email, type: 'forgetPassword' }; + // NetworkManager.request('/api/sendCode', data, (response) => { + // if (response.success) { + // // 发送成功,开始倒计时 + // this.startCountdown(); + // } else { + // // 发送失败,显示错误信息 + // console.error('发送验证码失败:', response.message); + // } + // }); + + console.log("TODO: 实现发送验证码接口调用"); + // 临时模拟发送成功,启动倒计时 + this.startCountdown(); + } + + /** + * 开始倒计时 + */ + private startCountdown(): void { + this._countdown = 60; // 60秒倒计时 + this.updateSendBtnState(false); + + this._countdownTimer = setInterval(() => { + this._countdown--; + + if (this._countdown <= 0) { + // 倒计时结束 + clearInterval(this._countdownTimer); + this._countdownTimer = null; + this.updateSendBtnState(true); + } else { + // 更新按钮文本 + this.updateSendBtnState(false); + } + }, 1000); + } + + /** + * 更新发送按钮状态 + */ + private updateSendBtnState(canSend: boolean): void { + if (!this._nodeTab.SendBtn) return; + + const label = this._nodeTab.SendBtn.getComponentInChildren(Label); + if (label) { + if (canSend) { + label.string = "发送验证码"; + } else { + label.string = `${this._countdown}秒后重试`; + } + } + + // TODO: 可以设置按钮的可交互状态 + // const button = this._nodeTab.SendBtn.getComponent(Button); + // if (button) { + // button.interactable = canSend; + // } + } + + /** + * 点击确定按钮 + */ + private onClickConfirm(): void { + // 获取邮箱和验证码输入框 + const emailInput = this._nodeTab.EmailInput?.getComponent(EditBox); + const codeInput = this._nodeTab.CodeInput?.getComponent(EditBox); + + if (!emailInput || !codeInput) { + console.error("未找到邮箱或验证码输入框"); + return; + } + + const email = emailInput.string.trim(); + const code = codeInput.string.trim(); + + // 验证输入 + if (!email) { + console.log("请输入邮箱"); + // TODO: 显示提示信息 + return; + } + + if (!this.isValidEmail(email)) { + console.log("请输入有效的邮箱地址"); + // TODO: 显示提示信息 + return; + } + + if (!code) { + console.log("请输入验证码"); + // TODO: 显示提示信息 + return; + } + + // TODO: 调用后端接口验证验证码 + console.log("验证验证码:", email, code); + this.requestVerifyCode(email, code); + } + + /** + * 调用后端接口验证验证码 + */ + private requestVerifyCode(email: string, code: string): void { + // TODO: 实现后端验证验证码接口调用 + // 示例: + // const data = { + // email: email, + // code: code, + // type: 'forgetPassword' + // }; + // NetworkManager.request('/api/verifyCode', data, (response) => { + // if (response.success) { + // // 验证成功,跳转到修改密码页面 + // this.openNewPasswordPanel(email, code); + // } else { + // // 验证失败,显示错误信息 + // console.error('验证码错误:', response.message); + // // TODO: 显示错误提示 + // } + // }); + + console.log("TODO: 实现验证验证码接口调用"); + // 临时模拟验证成功,跳转到修改密码页面 + this.openNewPasswordPanel(email, code); + } + + /** + * 打开修改密码页面 + */ + private openNewPasswordPanel(email: string, code: string): void { + // 关闭当前页面 + this.close(); + + // 打开修改密码页面,并传递邮箱和验证码信息 + ViewManager.I.openMainView("loginPanel/NewPasswordPanel", { + email: email, + code: code, + }); + } + + /** + * 点击关闭按钮 + */ + private onClickClose(): void { + // 清理倒计时定时器 + if (this._countdownTimer) { + clearInterval(this._countdownTimer); + this._countdownTimer = null; + } + + // 关闭当前界面 + this.close(); + } + + /** + * 验证邮箱格式 + */ + private isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + } + + /** + * 界面销毁时调用 + */ + protected onDestroy(): void { + // 清理倒计时定时器 + if (this._countdownTimer) { + clearInterval(this._countdownTimer); + this._countdownTimer = null; + } + } +} diff --git a/assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts.meta b/assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts.meta new file mode 100644 index 00000000..2e0920f9 --- /dev/null +++ b/assets/Scripts/chat18x/ui/panels/SafeCheckPanel.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "d3dfdbed-8fb1-4348-b9d4-32c6b9406310", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/bundles/Chat18x/releasePanel.prefab.meta b/assets/bundles/Chat18x/releasePanel.prefab.meta index 0c69f4e7..c82e837a 100644 --- a/assets/bundles/Chat18x/releasePanel.prefab.meta +++ b/assets/bundles/Chat18x/releasePanel.prefab.meta @@ -2,7 +2,7 @@ "ver": "1.1.50", "importer": "prefab", "imported": true, - "uuid": "6972298a-8553-4fe8-8296-06f6d886d450", + "uuid": "aa097cc9-b234-4077-8b2e-e543767ca13f", "files": [ ".json" ], diff --git a/assets/resources/loginPanel/LoginPanel.prefab b/assets/resources/loginPanel/LoginPanel.prefab index 851cc465..10d34600 100644 --- a/assets/resources/loginPanel/LoginPanel.prefab +++ b/assets/resources/loginPanel/LoginPanel.prefab @@ -27,18 +27,21 @@ ], "_active": true, "_components": [ - { - "__id__": 154 - }, { "__id__": 156 }, { "__id__": 158 + }, + { + "__id__": 160 + }, + { + "__id__": 162 } ], "_prefab": { - "__id__": 160 + "__id__": 164 }, "_lpos": { "__type__": "cc.Vec3", @@ -284,23 +287,23 @@ "__id__": 121 }, { - "__id__": 133 + "__id__": 135 }, { - "__id__": 141 + "__id__": 143 } ], "_active": true, "_components": [ { - "__id__": 149 + "__id__": 151 }, { - "__id__": 151 + "__id__": 153 } ], "_prefab": { - "__id__": 153 + "__id__": 155 }, "_lpos": { "__type__": "cc.Vec3", @@ -3074,10 +3077,13 @@ }, { "__id__": 130 + }, + { + "__id__": 132 } ], "_prefab": { - "__id__": 132 + "__id__": 134 }, "_lpos": { "__type__": "cc.Vec3", @@ -3343,6 +3349,62 @@ "__type__": "cc.CompPrefabInfo", "fileId": "e1FEs4AnFF84EGMms/npks" }, + { + "__type__": "cc.Button", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 121 + }, + "_enabled": true, + "__prefab": { + "__id__": 133 + }, + "clickEvents": [], + "_interactable": true, + "_transition": 0, + "_normalColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_hoverColor": { + "__type__": "cc.Color", + "r": 211, + "g": 211, + "b": 211, + "a": 255 + }, + "_pressedColor": { + "__type__": "cc.Color", + "r": 255, + "g": 255, + "b": 255, + "a": 255 + }, + "_disabledColor": { + "__type__": "cc.Color", + "r": 124, + "g": 124, + "b": 124, + "a": 255 + }, + "_normalSprite": null, + "_hoverSprite": null, + "_pressedSprite": null, + "_disabledSprite": null, + "_duration": 0.1, + "_zoomScale": 1.2, + "_target": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "36gJ21lkFNkIiRuVYHtlnX" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -3367,18 +3429,18 @@ "_children": [], "_active": true, "_components": [ - { - "__id__": 134 - }, { "__id__": 136 }, { "__id__": 138 + }, + { + "__id__": 140 } ], "_prefab": { - "__id__": 140 + "__id__": 142 }, "_lpos": { "__type__": "cc.Vec3", @@ -3415,11 +3477,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 133 + "__id__": 135 }, "_enabled": true, "__prefab": { - "__id__": 135 + "__id__": 137 }, "_contentSize": { "__type__": "cc.Size", @@ -3443,11 +3505,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 133 + "__id__": 135 }, "_enabled": true, "__prefab": { - "__id__": 137 + "__id__": 139 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -3488,11 +3550,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 133 + "__id__": 135 }, "_enabled": true, "__prefab": { - "__id__": 139 + "__id__": 141 }, "clickEvents": [], "_interactable": true, @@ -3562,18 +3624,18 @@ "_children": [], "_active": true, "_components": [ - { - "__id__": 142 - }, { "__id__": 144 }, { "__id__": 146 + }, + { + "__id__": 148 } ], "_prefab": { - "__id__": 148 + "__id__": 150 }, "_lpos": { "__type__": "cc.Vec3", @@ -3610,11 +3672,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 141 + "__id__": 143 }, "_enabled": true, "__prefab": { - "__id__": 143 + "__id__": 145 }, "_contentSize": { "__type__": "cc.Size", @@ -3638,11 +3700,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 141 + "__id__": 143 }, "_enabled": true, "__prefab": { - "__id__": 145 + "__id__": 147 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -3709,11 +3771,11 @@ "_objFlags": 0, "__editorExtras__": {}, "node": { - "__id__": 141 + "__id__": 143 }, "_enabled": true, "__prefab": { - "__id__": 147 + "__id__": 149 }, "clickEvents": [], "_interactable": true, @@ -3782,7 +3844,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 150 + "__id__": 152 }, "_contentSize": { "__type__": "cc.Size", @@ -3810,7 +3872,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 152 + "__id__": 154 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -3868,7 +3930,7 @@ }, "_enabled": true, "__prefab": { - "__id__": 155 + "__id__": 157 }, "_contentSize": { "__type__": "cc.Size", @@ -3896,7 +3958,7 @@ }, "_enabled": false, "__prefab": { - "__id__": 157 + "__id__": 159 }, "_customMaterial": null, "_srcBlendFactor": 2, @@ -3941,7 +4003,7 @@ }, "_enabled": false, "__prefab": { - "__id__": 159 + "__id__": 161 }, "_type": 3, "_inverted": false, @@ -3953,6 +4015,25 @@ "__type__": "cc.CompPrefabInfo", "fileId": "ccqvpyXtxAKoDFDkk+CXDL" }, + { + "__type__": "ce7e3FnjthOXJIbDJGurzbE", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 163 + }, + "m_rootNode": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "d5Jg3zVgVPlKq3fn8Mi7K2" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -3962,6 +4043,7 @@ "__id__": 0 }, "fileId": "06liSCoLBIbI3C6sNktTBJ", + "instance": null, "targetOverrides": null } ] \ No newline at end of file diff --git a/assets/resources/loginPanel/NewPasswordPanel.prefab b/assets/resources/loginPanel/NewPasswordPanel.prefab index 1f71bbdb..cce77bfc 100644 --- a/assets/resources/loginPanel/NewPasswordPanel.prefab +++ b/assets/resources/loginPanel/NewPasswordPanel.prefab @@ -35,10 +35,13 @@ }, { "__id__": 132 + }, + { + "__id__": 134 } ], "_prefab": { - "__id__": 134 + "__id__": 136 }, "_lpos": { "__type__": "cc.Vec3", @@ -3274,6 +3277,25 @@ "__type__": "cc.CompPrefabInfo", "fileId": "16wyEMuj1Meq5zmjtv4o5c" }, + { + "__type__": "e11c66hsnpOSYSUuvGlNPx1", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 135 + }, + "m_rootNode": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "b9CUMt3LlG0J8Jsuq+d3u5" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -3283,6 +3305,7 @@ "__id__": 0 }, "fileId": "e8L1H16d9HP6FYp51BeRWn", + "instance": null, "targetOverrides": null } ] \ No newline at end of file diff --git a/assets/resources/loginPanel/RegisterPanel.prefab b/assets/resources/loginPanel/RegisterPanel.prefab index aa23b2c4..84e46ead 100644 --- a/assets/resources/loginPanel/RegisterPanel.prefab +++ b/assets/resources/loginPanel/RegisterPanel.prefab @@ -35,10 +35,13 @@ }, { "__id__": 204 + }, + { + "__id__": 206 } ], "_prefab": { - "__id__": 206 + "__id__": 208 }, "_lpos": { "__type__": "cc.Vec3", @@ -5101,6 +5104,25 @@ "__type__": "cc.CompPrefabInfo", "fileId": "d3J11kwgJNxr/AEbd2kVpm" }, + { + "__type__": "565b7gN9DFOYKoCc9+V0oqI", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 207 + }, + "m_rootNode": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "335TfMiNxP2bYMZhXg1isT" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -5110,6 +5132,7 @@ "__id__": 0 }, "fileId": "72g0/xfadLzKbZU8hLeUmn", + "instance": null, "targetOverrides": null } ] \ No newline at end of file diff --git a/assets/resources/loginPanel/SafeCheckPanel.prefab b/assets/resources/loginPanel/SafeCheckPanel.prefab index 9d7c334e..8b09e0a6 100644 --- a/assets/resources/loginPanel/SafeCheckPanel.prefab +++ b/assets/resources/loginPanel/SafeCheckPanel.prefab @@ -35,10 +35,13 @@ }, { "__id__": 158 + }, + { + "__id__": 160 } ], "_prefab": { - "__id__": 160 + "__id__": 162 }, "_lpos": { "__type__": "cc.Vec3", @@ -3962,6 +3965,25 @@ "__type__": "cc.CompPrefabInfo", "fileId": "beWC4jbyNGzplzxhBUPZzq" }, + { + "__type__": "d3dfdvtj7FDSLnUMsa5QGMQ", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 161 + }, + "m_rootNode": null, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "36C8lniCNDfLQLX2kQVYKP" + }, { "__type__": "cc.PrefabInfo", "root": { @@ -3971,6 +3993,7 @@ "__id__": 0 }, "fileId": "342wdtF7JC273lhsG3dRQw", + "instance": null, "targetOverrides": null } ] \ No newline at end of file diff --git a/proto_cs b/proto_cs index 777dbf99..ac9f5df0 160000 --- a/proto_cs +++ b/proto_cs @@ -1 +1 @@ -Subproject commit 777dbf997791c16d4be16bd32b52c11809b93f90 +Subproject commit ac9f5df0b33b88ddd9a929eb0e4766624937b249 diff --git a/xchat_cfg b/xchat_cfg index b84eb447..d2c7978a 160000 --- a/xchat_cfg +++ b/xchat_cfg @@ -1 +1 @@ -Subproject commit b84eb4476aeb46d85e8b6def4cf5df7c1a1d9906 +Subproject commit d2c7978a508a20beba7b6a17e8c1d2e0517ba199