feat(profile): "set your city" gamification box → one-time 500-coin reward
- New searchable city picker (src/lib/iran-cities.ts, ~60 Iranian cities, fa/en search) shown as a gold reward card at the top of the profile Basic tab. - First time a non-empty city is set, the player earns 500 coins (CITY_REWARD), granted server-authoritatively. Collapses to a compact summary afterwards with a "change city" option (no re-reward). - Frontend: UserProfile.city + cityRewardClaimed; mock-service grants on first set; session/service updateProfile accept `city`; celebratory toast + sfx. - Backend (.NET): ProfileDto.City/CityRewardClaimed (JSON blob → no migration); ProfileService.Update grants +500 once and writes a "city" ledger entry. - i18n: city.* keys (fa + en). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -803,3 +803,8 @@ export const DAILY_REWARDS = [100, 150, 200, 300, 400, 600, 1500];
|
||||
export function dailyRewardFor(day: number): number {
|
||||
return DAILY_REWARDS[Math.min(day, DAILY_REWARDS.length) - 1] ?? 100;
|
||||
}
|
||||
|
||||
/* ----------------------- Profile-completion reward ----------------------- */
|
||||
|
||||
/** One-time coin reward for setting your city on the profile. */
|
||||
export const CITY_REWARD = 500;
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ACHIEVEMENTS,
|
||||
CARD_BACKS,
|
||||
CARD_FRONTS,
|
||||
CITY_REWARD,
|
||||
REACTION_PACKS,
|
||||
STICKER_PACKS,
|
||||
TITLES,
|
||||
@@ -362,7 +363,13 @@ export class MockOnlineService implements OnlineService {
|
||||
|
||||
async updateProfile(patch: Parameters<OnlineService["updateProfile"]>[0]) {
|
||||
const p = await this.getProfile();
|
||||
this.profile = { ...p, ...patch };
|
||||
const next = { ...p, ...patch };
|
||||
// One-time reward: first time the player sets a (non-empty) city → +500 coins.
|
||||
if (patch.city && patch.city.trim() && !p.cityRewardClaimed) {
|
||||
next.coins = p.coins + CITY_REWARD;
|
||||
next.cityRewardClaimed = true;
|
||||
}
|
||||
this.profile = next;
|
||||
this.saveProfile();
|
||||
return this.profile;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export interface OnlineService {
|
||||
Pick<
|
||||
UserProfile,
|
||||
| "displayName" | "avatar" | "avatarImage" | "title" | "cardFront" | "cardBack"
|
||||
| "gender" | "socials" | "socialsVisibility"
|
||||
| "gender" | "city" | "socials" | "socialsVisibility"
|
||||
>
|
||||
>
|
||||
): Promise<UserProfile>;
|
||||
|
||||
@@ -83,6 +83,8 @@ export interface UserProfile {
|
||||
|
||||
// social
|
||||
gender?: Gender;
|
||||
city?: string; // selected city id (see IRAN_CITIES)
|
||||
cityRewardClaimed?: boolean; // true once the one-time "set your city" reward was granted
|
||||
socials?: SocialLinks;
|
||||
socialsVisibility?: SocialVisibility; // default "public"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user