QUEST · CAMPAIGN 화면 — 퀘스트 참여·생성·인증 · 캠페인 목록·상세·후원·랭킹
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/// 캠페인 공유 문구 조립 — 「홈/HOT ARCHIVE/공유」 · Figma `480:14594`.
|
||||
///
|
||||
/// 조립을 화면 밖에 둔 이유는 정책 원장 **B-78** 이다. 해시태그를 콘텐츠에 따라
|
||||
/// 동적으로 구성하라고 확정했고 검증 기준이 *고정 3종 하드코딩 0건* 이므로,
|
||||
/// 규칙을 한 곳에 두고 테스트로 고정한다. 화면 안에 문자열을 이어 붙이면
|
||||
/// 캠페인과 퀘스트에서 규칙이 갈린다.
|
||||
library;
|
||||
|
||||
import '../../core/data/invite_code.dart';
|
||||
import '../../core/i18n/app_locale.dart';
|
||||
|
||||
const String shareBrand = 'FANUZU';
|
||||
|
||||
/// 웹 브릿지 주소 — 빌드 설정(`--dart-define=FANUZU_SHARE_BASE_URL=https://…`).
|
||||
///
|
||||
/// 도메인은 해석 확인 요청 「공유 · 웹 브릿지」 3-1 회신 뒤 정해진다. 설정이 없으면 링크 없이
|
||||
/// 문구만 보낸다 — 살아 있지 않은 주소를 사용자 손에 쥐여 주지 않는다.
|
||||
const String shareBaseUrl = String.fromEnvironment('FANUZU_SHARE_BASE_URL');
|
||||
|
||||
/// 공유 링크 한 곳 — 경로는 프로토타입 구조(`/b/<팬덤>/<날짜>` · `/c/<캠페인>`)를 따른다.
|
||||
/// 브리핑 · 캠페인(HOT ARCHIVE) 공유가 모두 여기를 지난다. 내 초대 코드([InviteCode])를
|
||||
/// 받아 두었으면 `?ref=<코드>` 를 붙이고, 없으면 붙이지 않는다(공유를 막지 않는다).
|
||||
String? shareLink(
|
||||
List<String> segments, {
|
||||
String base = shareBaseUrl,
|
||||
String? ref,
|
||||
}) {
|
||||
if (base.isEmpty || segments.any((s) => s.isEmpty)) return null;
|
||||
final root = base.endsWith('/') ? base.substring(0, base.length - 1) : base;
|
||||
final code = ref ?? InviteCode.current;
|
||||
final query = code == null || code.isEmpty
|
||||
? ''
|
||||
: '?ref=${Uri.encodeQueryComponent(code)}';
|
||||
return '$root/${segments.map(Uri.encodeComponent).join('/')}$query';
|
||||
}
|
||||
|
||||
/// 캠페인 상세로 돌아오는 링크 `/c/<campaignId>`.
|
||||
String? campaignShareLink(String campaignId, {String base = shareBaseUrl}) =>
|
||||
shareLink(['c', campaignId], base: base);
|
||||
|
||||
/// 해시태그를 조립한다. 최대 [maxHashtags] 개다.
|
||||
///
|
||||
/// B-78 — *브랜드 · Artist/Fandom · Campaign/Quest 등을 콘텐츠에 따라 최대 약
|
||||
/// 3 개로 동적 구성합니다.* 세 자리를 채우는 것이 아니라 **있는 것만** 넣는다.
|
||||
/// 팬덤을 모르는 캠페인은 두 개가 되고, 그것이 「고정 3종」이 아니라는 증거다.
|
||||
List<String> shareHashtags({
|
||||
String brand = shareBrand,
|
||||
String? fandomName,
|
||||
String? topic,
|
||||
int maxHashtags = 3,
|
||||
}) {
|
||||
final tags = <String>[
|
||||
for (final value in [brand, fandomName, topic])
|
||||
if (value != null && value.trim().isNotEmpty) '#${_tag(value)}',
|
||||
];
|
||||
return tags.take(maxHashtags).toList();
|
||||
}
|
||||
|
||||
/// 해시태그 한 조각. 공백과 해시는 태그를 끊으므로 지운다.
|
||||
String _tag(String value) => value.trim().replaceAll(RegExp(r'[\s#]+'), '');
|
||||
|
||||
/// 캠페인 공유 문구.
|
||||
///
|
||||
/// 제목과 한 줄 소개, 행성명, 링크(있을 때), 해시태그로 끝난다. 소개가 비어 있으면 그 줄을
|
||||
/// 빼고, 행성명을 모르면 그 줄과 팬덤 해시태그가 함께 빠진다.
|
||||
String campaignShareText({
|
||||
required String title,
|
||||
String? summary,
|
||||
String? fandomName,
|
||||
String? topic,
|
||||
String? link,
|
||||
}) {
|
||||
topic ??= appL10n.shareTopicCampaign;
|
||||
final lines = <String>[
|
||||
title.trim(),
|
||||
if (summary != null && summary.trim().isNotEmpty) summary.trim(),
|
||||
if (fandomName != null && fandomName.trim().isNotEmpty)
|
||||
appL10n.sharePlanetLine(shareBrand, fandomName.trim()),
|
||||
?link,
|
||||
shareHashtags(fandomName: fandomName, topic: topic).join(' '),
|
||||
];
|
||||
return lines.where((l) => l.isNotEmpty).join('\n');
|
||||
}
|
||||
Reference in New Issue
Block a user