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:
@@ -59,6 +59,8 @@ public class ProfileDto
|
||||
|
||||
// social
|
||||
public string Gender { get; set; } = ""; // "" | male | female | other
|
||||
public string? City { get; set; } // selected city id (see client IRAN_CITIES)
|
||||
public bool CityRewardClaimed { get; set; } // one-time "set your city" reward granted
|
||||
public SocialLinksDto Socials { get; set; } = new();
|
||||
public string SocialsVisibility { get; set; } = "public"; // public | friends | hidden
|
||||
|
||||
|
||||
@@ -69,9 +69,27 @@ public class ProfileService
|
||||
if (patch.TryGetProperty("socialsVisibility", out var sv) && sv.ValueKind == JsonValueKind.String) p.SocialsVisibility = sv.GetString()!;
|
||||
if (patch.TryGetProperty("socials", out var so) && so.ValueKind == JsonValueKind.Object)
|
||||
p.Socials = JsonSerializer.Deserialize<SocialLinksDto>(so.GetRawText(), JsonOpts.Default) ?? p.Socials;
|
||||
return await Save(p);
|
||||
// One-time "set your city" reward: first non-empty city → +500 coins.
|
||||
var cityRewarded = false;
|
||||
if (patch.TryGetProperty("city", out var ci) && ci.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
var city = ci.GetString();
|
||||
p.City = city;
|
||||
if (!string.IsNullOrWhiteSpace(city) && !p.CityRewardClaimed)
|
||||
{
|
||||
p.CityRewardClaimed = true;
|
||||
p.Coins += CityReward;
|
||||
cityRewarded = true;
|
||||
}
|
||||
}
|
||||
await Save(p);
|
||||
if (cityRewarded) await Ledger(uid, "city", CityReward, "profile-city");
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>One-time coin reward for setting your city (mirrors client CITY_REWARD).</summary>
|
||||
public const int CityReward = 500;
|
||||
|
||||
public async Task<ProfileDto> UpgradePlan(string uid)
|
||||
{
|
||||
var p = await GetOrCreate(uid, null);
|
||||
|
||||
Reference in New Issue
Block a user