优化屏蔽,可以保存状态
This commit is contained in:
@@ -20,6 +20,7 @@ namespace App\Http\Controllers;
|
|||||||
use App\Events\UserKicked;
|
use App\Events\UserKicked;
|
||||||
use App\Events\UserMuted;
|
use App\Events\UserMuted;
|
||||||
use App\Http\Requests\ChangePasswordRequest;
|
use App\Http\Requests\ChangePasswordRequest;
|
||||||
|
use App\Http\Requests\UpdateChatPreferencesRequest;
|
||||||
use App\Http\Requests\UpdateProfileRequest;
|
use App\Http\Requests\UpdateProfileRequest;
|
||||||
use App\Models\Room;
|
use App\Models\Room;
|
||||||
use App\Models\Sysparam;
|
use App\Models\Sysparam;
|
||||||
@@ -109,7 +110,6 @@ class UserController extends Controller
|
|||||||
$data['vip']['Name'] = $targetUser->vipName();
|
$data['vip']['Name'] = $targetUser->vipName();
|
||||||
$data['vip']['Icon'] = $targetUser->vipIcon();
|
$data['vip']['Icon'] = $targetUser->vipIcon();
|
||||||
|
|
||||||
|
|
||||||
// 拥有封禁IP(level_banip)或踢人以上权限的管理,可以查看IP和归属地
|
// 拥有封禁IP(level_banip)或踢人以上权限的管理,可以查看IP和归属地
|
||||||
$levelBanIp = (int) Sysparam::getValue('level_banip', '15');
|
$levelBanIp = (int) Sysparam::getValue('level_banip', '15');
|
||||||
if ($operator && $operator->user_level >= $levelBanIp) {
|
if ($operator && $operator->user_level >= $levelBanIp) {
|
||||||
@@ -203,6 +203,31 @@ class UserController extends Controller
|
|||||||
return response()->json(['status' => 'success', 'message' => '资料更新成功。']);
|
return response()->json(['status' => 'success', 'message' => '资料更新成功。']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存聊天室屏蔽与禁音偏好。
|
||||||
|
*/
|
||||||
|
public function updateChatPreferences(UpdateChatPreferencesRequest $request): JsonResponse
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
$data = $request->validated();
|
||||||
|
|
||||||
|
$preferences = [
|
||||||
|
// 去重并重建索引,保持存储结构稳定,便于后续继续扩展其它屏蔽项。
|
||||||
|
'blocked_system_senders' => array_values(array_unique($data['blocked_system_senders'] ?? [])),
|
||||||
|
'sound_muted' => (bool) $data['sound_muted'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$user->update([
|
||||||
|
'chat_preferences' => $preferences,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => '聊天室偏好已保存。',
|
||||||
|
'data' => $preferences,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改密码 (对应 chpasswd.asp)
|
* 修改密码 (对应 chpasswd.asp)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件功能:聊天室偏好设置验证器
|
||||||
|
* 负责校验用户提交的屏蔽播报与禁音配置。
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聊天室偏好设置验证器
|
||||||
|
* 仅允许提交白名单内的屏蔽项与布尔型禁音状态。
|
||||||
|
*/
|
||||||
|
class UpdateChatPreferencesRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 允许已登录用户保存自己的聊天室偏好。
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return $this->user() !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取聊天室偏好的验证规则。
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'blocked_system_senders' => ['nullable', 'array'],
|
||||||
|
'blocked_system_senders.*' => [
|
||||||
|
'string',
|
||||||
|
Rule::in(['钓鱼播报', '星海小博士', '百家乐', '跑马']),
|
||||||
|
],
|
||||||
|
'sound_muted' => ['required', 'boolean'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取聊天室偏好的中文错误提示。
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'blocked_system_senders.array' => '屏蔽设置格式无效。',
|
||||||
|
'blocked_system_senders.*.in' => '存在不支持的屏蔽项目。',
|
||||||
|
'sound_muted.required' => '请传入禁音状态。',
|
||||||
|
'sound_muted.boolean' => '禁音状态格式无效。',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ class User extends Authenticatable
|
|||||||
'custom_leave_message',
|
'custom_leave_message',
|
||||||
'custom_join_effect',
|
'custom_join_effect',
|
||||||
'custom_leave_effect',
|
'custom_leave_effect',
|
||||||
|
'chat_preferences',
|
||||||
'user_level',
|
'user_level',
|
||||||
'inviter_id',
|
'inviter_id',
|
||||||
'room_id',
|
'room_id',
|
||||||
@@ -101,6 +102,7 @@ class User extends Authenticatable
|
|||||||
'sj' => 'datetime',
|
'sj' => 'datetime',
|
||||||
'q3_time' => 'datetime',
|
'q3_time' => 'datetime',
|
||||||
'has_received_new_gift' => 'boolean',
|
'has_received_new_gift' => 'boolean',
|
||||||
|
'chat_preferences' => 'array',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 为用户表增加聊天室偏好配置字段。
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->json('chat_preferences')->nullable()->after('custom_leave_effect')->comment('聊天室屏蔽与禁音偏好配置');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回滚用户表中的聊天室偏好配置字段。
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('chat_preferences');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -94,8 +94,10 @@
|
|||||||
revokeUrl: "{{ route('chat.appoint.revoke') }}",
|
revokeUrl: "{{ route('chat.appoint.revoke') }}",
|
||||||
rewardUrl: "{{ route('command.reward') }}",
|
rewardUrl: "{{ route('command.reward') }}",
|
||||||
rewardQuotaUrl: "{{ route('command.reward_quota') }}",
|
rewardQuotaUrl: "{{ route('command.reward_quota') }}",
|
||||||
|
chatPreferencesUrl: "{{ route('user.update_chat_preferences') }}",
|
||||||
userJjb: {{ (int) $user->jjb }}, // 当前用户金币(求婚前金额预检查用)
|
userJjb: {{ (int) $user->jjb }}, // 当前用户金币(求婚前金额预检查用)
|
||||||
myGold: {{ (int) $user->jjb }}, // 赠金币面板显示余额用(赠送成功后前端更新)
|
myGold: {{ (int) $user->jjb }}, // 赠金币面板显示余额用(赠送成功后前端更新)
|
||||||
|
chatPreferences: @json($user->chat_preferences ?? []),
|
||||||
|
|
||||||
// ─── 婚姻系统 ──────────────────────────────
|
// ─── 婚姻系统 ──────────────────────────────
|
||||||
minWeddingCost: {{ (int) \App\Models\WeddingTier::where('is_active', true)->orderBy('amount')->value('amount') ?? 0 }},
|
minWeddingCost: {{ (int) \App\Models\WeddingTier::where('is_active', true)->orderBy('amount')->value('amount') ?? 0 }},
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
const onlineCount = document.getElementById('online-count');
|
const onlineCount = document.getElementById('online-count');
|
||||||
const onlineCountBottom = document.getElementById('online-count-bottom');
|
const onlineCountBottom = document.getElementById('online-count-bottom');
|
||||||
const BLOCKED_SYSTEM_SENDERS_STORAGE_KEY = 'chat_blocked_system_senders';
|
const BLOCKED_SYSTEM_SENDERS_STORAGE_KEY = 'chat_blocked_system_senders';
|
||||||
|
const CHAT_SOUND_MUTED_STORAGE_KEY = 'chat_sound_muted';
|
||||||
const BLOCKABLE_SYSTEM_SENDERS = ['钓鱼播报', '星海小博士', '百家乐', '跑马'];
|
const BLOCKABLE_SYSTEM_SENDERS = ['钓鱼播报', '星海小博士', '百家乐', '跑马'];
|
||||||
|
|
||||||
// ── 消息区:手机端双触发打开用户名片(PC 端靠 ondblclick 内联属性)──
|
// ── 消息区:手机端双触发打开用户名片(PC 端靠 ondblclick 内联属性)──
|
||||||
@@ -70,7 +71,26 @@
|
|||||||
let onlineUsers = {};
|
let onlineUsers = {};
|
||||||
let autoScroll = true;
|
let autoScroll = true;
|
||||||
let _maxMsgId = 0; // 记录当前收到的最大消息 ID
|
let _maxMsgId = 0; // 记录当前收到的最大消息 ID
|
||||||
let blockedSystemSenders = new Set(loadBlockedSystemSenders());
|
const initialChatPreferences = normalizeChatPreferences(window.chatContext?.chatPreferences || {});
|
||||||
|
let blockedSystemSenders = new Set(initialChatPreferences.blocked_system_senders);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规整聊天室偏好对象,过滤非法配置并补齐默认值。
|
||||||
|
*
|
||||||
|
* @param {Record<string, any>} raw 原始偏好对象
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function normalizeChatPreferences(raw) {
|
||||||
|
const blocked = Array.isArray(raw?.blocked_system_senders)
|
||||||
|
? raw.blocked_system_senders.filter(sender => BLOCKABLE_SYSTEM_SENDERS.includes(sender))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 默认所有用户都处于“不屏蔽”的开放状态,只有显式勾选的项目才会进入该列表。
|
||||||
|
blocked_system_senders: Array.from(new Set(blocked)),
|
||||||
|
sound_muted: Boolean(raw?.sound_muted),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 localStorage 读取已屏蔽的系统播报发送者列表。
|
* 从 localStorage 读取已屏蔽的系统播报发送者列表。
|
||||||
@@ -101,6 +121,77 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前禁音开关是否处于打开状态。
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isSoundMuted() {
|
||||||
|
const muteCheckbox = document.getElementById('sound_muted');
|
||||||
|
|
||||||
|
if (muteCheckbox) {
|
||||||
|
return Boolean(muteCheckbox.checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return localStorage.getItem(CHAT_SOUND_MUTED_STORAGE_KEY) === '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前聊天室偏好快照。
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function buildChatPreferencesPayload() {
|
||||||
|
return {
|
||||||
|
blocked_system_senders: Array.from(blockedSystemSenders),
|
||||||
|
sound_muted: isSoundMuted(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将聊天室偏好写入本地缓存,供刷新前快速恢复与迁移兜底。
|
||||||
|
*/
|
||||||
|
function persistChatPreferencesToLocal() {
|
||||||
|
persistBlockedSystemSenders();
|
||||||
|
localStorage.setItem(CHAT_SOUND_MUTED_STORAGE_KEY, isSoundMuted() ? '1' : '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将当前聊天室偏好保存到当前登录账号。
|
||||||
|
*/
|
||||||
|
async function saveChatPreferences() {
|
||||||
|
const payload = buildChatPreferencesPayload();
|
||||||
|
|
||||||
|
persistChatPreferencesToLocal();
|
||||||
|
|
||||||
|
if (!window.chatContext?.chatPreferencesUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(window.chatContext.chatPreferencesUrl, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content ?? '',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('save chat preferences failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data?.status === 'success') {
|
||||||
|
window.chatContext.chatPreferences = normalizeChatPreferences(data.data || payload);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('聊天室偏好保存失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步屏蔽菜单中的复选框状态。
|
* 同步屏蔽菜单中的复选框状态。
|
||||||
*/
|
*/
|
||||||
@@ -237,6 +328,7 @@
|
|||||||
|
|
||||||
persistBlockedSystemSenders();
|
persistBlockedSystemSenders();
|
||||||
syncBlockedSystemSenderCheckboxes();
|
syncBlockedSystemSenderCheckboxes();
|
||||||
|
void saveChatPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
syncBlockedSystemSenderCheckboxes();
|
syncBlockedSystemSenderCheckboxes();
|
||||||
@@ -1589,11 +1681,28 @@
|
|||||||
if (saved) {
|
if (saved) {
|
||||||
applyFontSize(saved);
|
applyFontSize(saved);
|
||||||
}
|
}
|
||||||
// 恢复禁音复选框状态
|
|
||||||
const muted = localStorage.getItem('chat_sound_muted') === '1';
|
const storedBlockedSystemSenders = loadBlockedSystemSenders();
|
||||||
|
const mutedFromLocal = localStorage.getItem(CHAT_SOUND_MUTED_STORAGE_KEY) === '1';
|
||||||
|
const hasServerPreferences = initialChatPreferences.blocked_system_senders.length > 0 || initialChatPreferences.sound_muted;
|
||||||
|
const shouldMigrateLocalPreferences = !hasServerPreferences
|
||||||
|
&& (storedBlockedSystemSenders.length > 0 || mutedFromLocal);
|
||||||
|
|
||||||
|
if (shouldMigrateLocalPreferences) {
|
||||||
|
blockedSystemSenders = new Set(storedBlockedSystemSenders);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复禁音复选框状态;默认一律为未禁音。
|
||||||
|
const muted = shouldMigrateLocalPreferences ? mutedFromLocal : initialChatPreferences.sound_muted;
|
||||||
const muteChk = document.getElementById('sound_muted');
|
const muteChk = document.getElementById('sound_muted');
|
||||||
if (muteChk) muteChk.checked = muted;
|
if (muteChk) muteChk.checked = muted;
|
||||||
syncBlockedSystemSenderCheckboxes();
|
syncBlockedSystemSenderCheckboxes();
|
||||||
|
|
||||||
|
if (shouldMigrateLocalPreferences) {
|
||||||
|
void saveChatPreferences();
|
||||||
|
} else {
|
||||||
|
persistChatPreferencesToLocal();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── 特效禁音开关 ─────────────────────────────────────────────────
|
// ── 特效禁音开关 ─────────────────────────────────────────────────
|
||||||
@@ -1604,10 +1713,12 @@
|
|||||||
* @param {boolean} muted true = 禁音,false = 开启声音
|
* @param {boolean} muted true = 禁音,false = 开启声音
|
||||||
*/
|
*/
|
||||||
function toggleSoundMute(muted) {
|
function toggleSoundMute(muted) {
|
||||||
localStorage.setItem('chat_sound_muted', muted ? '1' : '0');
|
localStorage.setItem(CHAT_SOUND_MUTED_STORAGE_KEY, muted ? '1' : '0');
|
||||||
if (muted && typeof EffectSounds !== 'undefined') {
|
if (muted && typeof EffectSounds !== 'undefined') {
|
||||||
EffectSounds.stop(); // 立即停止当前音效
|
EffectSounds.stop(); // 立即停止当前音效
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void saveChatPreferences();
|
||||||
}
|
}
|
||||||
window.toggleSoundMute = toggleSoundMute;
|
window.toggleSoundMute = toggleSoundMute;
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ Route::middleware(['chat.auth'])->group(function () {
|
|||||||
// ---- 第七阶段:用户资料与特权管理 ----
|
// ---- 第七阶段:用户资料与特权管理 ----
|
||||||
Route::get('/user/{username}', [UserController::class, 'show'])->name('user.show');
|
Route::get('/user/{username}', [UserController::class, 'show'])->name('user.show');
|
||||||
Route::put('/user/profile', [UserController::class, 'updateProfile'])->name('user.update_profile');
|
Route::put('/user/profile', [UserController::class, 'updateProfile'])->name('user.update_profile');
|
||||||
|
Route::put('/user/chat-preferences', [UserController::class, 'updateChatPreferences'])->name('user.update_chat_preferences');
|
||||||
Route::post('/user/generate-wechat-code', [UserController::class, 'generateWechatCode'])->name('user.generate_wechat_code');
|
Route::post('/user/generate-wechat-code', [UserController::class, 'generateWechatCode'])->name('user.generate_wechat_code');
|
||||||
Route::post('/user/unbind-wechat', [UserController::class, 'unbindWechat'])->name('user.unbind_wechat');
|
Route::post('/user/unbind-wechat', [UserController::class, 'unbindWechat'])->name('user.unbind_wechat');
|
||||||
Route::post('/user/send-email-code', [\App\Http\Controllers\Api\VerificationController::class, 'sendEmailCode'])->name('user.send_email_code');
|
Route::post('/user/send-email-code', [\App\Http\Controllers\Api\VerificationController::class, 'sendEmailCode'])->name('user.send_email_code');
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件功能:用户控制器功能测试
|
||||||
|
* 覆盖个人资料、密码与聊天室偏好设置等关键接口。
|
||||||
|
*/
|
||||||
|
|
||||||
namespace Tests\Feature;
|
namespace Tests\Feature;
|
||||||
|
|
||||||
use App\Models\Room;
|
use App\Models\Room;
|
||||||
@@ -15,6 +20,9 @@ class UserControllerTest extends TestCase
|
|||||||
{
|
{
|
||||||
use RefreshDatabase;
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户控制器测试所需的系统参数。
|
||||||
|
*/
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
@@ -27,6 +35,9 @@ class UserControllerTest extends TestCase
|
|||||||
Sysparam::updateOrCreate(['alias' => 'smtp_enabled'], ['body' => '1']); // Allow email changing in tests
|
Sysparam::updateOrCreate(['alias' => 'smtp_enabled'], ['body' => '1']); // Allow email changing in tests
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试可以查看用户资料卡接口。
|
||||||
|
*/
|
||||||
public function test_can_view_user_profile()
|
public function test_can_view_user_profile()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create([
|
$user = User::factory()->create([
|
||||||
@@ -43,6 +54,9 @@ class UserControllerTest extends TestCase
|
|||||||
->assertJsonPath('data.user_level', 10);
|
->assertJsonPath('data.user_level', 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试不改邮箱时可以正常更新个人资料。
|
||||||
|
*/
|
||||||
public function test_can_update_profile_without_email_change()
|
public function test_can_update_profile_without_email_change()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create([
|
$user = User::factory()->create([
|
||||||
@@ -67,6 +81,9 @@ class UserControllerTest extends TestCase
|
|||||||
$this->assertEquals('new sign', $user->sign);
|
$this->assertEquals('new sign', $user->sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试改邮箱但未提交验证码时会被拒绝。
|
||||||
|
*/
|
||||||
public function test_cannot_update_email_without_verification_code()
|
public function test_cannot_update_email_without_verification_code()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create([
|
$user = User::factory()->create([
|
||||||
@@ -87,6 +104,9 @@ class UserControllerTest extends TestCase
|
|||||||
->assertJsonPath('message', '新邮箱需要验证码,请先获取并填写验证码。');
|
->assertJsonPath('message', '新邮箱需要验证码,请先获取并填写验证码。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试提供有效验证码后可以成功修改邮箱。
|
||||||
|
*/
|
||||||
public function test_can_update_email_with_valid_code()
|
public function test_can_update_email_with_valid_code()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create([
|
$user = User::factory()->create([
|
||||||
@@ -111,6 +131,33 @@ class UserControllerTest extends TestCase
|
|||||||
$this->assertEquals('new@example.com', $user->email);
|
$this->assertEquals('new@example.com', $user->email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试可以保存聊天室屏蔽与禁音偏好。
|
||||||
|
*/
|
||||||
|
public function test_can_update_chat_preferences(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'chat_preferences' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->putJson('/user/chat-preferences', [
|
||||||
|
'blocked_system_senders' => ['钓鱼播报', '跑马'],
|
||||||
|
'sound_muted' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertOk()
|
||||||
|
->assertJsonPath('status', 'success')
|
||||||
|
->assertJsonPath('data.blocked_system_senders.0', '钓鱼播报')
|
||||||
|
->assertJsonPath('data.blocked_system_senders.1', '跑马')
|
||||||
|
->assertJsonPath('data.sound_muted', true);
|
||||||
|
|
||||||
|
$user->refresh();
|
||||||
|
$this->assertEquals([
|
||||||
|
'blocked_system_senders' => ['钓鱼播报', '跑马'],
|
||||||
|
'sound_muted' => true,
|
||||||
|
], $user->chat_preferences);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_can_change_password()
|
public function test_can_change_password()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create([
|
$user = User::factory()->create([
|
||||||
|
|||||||
Reference in New Issue
Block a user