Files
fanuzu/app/lib/features/campaign/campaign_share.dart
T

84 lines
3.6 KiB
Dart

/// 캠페인 공유 문구 조립 — 「홈/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');
}