import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'test_utils.dart'; /// URL 관련 유틸리티 기능을 제공하는 클래스 class UrlUtils { /// 이벤트 공개 URL의 기본 도메인 (프로덕션) static const String eventBaseUrl = 'https://lanebow.com/events/'; /// 공개 URL 해시 생성 /// /// 8자리 영숫자 해시를 생성합니다. /// 보안 및 충돌 방지를 위해 충분한 길이와 랜덤성을 가집니다. /// 테스트 환경에서는 예측 가능한 해시를 반환합니다. static String generatePublicHash() { // 테스트 환경에서는 예측 가능한 해시 반환 if (TestUtils.isInTest) { return TestUtils.getTestHash(); } // 일반 환경에서는 랜덤 해시 생성 const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; final random = Random(); return String.fromCharCodes( Iterable.generate( 8, (_) => chars.codeUnitAt(random.nextInt(chars.length)), ), ); } /// 이벤트 공개 URL 생성 /// /// [hash] 값을 기반으로 완전한 이벤트 공개 URL을 생성합니다. static String getEventPublicUrl(String hash) { return '$eventBaseUrl$hash'; } /// URL을 클립보드에 복사하고 스낵바로 알림 /// /// [context]는 스낵바를 표시할 BuildContext입니다. /// [url]은 복사할 URL 문자열입니다. /// [duration]은 스낵바 표시 시간입니다. 기본값은 2초입니다. static void copyUrlToClipboard(BuildContext context, String url, {Duration? duration}) { Clipboard.setData(ClipboardData(text: url)); // 테스트 환경에서 안전하게 스낵바 표시 if (TestUtils.isInTest) { // 테스트 환경에서는 로그만 출력 debugPrint('클립보드에 URL 복사됨: $url'); return; } // 일반 환경에서는 스낵바 표시 SchedulerBinding.instance.addPostFrameCallback((_) { if (context.mounted) { final snackBar = SnackBar( content: const Text('공개 URL이 클립보드에 복사되었습니다'), action: SnackBarAction( label: '확인', onPressed: () {}, ), duration: duration ?? const Duration(seconds: 2), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); } }); } /// 해시 생성/재생성 후 스낵바로 알림 /// /// [context]는 스낵바를 표시할 BuildContext입니다. /// [isNew]는 새로 생성된 해시인지 여부입니다. true면 생성, false면 재생성 메시지를 표시합니다. /// [hash]는 생성된 해시 값입니다. /// /// 주의: 이 메서드는 더 이상 직접 사용하지 않습니다. 테스트 환경에서 문제가 발생할 수 있습니다. /// 대신 각 화면에서 직접 스낵바를 표시하는 것이 좋습니다. @Deprecated('테스트 환경에서 문제가 발생할 수 있습니다. 대신 각 화면에서 직접 스낵바를 표시하세요.') static void showHashGenerationSnackbar(BuildContext context, bool isNew, String hash) { final message = isNew ? '공개 URL 해시가 생성되었습니다' : '공개 URL 해시가 재생성되었습니다. 이전 URL은 더 이상 유효하지 않습니다'; final url = getEventPublicUrl(hash); // 테스트 환경에서는 항상 로그만 출력 if (TestUtils.isInTest) { debugPrint('해시 생성 완료: $message, URL: $url'); return; } // 일반 환경에서는 스낵바 표시 (context가 유효한지 확인) if (context.mounted) { SchedulerBinding.instance.addPostFrameCallback((_) { if (context.mounted) { try { final snackBar = SnackBar( content: Text(message), action: SnackBarAction( label: 'URL 복사', onPressed: () => copyUrlToClipboard(context, url), ), duration: const Duration(seconds: 4), ); ScaffoldMessenger.of(context).showSnackBar(snackBar); } catch (e) { debugPrint('스낵바 표시 오류: $e'); } } }); } } }