142 lines
4.9 KiB
Dart
142 lines
4.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:share_plus/share_plus.dart';
|
|
|
|
import '../../../core/format.dart';
|
|
import '../../../core/theme/tokens.dart';
|
|
import '../../../core/ui/app_icons.dart';
|
|
import '../my_data.dart';
|
|
import 'my_cards.dart';
|
|
import '../../../core/i18n/app_locale.dart';
|
|
import '../../../core/ui/clipboard_copy.dart';
|
|
import '../../campaign/campaign_share.dart' show shareHashtags;
|
|
|
|
/// 프로필 카드 오버레이 — 시안 `374:15738`(명세 `549:22058`).
|
|
///
|
|
/// 닫기 · 프로필 사진 80 · 닉네임 · 티어 카드 · 통계 카드 · 공유 4종(36 원형 아이콘).
|
|
/// *인스타 공유 · 틱톡 공유 · X 공유 · 링크 복사*
|
|
class ProfileCard extends StatelessWidget {
|
|
const ProfileCard({super.key, required this.profile, required this.onShare});
|
|
|
|
final MyProfile profile;
|
|
final void Function(String target) onShare;
|
|
|
|
/// 공유 대상과 시안 아이콘. 명세가 적은 순서 그대로다.
|
|
/// 프로필 웹 주소가 아직 없어 마지막 칸은 「링크 복사」가 아니라 「문구 복사」다.
|
|
static List<String> get shareTargets => [
|
|
appL10n.shareInstagram,
|
|
appL10n.shareTiktok,
|
|
'X',
|
|
appL10n.shareCopyText,
|
|
];
|
|
static const _shareIcons = [
|
|
'assets/figma/share-instagram.png',
|
|
'assets/figma/share-tiktok.png',
|
|
'assets/figma/share-x.png',
|
|
'assets/figma/share-link.png',
|
|
];
|
|
|
|
ImageProvider<Object> get _photo {
|
|
final url = profile.photoUrl;
|
|
return url == null || url.isEmpty
|
|
? const AssetImage('assets/figma/avatar-sample.png')
|
|
: NetworkImage(url);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 20, 16, 28),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.centerRight,
|
|
child: IconButton(
|
|
tooltip: context.l10n.commonClose,
|
|
onPressed: () => Navigator.of(context).maybePop(),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints.tightFor(width: 24, height: 24),
|
|
icon: const ImageIcon(
|
|
AppIcons.x,
|
|
size: 24,
|
|
color: AppColors.onCosmic,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
CircleAvatar(
|
|
radius: 40,
|
|
backgroundColor: AppColors.cosmicSurfaceRaised,
|
|
backgroundImage: _photo,
|
|
),
|
|
const SizedBox(height: 20),
|
|
Text(profile.nickname, style: myText(20, FontWeight.w600)),
|
|
const SizedBox(height: 24),
|
|
MyTierCard(profile: profile),
|
|
const SizedBox(height: 19),
|
|
MyStatsCard(profile: profile),
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
for (final (i, target) in shareTargets.indexed) ...[
|
|
if (i > 0) const SizedBox(width: 16),
|
|
Semantics(
|
|
button: true,
|
|
label: target,
|
|
child: Tooltip(
|
|
message: target,
|
|
child: GestureDetector(
|
|
onTap: () => onShare(target),
|
|
child: ClipOval(
|
|
child: Container(
|
|
width: 36,
|
|
height: 36,
|
|
color: Colors.black,
|
|
alignment: Alignment.center,
|
|
child: Image.asset(
|
|
_shareIcons[i],
|
|
// X 원본은 21 짜리 글리프라 검정 원 안에 그대로 둔다.
|
|
width: i == 2 ? 21 : 36,
|
|
height: i == 2 ? 21 : 36,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 프로필 공유 문구 — 공유 시트와 복사가 같은 문구를 쓴다.
|
|
String profileShareText(MyProfile profile, [AppLocalizations? l10n]) => [
|
|
(l10n ?? appL10n).myShareText(
|
|
profile.nickname,
|
|
'${profile.userLevel}',
|
|
formatPop(profile.contributedPop),
|
|
),
|
|
shareHashtags().join(' '),
|
|
].join('\n');
|
|
|
|
/// 프로필 카드 공유. 앱별 전용 SDK 없이 OS 공유 시트로 보내고(사용자가 그 안에서
|
|
/// 인스타 · 틱톡 · X 를 고른다), 마지막 칸은 문구를 복사한다. 복사했고 앱이 알려야 하면 true
|
|
/// (Android 13+ 는 시스템이 알린다 — [copyToClipboard]).
|
|
Future<bool> shareProfile(
|
|
MyProfile profile,
|
|
String target, {
|
|
Future<ShareResult> Function(ShareParams)? share,
|
|
}) async {
|
|
final text = profileShareText(profile);
|
|
if (target == ProfileCard.shareTargets.last) {
|
|
var announce = false;
|
|
await copyToClipboard(text, () => announce = true);
|
|
return announce;
|
|
}
|
|
await (share ?? SharePlus.instance.share)(ShareParams(text: text));
|
|
return false;
|
|
}
|