234 lines
7.7 KiB
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;
|
|
}
|