템플릿 테마를 앱 전체로 확장 - 제목 알약, 백품타, 모든 화면 배경/카드색
구조색 4종(mist/paper/linen/sage)을 const에서 뺀 뒤 AppPalette.applyTemplateTheme() 으로 템플릿 장착/해제 시 한 번에 바꿔치기하게 했다. 이 4개는 전부 "배경/카드/ 구분선" 용도로만 쓰이던 색이라, 장착 즉시 거의 모든 화면(백품타 포함, AppPalette.mist/ paper/sage를 쓰는 화면이면 코드 수정 없이 자동 반영)의 배경·카드·테두리가 테마를 따라간다. ink는 텍스트/버튼 등 너무 광범위하게 쓰여서 그대로 고정. 제목 알약(TitlePill, 24개 화면이 공유)은 ink를 직접 바꾸면 안 되므로 별도로 globalProfileTheme(ValueNotifier) 하나를 구독해서 배경을 theme.secondary로 바꾸게 했다 - 이 위젯 하나만 고쳐서 모든 화면의 제목 배경에 한 번에 적용. 백품타(study_timer_screen)는 시작/재개 버튼과 큰 타이머 숫자처럼 "중립" 요소만 테마 강조색(accent)으로 바꾸고, 일시정지(warning)/종료(danger) 같은 경고성 색은 의미가 있어서 그대로 뒀다. main_dashboard/profile_customize_screen은 실제로 장착이 바뀔 때마다 applyGlobalProfileTheme()을 호출해 위 두 전역 상태를 동기화한다. 구조색을 const에서 빼면서 기존에 const로 선언돼 있던 지점 몇 곳 (app_notice, device_checkout_ledger_page, group_detail_screen, main_dashboard, teacher_attendance_page)에서 const 컴파일 에러가 나서 해당 const 키워드만 제거해 수정했다.
This commit is contained in:
@@ -1,16 +1,36 @@
|
|||||||
// 🎨 앱 전역 무채색 팔레트 (coolors.co: 1c1c1c-daddd8-ecebe4-eef0f2-fafaff).
|
// 🎨 앱 전역 무채색 팔레트 (coolors.co: 1c1c1c-daddd8-ecebe4-eef0f2-fafaff).
|
||||||
// 배경/카드/배너 같은 구조적 색상은 전부 이 5색으로 통일한다.
|
// 배경/카드/배너 같은 구조적 색상은 전부 이 5색으로 통일한다.
|
||||||
// 삭제/위반/거절처럼 사용자가 즉시 알아채야 하는 경고성 색상(빨강 등)만 예외로 유지한다.
|
// 삭제/위반/거절처럼 사용자가 즉시 알아채야 하는 경고성 색상(빨강 등)만 예외로 유지한다.
|
||||||
|
//
|
||||||
|
// 🛍️ mist/paper/linen/sage(구조색 4종)는 프로필 꾸미기 템플릿을 장착하면 화면
|
||||||
|
// 전체에 그 테마가 번지도록 const를 빼고 바꿔치기 가능하게 했다. ink는 텍스트/
|
||||||
|
// 버튼 등 너무 많은 곳에서 쓰여서 바꾸면 가독성이 깨질 수 있어 그대로 고정 둔다.
|
||||||
|
// applyTemplateTheme()로만 바꾸고, 직접 대입하지 않는다.
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'profile_themes.dart';
|
||||||
|
|
||||||
class AppPalette {
|
class AppPalette {
|
||||||
AppPalette._();
|
AppPalette._();
|
||||||
|
|
||||||
static const Color ink = Color(0xFF1C1C1C); // 가장 어두운 톤 — 배너/주요 텍스트/버튼
|
static const Color ink = Color(0xFF1C1C1C); // 가장 어두운 톤 — 배너/주요 텍스트/버튼
|
||||||
static const Color sage = Color(0xFFDADDD8); // 보더/구분선/보조 표면
|
|
||||||
static const Color linen = Color(0xFFECEBE4); // 은은한 카드/배지 배경
|
static const Color _defaultSage = Color(0xFFDADDD8);
|
||||||
static const Color mist = Color(0xFFEEF0F2); // 페이지 배경
|
static const Color _defaultLinen = Color(0xFFECEBE4);
|
||||||
static const Color paper = Color(0xFFFAFAFF); // 카드 표면(거의 흰색)
|
static const Color _defaultMist = Color(0xFFEEF0F2);
|
||||||
|
static const Color _defaultPaper = Color(0xFFFAFAFF);
|
||||||
|
|
||||||
|
static Color sage = _defaultSage; // 보더/구분선/보조 표면
|
||||||
|
static Color linen = _defaultLinen; // 은은한 카드/배지 배경
|
||||||
|
static Color mist = _defaultMist; // 페이지 배경
|
||||||
|
static Color paper = _defaultPaper; // 카드 표면(거의 흰색)
|
||||||
|
|
||||||
|
/// 🛍️ 장착한 템플릿에 맞춰 구조색 4종을 한 번에 바꾼다. null을 주면 기본값으로 복원.
|
||||||
|
static void applyTemplateTheme(ProfileTheme? theme) {
|
||||||
|
mist = theme?.background ?? _defaultMist;
|
||||||
|
paper = theme?.surface ?? _defaultPaper;
|
||||||
|
linen = theme?.surfaceAlt ?? _defaultLinen;
|
||||||
|
sage = theme?.border ?? _defaultSage;
|
||||||
|
}
|
||||||
|
|
||||||
// 🚨 경고성 색상 3종 - 예전엔 화면마다 Colors.red/red[400]/red[700]/redAccent처럼
|
// 🚨 경고성 색상 3종 - 예전엔 화면마다 Colors.red/red[400]/red[700]/redAccent처럼
|
||||||
// 제각각 써서 같은 의미인데 색이 미묘하게 달랐다. 이제 의미별로 딱 하나씩만 쓴다.
|
// 제각각 써서 같은 의미인데 색이 미묘하게 달랐다. 이제 의미별로 딱 하나씩만 쓴다.
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// 🌐 지금 장착된 프로필 테마를 앱 전체에 퍼뜨리는 통로.
|
||||||
|
// AppPalette의 구조색(mist/paper/linen/sage)은 전역 static 값이라 바꾸면 되지만,
|
||||||
|
// TitlePill처럼 "실시간으로" 다시 그려져야 하는 공유 위젯은 구독할 대상이
|
||||||
|
// 따로 필요해서 ValueNotifier를 하나 더 둔다. 템플릿을 장착/해제할 때는
|
||||||
|
// AppPalette를 직접 건드리지 말고 꼭 이 함수를 통해서만 바꾼다.
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'app_palette.dart';
|
||||||
|
import 'profile_themes.dart';
|
||||||
|
|
||||||
|
final ValueNotifier<ProfileTheme?> globalProfileTheme = ValueNotifier(null);
|
||||||
|
|
||||||
|
void applyGlobalProfileTheme(ProfileTheme? theme) {
|
||||||
|
AppPalette.applyTemplateTheme(theme);
|
||||||
|
globalProfileTheme.value = theme;
|
||||||
|
}
|
||||||
@@ -131,7 +131,7 @@ class _NoticeBannerState extends State<_NoticeBanner>
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.message,
|
widget.message,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: AppPalette.paper,
|
color: AppPalette.paper,
|
||||||
fontSize: 13.5,
|
fontSize: 13.5,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ class _DeviceCheckoutLedgerPageState extends State<DeviceCheckoutLedgerPage> {
|
|||||||
? const BorderSide(
|
? const BorderSide(
|
||||||
color: AppPalette.warning,
|
color: AppPalette.warning,
|
||||||
)
|
)
|
||||||
: const BorderSide(
|
: BorderSide(
|
||||||
color: AppPalette.sage,
|
color: AppPalette.sage,
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ class _GroupDetailScreenState extends State<GroupDetailScreen> {
|
|||||||
label: const Text('백품타 바로가기'),
|
label: const Text('백품타 바로가기'),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: AppPalette.ink,
|
foregroundColor: AppPalette.ink,
|
||||||
side: const BorderSide(color: AppPalette.sage),
|
side: BorderSide(color: AppPalette.sage),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import '../function/student_dashboard_controller.dart';
|
|||||||
import '../models/dashboard_tile_layout.dart';
|
import '../models/dashboard_tile_layout.dart';
|
||||||
import '../theme/app_palette.dart';
|
import '../theme/app_palette.dart';
|
||||||
import '../theme/profile_themes.dart';
|
import '../theme/profile_themes.dart';
|
||||||
|
import '../theme/theme_sync.dart';
|
||||||
import 'admin_dashboard.dart';
|
import 'admin_dashboard.dart';
|
||||||
import 'app_notice.dart';
|
import 'app_notice.dart';
|
||||||
import 'board_home_screen.dart';
|
import 'board_home_screen.dart';
|
||||||
@@ -94,6 +95,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
|||||||
|
|
||||||
Future<void> _loadEquippedTheme() async {
|
Future<void> _loadEquippedTheme() async {
|
||||||
final theme = await ProfileShopController.fetchEquippedTheme(_displayId);
|
final theme = await ProfileShopController.fetchEquippedTheme(_displayId);
|
||||||
|
applyGlobalProfileTheme(theme);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_equippedTheme = theme;
|
_equippedTheme = theme;
|
||||||
@@ -808,8 +810,8 @@ class _MainDashboardState extends State<MainDashboard> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isEditing)
|
if (isEditing)
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 8, left: 24, right: 24),
|
padding: const EdgeInsets.only(top: 8, left: 24, right: 24),
|
||||||
child: Text(
|
child: Text(
|
||||||
'타일을 드래그해서 옮기고, 오른쪽 아래 손잡이로 크기를 바꿔보세요.',
|
'타일을 드래그해서 옮기고, 오른쪽 아래 손잡이로 크기를 바꿔보세요.',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@@ -969,7 +971,7 @@ class _MainDashboardState extends State<MainDashboard> {
|
|||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.open_in_full_rounded,
|
Icons.open_in_full_rounded,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: AppPalette.paper,
|
color: AppPalette.paper,
|
||||||
@@ -1131,7 +1133,7 @@ class _DashboardMenuCard extends StatelessWidget {
|
|||||||
label: '로그아웃',
|
label: '로그아웃',
|
||||||
onTap: onLogout,
|
onTap: onLogout,
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: AppPalette.sage),
|
Divider(height: 1, color: AppPalette.sage),
|
||||||
_MenuRow(
|
_MenuRow(
|
||||||
icon: Icons.settings_outlined,
|
icon: Icons.settings_outlined,
|
||||||
label: '설정',
|
label: '설정',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import '../function/profile_shop_controller.dart';
|
import '../function/profile_shop_controller.dart';
|
||||||
import '../theme/app_palette.dart';
|
import '../theme/app_palette.dart';
|
||||||
import '../theme/profile_themes.dart';
|
import '../theme/profile_themes.dart';
|
||||||
|
import '../theme/theme_sync.dart';
|
||||||
import 'app_notice.dart';
|
import 'app_notice.dart';
|
||||||
import 'title_pill.dart';
|
import 'title_pill.dart';
|
||||||
|
|
||||||
@@ -35,7 +36,21 @@ class _ProfileCustomizeScreenState extends State<ProfileCustomizeScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller.loadCatalog(widget.studentId);
|
_initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 상점 화면을 열 때마다 서버의 실제 장착 상태로 전역 테마를 다시 맞춰둔다
|
||||||
|
/// (딴 데서 장착 상태가 바뀌었을 수도 있는 엣지 케이스 보호용).
|
||||||
|
Future<void> _initialize() async {
|
||||||
|
await _controller.loadCatalog(widget.studentId);
|
||||||
|
if (!mounted) return;
|
||||||
|
for (final t in _controller.templates) {
|
||||||
|
if (t.equipped) {
|
||||||
|
_syncGlobalTheme(t.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_syncGlobalTheme(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -88,6 +103,7 @@ class _ProfileCustomizeScreenState extends State<ProfileCustomizeScreen> {
|
|||||||
widget.studentId,
|
widget.studentId,
|
||||||
templateId,
|
templateId,
|
||||||
);
|
);
|
||||||
|
if (success) _syncGlobalTheme(templateId);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
AppNotice.show(
|
AppNotice.show(
|
||||||
context,
|
context,
|
||||||
@@ -96,6 +112,21 @@ class _ProfileCustomizeScreenState extends State<ProfileCustomizeScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 💡 커스텀창 안에서의 "미리보기"와 달리, 실제로 장착이 끝난 뒤에는 앱 전체
|
||||||
|
/// (TitlePill, 다른 화면의 배경/카드색)도 같이 바뀌도록 전역에 반영한다.
|
||||||
|
void _syncGlobalTheme(int? equippedTemplateId) {
|
||||||
|
if (equippedTemplateId == null) {
|
||||||
|
applyGlobalProfileTheme(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (final t in _controller.templates) {
|
||||||
|
if (t.id == equippedTemplateId) {
|
||||||
|
applyGlobalProfileTheme(profileThemes[t.code]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
|
|||||||
import '../function/browser_notification.dart';
|
import '../function/browser_notification.dart';
|
||||||
import '../function/study_timer_controller.dart';
|
import '../function/study_timer_controller.dart';
|
||||||
import '../theme/app_palette.dart';
|
import '../theme/app_palette.dart';
|
||||||
|
import '../theme/theme_sync.dart';
|
||||||
import 'app_notice.dart';
|
import 'app_notice.dart';
|
||||||
import 'launchpad_transition.dart';
|
import 'launchpad_transition.dart';
|
||||||
import 'study_preset_screen.dart';
|
import 'study_preset_screen.dart';
|
||||||
@@ -117,6 +118,10 @@ class _StudyTimerScreenState extends State<StudyTimerScreen>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🛍️ 장착한 템플릿의 강조색 - 시작/재개처럼 "중립" 상태의 버튼/텍스트에만 쓴다.
|
||||||
|
// 일시정지(warning)/종료(danger) 같은 경고성 색은 의미가 있어서 그대로 둔다.
|
||||||
|
Color get _accentColor => globalProfileTheme.value?.accent ?? AppPalette.ink;
|
||||||
|
|
||||||
String _formatHMS(int totalSeconds) {
|
String _formatHMS(int totalSeconds) {
|
||||||
final h = totalSeconds ~/ 3600;
|
final h = totalSeconds ~/ 3600;
|
||||||
final m = (totalSeconds % 3600) ~/ 60;
|
final m = (totalSeconds % 3600) ~/ 60;
|
||||||
@@ -430,7 +435,7 @@ class _StudyTimerScreenState extends State<StudyTimerScreen>
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 52,
|
fontSize: 52,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: isPaused ? Colors.grey : AppPalette.ink,
|
color: isPaused ? Colors.grey : _accentColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 36),
|
const SizedBox(height: 36),
|
||||||
@@ -471,7 +476,7 @@ class _StudyTimerScreenState extends State<StudyTimerScreen>
|
|||||||
size: 140,
|
size: 140,
|
||||||
icon: Icons.play_arrow_rounded,
|
icon: Icons.play_arrow_rounded,
|
||||||
label: _controller.pomodoroEnabled ? '뽀모도로 시작' : '공부 시작',
|
label: _controller.pomodoroEnabled ? '뽀모도로 시작' : '공부 시작',
|
||||||
color: AppPalette.ink,
|
color: _accentColor,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
requestNotificationPermission();
|
requestNotificationPermission();
|
||||||
_runAction(_controller.start);
|
_runAction(_controller.start);
|
||||||
@@ -495,7 +500,7 @@ class _StudyTimerScreenState extends State<StudyTimerScreen>
|
|||||||
size: 110,
|
size: 110,
|
||||||
icon: isPaused ? Icons.play_arrow_rounded : Icons.pause_rounded,
|
icon: isPaused ? Icons.play_arrow_rounded : Icons.pause_rounded,
|
||||||
label: isPaused ? '재개' : '일시정지',
|
label: isPaused ? '재개' : '일시정지',
|
||||||
color: isPaused ? AppPalette.ink : AppPalette.warning,
|
color: isPaused ? _accentColor : AppPalette.warning,
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
_runAction(isPaused ? _controller.resume : _controller.pause),
|
_runAction(isPaused ? _controller.resume : _controller.pause),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -921,25 +921,25 @@ class _TimeSettingsMenuCard extends StatelessWidget {
|
|||||||
label: '새로고침',
|
label: '새로고침',
|
||||||
onTap: () => onPick('refresh'),
|
onTap: () => onPick('refresh'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: AppPalette.sage),
|
Divider(height: 1, color: AppPalette.sage),
|
||||||
_TimeMenuRow(
|
_TimeMenuRow(
|
||||||
icon: Icons.access_time_rounded,
|
icon: Icons.access_time_rounded,
|
||||||
label: attendanceTimeLabel,
|
label: attendanceTimeLabel,
|
||||||
onTap: () => onPick('attendance_time'),
|
onTap: () => onPick('attendance_time'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: AppPalette.sage),
|
Divider(height: 1, color: AppPalette.sage),
|
||||||
_TimeMenuRow(
|
_TimeMenuRow(
|
||||||
icon: Icons.timer_outlined,
|
icon: Icons.timer_outlined,
|
||||||
label: '반출 허용 시간 설정',
|
label: '반출 허용 시간 설정',
|
||||||
onTap: () => onPick('permission_window'),
|
onTap: () => onPick('permission_window'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: AppPalette.sage),
|
Divider(height: 1, color: AppPalette.sage),
|
||||||
_TimeMenuRow(
|
_TimeMenuRow(
|
||||||
icon: Icons.school_rounded,
|
icon: Icons.school_rounded,
|
||||||
label: '하교 처리',
|
label: '하교 처리',
|
||||||
onTap: () => onPick('dismissal'),
|
onTap: () => onPick('dismissal'),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: AppPalette.sage),
|
Divider(height: 1, color: AppPalette.sage),
|
||||||
_TimeMenuRow(
|
_TimeMenuRow(
|
||||||
icon: Icons.bug_report_outlined,
|
icon: Icons.bug_report_outlined,
|
||||||
label: '테스트용: 허용시간 초기화',
|
label: '테스트용: 허용시간 초기화',
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
// 🪶 검정 AppBar 대신 쓰는 둥근 제목 알약. 모든 화면이 똑같은 폰트/크기를 쓰고,
|
// 🪶 검정 AppBar 대신 쓰는 둥근 제목 알약. 모든 화면이 똑같은 폰트/크기를 쓰고,
|
||||||
// 화면 맨 위에 딱 붙어 보이지 않도록 위쪽에 살짝 여백을 둔다.
|
// 화면 맨 위에 딱 붙어 보이지 않도록 위쪽에 살짝 여백을 둔다.
|
||||||
|
// 🛍️ 프로필 꾸미기 템플릿을 장착하면 이 알약 배경도 같이 바뀐다 - 앱 전체가
|
||||||
|
// 공유하는 위젯이라 여기 하나만 고치면 모든 화면의 제목 배경이 한번에 바뀐다.
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../theme/app_palette.dart';
|
import '../theme/app_palette.dart';
|
||||||
|
import '../theme/theme_sync.dart';
|
||||||
|
|
||||||
class TitlePill extends StatelessWidget {
|
class TitlePill extends StatelessWidget {
|
||||||
final String text;
|
final String text;
|
||||||
@@ -10,12 +13,15 @@ class TitlePill extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: globalProfileTheme,
|
||||||
|
builder: (context, theme, _) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 8),
|
padding: const EdgeInsets.only(top: 8),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppPalette.ink,
|
color: theme?.secondary ?? AppPalette.ink,
|
||||||
borderRadius: BorderRadius.circular(999),
|
borderRadius: BorderRadius.circular(999),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -28,5 +34,7 @@ class TitlePill extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user