Polish: daily reward via celebration overlay + premium chat to recipient
- Daily reward now routes through the global CelebrationOverlay: new "daily"
variant + coins count-up; claiming closes the daily modal and fires
celebrate({variant:"daily", coins}). Unifies the "you earned X" moment.
- Premium (pro) gold chat is now visible to the OTHER player: ChatMessage gains
senderPro; server resolves each participant's plan once (SocialService.IsPro)
and stamps it on ChatMessageDto; ChatScreen styles incoming bubbles with
.premium-chat when senderPro. Mock marks ~half its friends pro so it's visible
offline too.
Verified: tsc + next build + dotnet build all pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ public class ChatMessageDto
|
||||
public bool FromMe { get; set; }
|
||||
public string Text { get; set; } = "";
|
||||
public long Ts { get; set; }
|
||||
public bool SenderPro { get; set; }
|
||||
}
|
||||
|
||||
public class ConversationDto
|
||||
|
||||
@@ -265,7 +265,9 @@ public class SocialService
|
||||
.OrderBy(m => m.CreatedAt).ToListAsync();
|
||||
var unread = msgs.Where(m => m.UserId == peerId && m.PeerId == uid && !m.ReadByPeer).ToList();
|
||||
if (unread.Count > 0) { unread.ForEach(m => m.ReadByPeer = true); await _db.SaveChangesAsync(); }
|
||||
return msgs.Select(m => ToDto(m, uid)).ToList();
|
||||
// Resolve each participant's plan once so premium (pro) senders show gold.
|
||||
bool uidPro = await IsPro(uid), peerPro = await IsPro(peerId);
|
||||
return msgs.Select(m => ToDto(m, uid, m.UserId == uid ? uidPro : peerPro)).ToList();
|
||||
}
|
||||
|
||||
public async Task<ChatMessageDto> Send(string uid, string peerId, string text)
|
||||
@@ -274,14 +276,24 @@ public class SocialService
|
||||
_db.Messages.Add(m);
|
||||
await _db.SaveChangesAsync();
|
||||
await _hub.Clients.User(peerId).SendAsync("chat", new { peerId = uid });
|
||||
return ToDto(m, uid);
|
||||
return ToDto(m, uid, await IsPro(uid));
|
||||
}
|
||||
|
||||
private static ChatMessageDto ToDto(MessageRow m, string uid) => new()
|
||||
/// <summary>True when the user has an active premium (pro) plan.</summary>
|
||||
private async Task<bool> IsPro(string userId)
|
||||
{
|
||||
var row = await _db.Profiles.FindAsync(userId);
|
||||
if (row == null) return false;
|
||||
var p = JsonSerializer.Deserialize<ProfileDto>(row.Json, JsonOpts.Default);
|
||||
return p?.Plan == "pro";
|
||||
}
|
||||
|
||||
private static ChatMessageDto ToDto(MessageRow m, string uid, bool senderPro = false) => new()
|
||||
{
|
||||
Id = m.Id.ToString(),
|
||||
FromMe = m.UserId == uid,
|
||||
Text = m.Text,
|
||||
Ts = new DateTimeOffset(m.CreatedAt).ToUnixTimeMilliseconds(),
|
||||
SenderPro = senderPro,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user