- ScheduleDetail 모델: broadcaster, replayUrl, varietyThumbnailUrl 필드 추가 - 예능 섹션: 썸네일(블러 배경 + contain) + 정보 카드 - 방송사 뱃지(카테고리 색상), 날짜/시간, 제목, 멤버, 다시보기 버튼 - 썸네일 없을 때 카테고리 색상 배경 + Tv 아이콘 표시 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
215 lines
6 KiB
Dart
215 lines
6 KiB
Dart
/// 일정 모델
|
|
library;
|
|
|
|
/// 멤버 정보
|
|
class ScheduleMember {
|
|
final int id;
|
|
final String name;
|
|
|
|
ScheduleMember({required this.id, required this.name});
|
|
|
|
factory ScheduleMember.fromJson(Map<String, dynamic> json) {
|
|
return ScheduleMember(
|
|
id: json['id'] as int,
|
|
name: json['name'] as String,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 관련 일정 (콘서트 회차 등)
|
|
class RelatedDate {
|
|
final int id;
|
|
final String date;
|
|
final String? time;
|
|
|
|
RelatedDate({required this.id, required this.date, this.time});
|
|
|
|
factory RelatedDate.fromJson(Map<String, dynamic> json) {
|
|
return RelatedDate(
|
|
id: json['id'] as int,
|
|
date: json['date'] as String,
|
|
time: json['time'] as String?,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 일정 상세 모델
|
|
class ScheduleDetail {
|
|
final int id;
|
|
final String title;
|
|
final String date;
|
|
final String? time;
|
|
final int? categoryId;
|
|
final String? categoryName;
|
|
final String? categoryColor;
|
|
final List<ScheduleMember> members;
|
|
// YouTube 관련
|
|
final String? channelName;
|
|
final String? videoId;
|
|
final String? videoType;
|
|
final String? videoUrl;
|
|
final String? bannerUrl;
|
|
// X 관련
|
|
final String? postId;
|
|
final String? username;
|
|
final String? content;
|
|
final List<String> imageUrls;
|
|
final String? postUrl;
|
|
// X 프로필
|
|
final String? profileDisplayName;
|
|
final String? profileAvatarUrl;
|
|
// 예능 관련
|
|
final String? broadcaster;
|
|
final String? replayUrl;
|
|
final String? varietyThumbnailUrl;
|
|
|
|
ScheduleDetail({
|
|
required this.id,
|
|
required this.title,
|
|
required this.date,
|
|
this.time,
|
|
this.categoryId,
|
|
this.categoryName,
|
|
this.categoryColor,
|
|
this.members = const [],
|
|
this.channelName,
|
|
this.videoId,
|
|
this.videoType,
|
|
this.videoUrl,
|
|
this.bannerUrl,
|
|
this.postId,
|
|
this.username,
|
|
this.content,
|
|
this.imageUrls = const [],
|
|
this.postUrl,
|
|
this.profileDisplayName,
|
|
this.profileAvatarUrl,
|
|
this.broadcaster,
|
|
this.replayUrl,
|
|
this.varietyThumbnailUrl,
|
|
});
|
|
|
|
factory ScheduleDetail.fromJson(Map<String, dynamic> json) {
|
|
// category 중첩 객체 파싱
|
|
final category = json['category'] as Map<String, dynamic>?;
|
|
|
|
return ScheduleDetail(
|
|
id: json['id'] as int,
|
|
title: json['title'] as String,
|
|
date: json['date'] as String,
|
|
time: json['time'] as String?,
|
|
categoryId: category?['id'] as int?,
|
|
categoryName: category?['name'] as String?,
|
|
categoryColor: category?['color'] as String?,
|
|
members: (json['members'] as List<dynamic>?)
|
|
?.map((m) => ScheduleMember.fromJson(m))
|
|
.toList() ??
|
|
[],
|
|
channelName: json['channelName'] as String?,
|
|
videoId: json['videoId'] as String?,
|
|
videoType: json['videoType'] as String?,
|
|
videoUrl: json['videoUrl'] as String?,
|
|
bannerUrl: json['bannerUrl'] as String?,
|
|
postId: json['postId'] as String?,
|
|
username: json['username'] as String?,
|
|
content: json['content'] as String?,
|
|
imageUrls: (json['imageUrls'] as List<dynamic>?)?.cast<String>() ?? [],
|
|
postUrl: json['postUrl'] as String?,
|
|
profileDisplayName: (json['profile'] as Map<String, dynamic>?)?['displayName'] as String?,
|
|
profileAvatarUrl: (json['profile'] as Map<String, dynamic>?)?['avatarUrl'] as String?,
|
|
broadcaster: json['broadcaster'] as String?,
|
|
replayUrl: json['replayUrl'] as String?,
|
|
varietyThumbnailUrl: json['thumbnailUrl'] as String?,
|
|
);
|
|
}
|
|
|
|
/// 시간 포맷 (HH:mm)
|
|
String? get formattedTime {
|
|
if (time == null) return null;
|
|
return time!.length >= 5 ? time!.substring(0, 5) : time;
|
|
}
|
|
}
|
|
|
|
class Schedule {
|
|
/// ID (일반 일정: int, 생일/기념일: String)
|
|
final dynamic id;
|
|
final String title;
|
|
final String date;
|
|
final String? time;
|
|
final int? categoryId;
|
|
final String? categoryName;
|
|
final String? categoryColor;
|
|
final List<String> members;
|
|
final String? sourceName;
|
|
final String? sourceUrl;
|
|
// 특별 일정 필드
|
|
final bool isBirthday;
|
|
final bool isDebut;
|
|
final bool isAnniversary;
|
|
final String? memberImage;
|
|
final int? anniversaryYear;
|
|
|
|
Schedule({
|
|
required this.id,
|
|
required this.title,
|
|
required this.date,
|
|
this.time,
|
|
this.categoryId,
|
|
this.categoryName,
|
|
this.categoryColor,
|
|
this.members = const [],
|
|
this.sourceName,
|
|
this.sourceUrl,
|
|
this.isBirthday = false,
|
|
this.isDebut = false,
|
|
this.isAnniversary = false,
|
|
this.memberImage,
|
|
this.anniversaryYear,
|
|
});
|
|
|
|
factory Schedule.fromJson(Map<String, dynamic> json) {
|
|
// category 중첩 객체 파싱
|
|
final category = json['category'] as Map<String, dynamic>?;
|
|
|
|
// members 배열 파싱
|
|
final membersList = (json['members'] as List<dynamic>?)
|
|
?.map((m) => m is String ? m : m.toString())
|
|
.toList() ?? [];
|
|
|
|
// source 중첩 객체 파싱
|
|
final source = json['source'] as Map<String, dynamic>?;
|
|
|
|
return Schedule(
|
|
id: json['id'], // int 또는 String (생일/기념일)
|
|
title: json['title'] as String,
|
|
date: json['date'] as String,
|
|
time: json['time'] as String?,
|
|
categoryId: category?['id'] as int?,
|
|
categoryName: category?['name'] as String?,
|
|
categoryColor: category?['color'] as String?,
|
|
members: membersList,
|
|
sourceName: source?['name'] as String?,
|
|
sourceUrl: source?['url'] as String?,
|
|
isBirthday: json['is_birthday'] == true,
|
|
isDebut: json['is_debut'] == true,
|
|
isAnniversary: json['is_anniversary'] == true,
|
|
memberImage: json['member_image'] as String?,
|
|
anniversaryYear: (json['anniversary_year'] as num?)?.toInt(),
|
|
);
|
|
}
|
|
|
|
/// 특별 일정 여부 (생일, 데뷔, 기념일)
|
|
bool get isSpecial => isBirthday || isDebut || isAnniversary;
|
|
|
|
/// 일반 일정의 int ID (특별 일정은 null)
|
|
int? get numericId => id is int ? id : null;
|
|
|
|
/// 멤버 리스트 반환
|
|
List<String> get memberList => members;
|
|
|
|
/// 시간 포맷 (HH:mm)
|
|
String? get formattedTime {
|
|
if (time == null) return null;
|
|
return time!.length >= 5 ? time!.substring(0, 5) : time;
|
|
}
|
|
}
|