using Nuuru.Server.DTOs.Booru;
using Nuuru.Server.DTOs.Reaction;
using Nuuru.Server.Models;
using Nuuru.Server.Models.Booru;
using Nuuru.Server.Services;
namespace Nuuru.Server.Extensions
{
///
/// Extension methods for mapping between Booru entities and DTOs
///
public static class BooruMappingExtensions
{
///
/// Maps a Tag entity to TagDto
///
public static TagDto ToDto(this Tag tag)
{
if (tag == null) return null;
return new TagDto
{
Id = tag.Id,
Name = tag.Name,
Category = tag.Category?.Slug,
CategoryName = tag.Category?.Name,
CategoryColor = tag.Category?.ColorHex,
PostCount = tag.PostCount,
CreatedAt = tag.CreatedAt
};
}
///
/// Maps an ApplicationUser to UploaderDto
///
public static UploaderDto ToUploaderDto(this Models.ApplicationUser user, UserDisplayInfo? displayInfo = null)
{
if (user == null) return null!;
return new UploaderDto
{
Id = user.Id,
UserName = user.UserName ?? string.Empty,
AvatarUrl = ApplicationUser.GetAvatarUrl(user.UserName, user.AvatarStorageIdentifier),
BackgroundImageUrl = ApplicationUser.GetBackgroundImageUrl(user.UserName, user.BackgroundImageStorageIdentifier),
RoleColor = displayInfo?.RoleColor,
Status = user?.Status,
Badges = displayInfo?.Badges ?? [],
ActiveBanZones = displayInfo?.ActiveBanZones?.Select(z => z.ToString()).ToList() ?? [],
ActiveBans = displayInfo?.ActiveBans ?? [],
ForcedDisplayName = (displayInfo?.BointsEnabled ?? false) ? user.ActiveForcedDisplayName : null,
ClanId = displayInfo?.ClanId,
ClanTag = displayInfo?.ClanTag,
ClanColor = displayInfo?.ClanColor,
ClanBadgeUrl = displayInfo?.ClanBadgeUrl
};
}
///
/// Maps a Post entity to PostDto
///
public static PostDto ToDto(
this Post post,
Dictionary? displayInfoMap = null,
ReactionDTO? reactions = null)
{
if (post == null) return null!;
UserDisplayInfo? uploaderDisplayInfo = null;
if (post.Uploader != null && displayInfoMap != null)
{
displayInfoMap.TryGetValue(post.Uploader.Id, out uploaderDisplayInfo);
}
UserDisplayInfo? approverDisplayInfo = null;
if (post.ApprovedBy != null && displayInfoMap != null)
{
displayInfoMap.TryGetValue(post.ApprovedBy.Id, out approverDisplayInfo);
}
return new PostDto
{
Id = post.Id,
StorageIdentifier = post.StorageIdentifier,
ImageHash = post.ImageHash,
MimeType = post.MimeType,
FileSize = post.FileSize,
OriginalFileName = post.OriginalFileName,
Source = post.Source,
Description = post.Description,
Width = post.Width,
Height = post.Height,
DurationSeconds = post.DurationSeconds,
ThumbnailPath = post.ThumbnailPath,
UploadedAt = post.UploadedAt,
Rating = post.Rating.ToString().ToLowerInvariant(),
Category = post.Category.ToString().ToLowerInvariant(),
IsApproved = post.IsApproved,
ApprovedBy = post.ApprovedBy?.ToUploaderDto(approverDisplayInfo),
ApprovedAt = post.ApprovedAt,
CommentsLocked = post.CommentsLocked,
IsTrashed = post.IsTrashed,
TrashedAt = post.TrashedAt,
TrashReason = post.TrashReason,
IsFeatured = post.IsFeatured,
FeaturedAt = post.FeaturedAt,
HasGoldenFrame = (uploaderDisplayInfo?.BointsEnabled ?? false) && post.HasGoldenFrame,
Uploader = post.Uploader?.ToUploaderDto(uploaderDisplayInfo),
Tags = post.PostTags?.Select(pt => pt.Tag.ToDto()).ToList() ?? new List(),
Reactions = reactions
};
}
///
/// Maps a collection of Tags to TagDtos
///
public static List ToDto(this IEnumerable tags)
{
return tags?.Select(t => t.ToDto()).ToList() ?? new List();
}
///
/// Maps a collection of Posts to PostDtos
///
public static List ToDto(
this IEnumerable posts,
Dictionary? displayInfoMap = null,
Dictionary? reactionsMap = null)
{
return posts?.Select(p =>
{
ReactionDTO? reactions = null;
reactionsMap?.TryGetValue(p.Id, out reactions);
return p.ToDto(displayInfoMap, reactions);
}).ToList() ?? new List();
}
///
/// Maps a Comment entity to CommentDto
///
/// The comment to map
/// If provided, includes raw content only if this user is the author
/// Optional display info map for badge/color lookup
/// Optional reactions map for reaction data
public static CommentDto ToDto(
this Comment comment,
Guid? requestingUserId = null,
Dictionary? displayInfoMap = null,
Dictionary? reactionsMap = null)
{
if (comment == null) return null!;
UserDisplayInfo? authorDisplayInfo = null;
if (comment.User != null && displayInfoMap != null)
{
displayInfoMap.TryGetValue(comment.User.Id, out authorDisplayInfo);
}
ReactionDTO? reactions = null;
reactionsMap?.TryGetValue(comment.Id, out reactions);
return new CommentDto
{
Id = comment.Id,
ContentHtml = comment.ContentHtml,
// Only include raw content for the author (so they can edit)
ContentRaw = requestingUserId.HasValue && comment.UserId == requestingUserId.Value
? comment.ContentRaw
: null,
CreatedAt = comment.CreatedAt,
EditedAt = comment.EditedAt,
Author = comment.User?.ToUploaderDto(authorDisplayInfo),
PostId = comment.PostId,
Reactions = reactions
};
}
///
/// Maps a collection of Comments to CommentDtos
///
public static List ToDto(
this IEnumerable comments,
Guid? requestingUserId = null,
Dictionary? displayInfoMap = null,
Dictionary? reactionsMap = null)
{
return comments?.Select(c => c.ToDto(requestingUserId, displayInfoMap, reactionsMap)).ToList() ?? new List();
}
///
/// Maps a TagHistory entity to TagHistoryDto
///
public static TagHistoryDto ToDto(
this TagHistory history,
Dictionary? displayInfoMap = null,
bool includeSuppressedInfo = false)
{
if (history == null) return null!;
UserDisplayInfo? userDisplayInfo = null;
if (history.User != null && displayInfoMap != null)
{
displayInfoMap.TryGetValue(history.User.Id, out userDisplayInfo);
}
UserDisplayInfo? suppressedByDisplayInfo = null;
if (history.SuppressedBy != null && displayInfoMap != null)
{
displayInfoMap.TryGetValue(history.SuppressedBy.Id, out suppressedByDisplayInfo);
}
return new TagHistoryDto
{
Id = history.Id,
PostId = history.PostId,
User = history.User?.ToUploaderDto(userDisplayInfo)!,
Tags = history.Tags.Split(' ', StringSplitOptions.RemoveEmptyEntries),
DateSet = history.DateSet,
IsSuppressed = history.SuppressedAt.HasValue,
SuppressedAt = includeSuppressedInfo ? history.SuppressedAt : null,
SuppressedBy = includeSuppressedInfo && history.SuppressedBy != null
? history.SuppressedBy.ToUploaderDto(suppressedByDisplayInfo)
: null,
SuppressionReason = includeSuppressedInfo ? history.SuppressionReason : null
};
}
///
/// Maps a SourceHistory entity to SourceHistoryDto
///
public static SourceHistoryDto ToDto(
this SourceHistory history,
Dictionary? displayInfoMap = null,
bool includeSuppressedInfo = false)
{
if (history == null) return null!;
UserDisplayInfo? userDisplayInfo = null;
if (history.User != null && displayInfoMap != null)
{
displayInfoMap.TryGetValue(history.User.Id, out userDisplayInfo);
}
UserDisplayInfo? suppressedByDisplayInfo = null;
if (history.SuppressedBy != null && displayInfoMap != null)
{
displayInfoMap.TryGetValue(history.SuppressedBy.Id, out suppressedByDisplayInfo);
}
return new SourceHistoryDto
{
Id = history.Id,
PostId = history.PostId,
User = history.User?.ToUploaderDto(userDisplayInfo)!,
Source = history.Source,
DateSet = history.DateSet,
IsSuppressed = history.SuppressedAt.HasValue,
SuppressedAt = includeSuppressedInfo ? history.SuppressedAt : null,
SuppressedBy = includeSuppressedInfo && history.SuppressedBy != null
? history.SuppressedBy.ToUploaderDto(suppressedByDisplayInfo)
: null,
SuppressionReason = includeSuppressedInfo ? history.SuppressionReason : null
};
}
}
}