MotorSignal-App/lib/services/clubs/club_wall_models.dart

234 lines
7.7 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
import 'club_models.dart';
DateTime? _parseDateTime(dynamic value) {
if (value == null) return null;
if (value is Timestamp) return value.toDate();
if (value is DateTime) return value;
if (value is String) return DateTime.tryParse(value);
if (value is int) return DateTime.fromMillisecondsSinceEpoch(value);
return null;
}
dynamic _dateTimeToFirestore(DateTime? dt) {
if (dt == null) return null;
return Timestamp.fromDate(dt);
}
/// Comment on a Club Wall post.
@immutable
class ClubWallComment {
final String id;
final String postId;
final String authorUid;
final String authorDisplayName;
final String? authorHandle;
final String? authorAvatarUrl;
final String? authorDisplayRank;
final ClubRole authorRole;
final String content;
final DateTime createdAt;
const ClubWallComment({
required this.id,
required this.postId,
required this.authorUid,
required this.authorDisplayName,
this.authorHandle,
this.authorAvatarUrl,
this.authorDisplayRank,
this.authorRole = ClubRole.member,
required this.content,
required this.createdAt,
});
Map<String, dynamic> toJson() => {
'id': id,
'postId': postId,
'authorUid': authorUid,
'authorDisplayName': authorDisplayName,
if (authorHandle != null) 'authorHandle': authorHandle,
if (authorAvatarUrl != null) 'authorAvatarUrl': authorAvatarUrl,
if (authorDisplayRank != null) 'authorDisplayRank': authorDisplayRank,
'authorRole': authorRole.name,
'content': content,
'createdAt': createdAt.toIso8601String(),
};
factory ClubWallComment.fromJson(Map<String, dynamic> json) {
return ClubWallComment(
id: json['id'] as String? ?? '',
postId: json['postId'] as String? ?? '',
authorUid: json['authorUid'] as String? ?? '',
authorDisplayName: json['authorDisplayName'] as String? ?? 'Rider',
authorHandle: json['authorHandle'] as String?,
authorAvatarUrl: json['authorAvatarUrl'] as String?,
authorDisplayRank: json['authorDisplayRank'] as String?,
authorRole: ClubRole.fromString(json['authorRole'] as String?),
content: json['content'] as String? ?? '',
createdAt: _parseDateTime(json['createdAt']) ?? DateTime.now(),
);
}
}
/// Post on a Club Wall discussion board.
@immutable
class ClubWallPost {
final String id;
final String clubId;
final String authorUid;
final String authorDisplayName;
final String? authorHandle;
final String? authorAvatarUrl;
final String? authorDisplayRank;
final ClubRole authorRole;
final String content;
final DateTime createdAt;
final bool isPinned;
final int fistBumpCount;
final List<String> fistBumpUserIds;
final List<ClubWallComment> comments;
const ClubWallPost({
required this.id,
required this.clubId,
required this.authorUid,
required this.authorDisplayName,
this.authorHandle,
this.authorAvatarUrl,
this.authorDisplayRank,
this.authorRole = ClubRole.member,
required this.content,
required this.createdAt,
this.isPinned = false,
this.fistBumpCount = 0,
this.fistBumpUserIds = const [],
this.comments = const [],
});
bool isFistBumpedBy(String? uid) {
if (uid == null) return false;
return fistBumpUserIds.contains(uid);
}
ClubWallPost copyWith({
String? id,
String? clubId,
String? authorUid,
String? authorDisplayName,
String? authorHandle,
String? authorAvatarUrl,
String? authorDisplayRank,
ClubRole? authorRole,
String? content,
DateTime? createdAt,
bool? isPinned,
int? fistBumpCount,
List<String>? fistBumpUserIds,
List<ClubWallComment>? comments,
}) {
return ClubWallPost(
id: id ?? this.id,
clubId: clubId ?? this.clubId,
authorUid: authorUid ?? this.authorUid,
authorDisplayName: authorDisplayName ?? this.authorDisplayName,
authorHandle: authorHandle ?? this.authorHandle,
authorAvatarUrl: authorAvatarUrl ?? this.authorAvatarUrl,
authorDisplayRank: authorDisplayRank ?? this.authorDisplayRank,
authorRole: authorRole ?? this.authorRole,
content: content ?? this.content,
createdAt: createdAt ?? this.createdAt,
isPinned: isPinned ?? this.isPinned,
fistBumpCount: fistBumpCount ?? this.fistBumpCount,
fistBumpUserIds: fistBumpUserIds ?? this.fistBumpUserIds,
comments: comments ?? this.comments,
);
}
Map<String, dynamic> toFirestore() => {
'id': id,
'clubId': clubId,
'authorUid': authorUid,
'authorDisplayName': authorDisplayName,
if (authorHandle != null) 'authorHandle': authorHandle,
if (authorAvatarUrl != null) 'authorAvatarUrl': authorAvatarUrl,
if (authorDisplayRank != null) 'authorDisplayRank': authorDisplayRank,
'authorRole': authorRole.name,
'content': content,
'createdAt': _dateTimeToFirestore(createdAt),
'isPinned': isPinned,
'fistBumpCount': fistBumpCount,
'fistBumpUserIds': fistBumpUserIds,
'comments': comments.map((c) => c.toJson()).toList(),
};
Map<String, dynamic> toJson() => {
'id': id,
'clubId': clubId,
'authorUid': authorUid,
'authorDisplayName': authorDisplayName,
if (authorHandle != null) 'authorHandle': authorHandle,
if (authorAvatarUrl != null) 'authorAvatarUrl': authorAvatarUrl,
if (authorDisplayRank != null) 'authorDisplayRank': authorDisplayRank,
'authorRole': authorRole.name,
'content': content,
'createdAt': createdAt.toIso8601String(),
'isPinned': isPinned,
'fistBumpCount': fistBumpCount,
'fistBumpUserIds': fistBumpUserIds,
'comments': comments.map((c) => c.toJson()).toList(),
};
factory ClubWallPost.fromFirestore(DocumentSnapshot doc) {
final data = doc.data() as Map<String, dynamic>? ?? {};
return ClubWallPost.fromJson(data, idOverride: doc.id);
}
factory ClubWallPost.fromJson(Map<String, dynamic> json, {String? idOverride}) {
final rawComments = json['comments'] as List<dynamic>? ?? [];
final commentsList = rawComments
.map((c) => ClubWallComment.fromJson(Map<String, dynamic>.from(c as Map)))
.toList();
final rawFistBumps = json['fistBumpUserIds'] as List<dynamic>? ?? [];
final fistBumpList = rawFistBumps.map((e) => e.toString()).toList();
return ClubWallPost(
id: idOverride ?? json['id'] as String? ?? '',
clubId: json['clubId'] as String? ?? '',
authorUid: json['authorUid'] as String? ?? '',
authorDisplayName: json['authorDisplayName'] as String? ?? 'Rider',
authorHandle: json['authorHandle'] as String?,
authorAvatarUrl: json['authorAvatarUrl'] as String?,
authorDisplayRank: json['authorDisplayRank'] as String?,
authorRole: ClubRole.fromString(json['authorRole'] as String?),
content: json['content'] as String? ?? '',
createdAt: _parseDateTime(json['createdAt']) ?? DateTime.now(),
isPinned: json['isPinned'] as bool? ?? false,
fistBumpCount: (json['fistBumpCount'] as num?)?.toInt() ?? fistBumpList.length,
fistBumpUserIds: fistBumpList,
comments: commentsList,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ClubWallPost &&
runtimeType == other.runtimeType &&
id == other.id &&
clubId == other.clubId &&
content == other.content &&
isPinned == other.isPinned &&
fistBumpCount == other.fistBumpCount;
@override
int get hashCode =>
id.hashCode ^
clubId.hashCode ^
content.hashCode ^
isPinned.hashCode ^
fistBumpCount.hashCode;
}