Polish: daily reward via celebration overlay + premium chat to recipient
CI/CD / CI - API (dotnet build + engine sim) (push) Has been cancelled
CI/CD / CI - Web (tsc + next build) (push) Has been cancelled
CI/CD / Deploy - local stack (db + server + web) (push) Has been cancelled

- 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:
soroush.asadi
2026-06-06 22:26:28 +03:30
parent 03dfbe1e67
commit 82b2bc0648
8 changed files with 45 additions and 7 deletions
+3 -1
View File
@@ -6,11 +6,13 @@ import { AchievementUnlock } from "./online/types";
/** A queued celebration shown by <CelebrationOverlay/>. */
export interface Celebration {
id: number;
variant: "xp" | "purchase";
variant: "xp" | "purchase" | "daily";
title?: string;
/** emoji/glyph shown big at the top */
icon?: string;
xpGained?: number;
/** coins gained (daily reward, etc.) — shown with a count-up */
coins?: number;
levelBefore?: number;
levelAfter?: number;
achievements?: AchievementUnlock[];
+4
View File
@@ -616,11 +616,14 @@ export class MockOnlineService implements OnlineService {
fromMe: true,
text: text.trim(),
ts: Date.now(),
senderPro: this.profile?.plan === "pro",
};
this.messages[friendId] = [...(this.messages[friendId] ?? []), msg];
this.saveChats();
this.emitChat(friendId);
// deterministic: ~half of mock friends are "pro" so the gold bubble is visible offline
const friendPro = [...friendId].reduce((a, c) => a + c.charCodeAt(0), 0) % 2 === 0;
// simulate a reply from the friend
this.after(randInt(900, 1900), () => {
const reply: ChatMessage = {
@@ -628,6 +631,7 @@ export class MockOnlineService implements OnlineService {
fromMe: false,
text: pick(CANNED_REPLIES),
ts: Date.now(),
senderPro: friendPro,
};
this.messages[friendId] = [...(this.messages[friendId] ?? []), reply];
this.unread[friendId] = (this.unread[friendId] ?? 0) + 1;
+2
View File
@@ -497,6 +497,8 @@ export interface ChatMessage {
fromMe: boolean;
text: string;
ts: number;
/** the sender has a premium (pro) plan → show gold/animated bubble */
senderPro?: boolean;
}
export interface Conversation {