Merge remote-tracking branch 'origin/master'
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -31,9 +31,7 @@ pub struct CreationEntryTypeConfig {
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn get_creation_entry_config(
|
||||
ctx: &mut ProcedureContext,
|
||||
) -> CreationEntryConfigProcedureResult {
|
||||
pub fn get_creation_entry_config(ctx: &mut ProcedureContext) -> CreationEntryConfigProcedureResult {
|
||||
match ctx.try_with_tx(|tx| get_or_seed_creation_entry_config_snapshot(tx)) {
|
||||
Ok(record) => CreationEntryConfigProcedureResult {
|
||||
ok: true,
|
||||
@@ -180,18 +178,129 @@ fn seed_creation_entry_config_if_missing(ctx: &ReducerContext) {
|
||||
ctx.db.creation_entry_type_config().insert(seed);
|
||||
}
|
||||
}
|
||||
|
||||
migrate_visual_novel_entry_from_old_open_default(ctx, now);
|
||||
}
|
||||
|
||||
fn migrate_visual_novel_entry_from_old_open_default(ctx: &ReducerContext, now: Timestamp) {
|
||||
let id = "visual-novel".to_string();
|
||||
let Some(row) = ctx.db.creation_entry_type_config().id().find(&id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// 中文注释:只纠偏旧默认种子,不覆盖后台入口开关里后续手动调整的视觉小说配置。
|
||||
let still_old_default = row.title == "视觉小说"
|
||||
&& row.subtitle == "分支叙事体验"
|
||||
&& row.badge == "可创建"
|
||||
&& row.image_src == "/creation-type-references/visual-novel.webp"
|
||||
&& row.visible
|
||||
&& row.open
|
||||
&& row.sort_order == 60;
|
||||
if !still_old_default {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.db
|
||||
.creation_entry_type_config()
|
||||
.id()
|
||||
.update(CreationEntryTypeConfig {
|
||||
badge: "敬请期待".to_string(),
|
||||
open: false,
|
||||
updated_at: now,
|
||||
..row
|
||||
});
|
||||
}
|
||||
|
||||
fn default_creation_entry_type_configs(now: Timestamp) -> Vec<CreationEntryTypeConfig> {
|
||||
vec![
|
||||
build_creation_entry_type_seed("rpg", "文字冒险", "经典 RPG 体验", "内测", "/creation-type-references/rpg.webp", false, true, 10, now),
|
||||
build_creation_entry_type_seed("big-fish", "摸鱼", "轻量闯关玩法", "可创建", "/creation-type-references/big-fish.webp", false, true, 20, now),
|
||||
build_creation_entry_type_seed("puzzle", "拼图", "拼图关卡创作", "可创建", "/creation-type-references/puzzle.webp", true, true, 30, now),
|
||||
build_creation_entry_type_seed("match3d", "抓大鹅", "3D 消除关卡", "可创建", "/creation-type-references/match3d.webp", true, true, 40, now),
|
||||
build_creation_entry_type_seed("square-hole", "方洞", "形状投放挑战", "可创建", "/creation-type-references/square-hole.webp", true, true, 50, now),
|
||||
build_creation_entry_type_seed("visual-novel", "视觉小说", "分支叙事体验", "可创建", "/creation-type-references/visual-novel.webp", true, true, 60, now),
|
||||
build_creation_entry_type_seed("airp", "AI RPG", "原生角色扮演", "即将开放", "/creation-type-references/airp.webp", true, false, 70, now),
|
||||
build_creation_entry_type_seed("creative-agent", "智能体创作", "对话式创作实验", "内测", "/creation-type-references/creative-agent.webp", false, true, 80, now),
|
||||
build_creation_entry_type_seed(
|
||||
"rpg",
|
||||
"文字冒险",
|
||||
"经典 RPG 体验",
|
||||
"内测",
|
||||
"/creation-type-references/rpg.webp",
|
||||
false,
|
||||
true,
|
||||
10,
|
||||
now,
|
||||
),
|
||||
build_creation_entry_type_seed(
|
||||
"big-fish",
|
||||
"摸鱼",
|
||||
"轻量闯关玩法",
|
||||
"可创建",
|
||||
"/creation-type-references/big-fish.webp",
|
||||
false,
|
||||
true,
|
||||
20,
|
||||
now,
|
||||
),
|
||||
build_creation_entry_type_seed(
|
||||
"puzzle",
|
||||
"拼图",
|
||||
"拼图关卡创作",
|
||||
"可创建",
|
||||
"/creation-type-references/puzzle.webp",
|
||||
true,
|
||||
true,
|
||||
30,
|
||||
now,
|
||||
),
|
||||
build_creation_entry_type_seed(
|
||||
"match3d",
|
||||
"抓大鹅",
|
||||
"3D 消除关卡",
|
||||
"可创建",
|
||||
"/creation-type-references/match3d.webp",
|
||||
true,
|
||||
true,
|
||||
40,
|
||||
now,
|
||||
),
|
||||
build_creation_entry_type_seed(
|
||||
"square-hole",
|
||||
"方洞",
|
||||
"形状投放挑战",
|
||||
"可创建",
|
||||
"/creation-type-references/square-hole.webp",
|
||||
false,
|
||||
true,
|
||||
50,
|
||||
now,
|
||||
),
|
||||
build_creation_entry_type_seed(
|
||||
"visual-novel",
|
||||
"视觉小说",
|
||||
"分支叙事体验",
|
||||
"敬请期待",
|
||||
"/creation-type-references/visual-novel.webp",
|
||||
true,
|
||||
false,
|
||||
60,
|
||||
now,
|
||||
),
|
||||
build_creation_entry_type_seed(
|
||||
"airp",
|
||||
"AI RPG",
|
||||
"原生角色扮演",
|
||||
"即将开放",
|
||||
"/creation-type-references/airp.webp",
|
||||
true,
|
||||
false,
|
||||
70,
|
||||
now,
|
||||
),
|
||||
build_creation_entry_type_seed(
|
||||
"creative-agent",
|
||||
"智能体创作",
|
||||
"对话式创作实验",
|
||||
"内测",
|
||||
"/creation-type-references/creative-agent.webp",
|
||||
false,
|
||||
true,
|
||||
80,
|
||||
now,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,6 @@ pub struct ProfileInviteCode {
|
||||
pub(crate) starts_at: Option<Timestamp>,
|
||||
#[default(None::<Timestamp>)]
|
||||
pub(crate) expires_at: Option<Timestamp>,
|
||||
pub(crate) granted_user_tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[spacetimedb::table(
|
||||
@@ -2201,7 +2200,8 @@ fn redeem_profile_referral_invite_code_record(
|
||||
if inviter_code.user_id == invitee_user_id {
|
||||
return Err("不能填写自己的邀请码".to_string());
|
||||
}
|
||||
let granted_user_tags = inviter_code.granted_user_tags.clone();
|
||||
let invite_metadata_user_tags =
|
||||
profile_invite_code_metadata_user_tags(&inviter_code.metadata_json)?;
|
||||
|
||||
let invitee_balance_after = apply_profile_wallet_delta(
|
||||
ctx,
|
||||
@@ -2248,7 +2248,7 @@ fn redeem_profile_referral_invite_code_record(
|
||||
invitee_reward_granted: true,
|
||||
bound_at,
|
||||
});
|
||||
merge_user_account_tags(ctx, &invitee_user_id, granted_user_tags)?;
|
||||
merge_user_account_tags(ctx, &invitee_user_id, invite_metadata_user_tags)?;
|
||||
|
||||
Ok(RuntimeReferralRedeemSnapshot {
|
||||
center: build_profile_referral_invite_center_snapshot(ctx, &invitee_user_id),
|
||||
@@ -2422,7 +2422,6 @@ fn admin_upsert_profile_invite_code_record(
|
||||
input.admin_user_id,
|
||||
input.invite_code,
|
||||
input.metadata_json,
|
||||
input.granted_user_tags,
|
||||
input.starts_at_micros,
|
||||
input.expires_at_micros,
|
||||
input.updated_at_micros,
|
||||
@@ -2459,7 +2458,6 @@ fn admin_upsert_profile_invite_code_record(
|
||||
expires_at: validated_input
|
||||
.expires_at_micros
|
||||
.map(Timestamp::from_micros_since_unix_epoch),
|
||||
granted_user_tags: validated_input.granted_user_tags,
|
||||
});
|
||||
return Ok(build_profile_invite_code_snapshot_from_row(&inserted));
|
||||
}
|
||||
@@ -2476,7 +2474,6 @@ fn admin_upsert_profile_invite_code_record(
|
||||
expires_at: validated_input
|
||||
.expires_at_micros
|
||||
.map(Timestamp::from_micros_since_unix_epoch),
|
||||
granted_user_tags: validated_input.granted_user_tags,
|
||||
});
|
||||
Ok(build_profile_invite_code_snapshot_from_row(&inserted))
|
||||
}
|
||||
@@ -2603,7 +2600,6 @@ fn ensure_profile_invite_code(ctx: &ReducerContext, user_id: &str) -> ProfileInv
|
||||
updated_at: ctx.timestamp,
|
||||
starts_at: None,
|
||||
expires_at: None,
|
||||
granted_user_tags: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2622,14 +2618,33 @@ fn merge_user_account_tags(
|
||||
return Err("用户不存在".to_string());
|
||||
};
|
||||
|
||||
account.user_tags.extend(granted_tags);
|
||||
let mut next_tags = account.user_tags.take().unwrap_or_default();
|
||||
next_tags.extend(granted_tags);
|
||||
account.user_tags =
|
||||
normalize_profile_user_tags(account.user_tags).map_err(|error| error.to_string())?;
|
||||
Some(normalize_profile_user_tags(next_tags).map_err(|error| error.to_string())?);
|
||||
ctx.db.user_account().user_id().delete(&account.user_id);
|
||||
ctx.db.user_account().insert(account);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn profile_invite_code_metadata_user_tags(metadata_json: &str) -> Result<Vec<String>, String> {
|
||||
let metadata = serde_json::from_str::<JsonValue>(metadata_json)
|
||||
.map_err(|_| RuntimeProfileFieldError::InvalidInviteCodeMetadata.to_string())?;
|
||||
let tags = metadata
|
||||
.get("userTags")
|
||||
.or_else(|| metadata.get("user_tags"))
|
||||
.and_then(JsonValue::as_array)
|
||||
.map(|items| {
|
||||
items
|
||||
.iter()
|
||||
.filter_map(JsonValue::as_str)
|
||||
.map(str::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
normalize_profile_user_tags(tags).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
fn validate_profile_invite_code_redeem_time(
|
||||
invite_code: &ProfileInviteCode,
|
||||
now_micros: i64,
|
||||
@@ -3654,7 +3669,6 @@ fn build_profile_invite_code_snapshot_from_row(
|
||||
user_id: row.user_id.clone(),
|
||||
invite_code: row.invite_code.clone(),
|
||||
metadata_json: row.metadata_json.clone(),
|
||||
granted_user_tags: row.granted_user_tags.clone(),
|
||||
starts_at_micros: row
|
||||
.starts_at
|
||||
.map(|value| value.to_micros_since_unix_epoch()),
|
||||
|
||||
Reference in New Issue
Block a user