This commit is contained in:
2026-05-01 01:53:16 +08:00
parent c9be987671
commit 96313dd481
10 changed files with 100 additions and 55 deletions

View File

@@ -57,13 +57,13 @@
1. `world_key = puzzle:{entry_profile_id}` 1. `world_key = puzzle:{entry_profile_id}`
2. `world_type = PUZZLE` 2. `world_type = PUZZLE`
3. `profile_id = entry_profile_id`,保证同一个作品链只覆盖一条存档。 3. `profile_id = entry_profile_id`,保证同一个作品链只覆盖一条存档。
4. `world_name` 使用当前关卡名。 4. `world_name` 使用当前可恢复关卡名。
5. `subtitle` 使用 `第 N 关` 5. `subtitle` 使用 `第 N 关`
6. `summary_text` 使用当前状态: 6. `summary_text` 使用可恢复关卡状态:
- playing`拼图进行中` - playing`拼图进行中`
- failed`关卡失败` - failed`关卡失败`
- cleared`关卡已完成` - cleared`关卡已完成`
7. `cover_image_src` 使用当前关卡正式图。 7. `cover_image_src` 使用可恢复关卡正式图。
8. `game_state_json` 保存最小拼图恢复载荷: 8. `game_state_json` 保存最小拼图恢复载荷:
- `runtimeKind = "puzzle"` - `runtimeKind = "puzzle"`
- `runId` - `runId`
@@ -73,6 +73,8 @@
- `currentLevelId` - `currentLevelId`
- `status` - `status`
通关存档投影有一个额外规则:如果当前关卡已通关,并且 `refresh_next_level_handoff` 已经确认同作品存在下一关,则存档立即投影到同作品下一关入口,`status` 写为 `playing``subtitle / world_name / cover_image_src / currentLevelId` 都使用下一关。若当前作品没有下一关、只存在相似作品候选,存档保持当前已通关关卡,等待玩家在结算弹窗里选择相似作品,不能提前替玩家切换到某个候选作品。
## 写入时机 ## 写入时机
SpacetimeDB 拼图运行态每次持久化 run 时同步刷新存档: SpacetimeDB 拼图运行态每次持久化 run 时同步刷新存档:
@@ -81,8 +83,9 @@ SpacetimeDB 拼图运行态每次持久化 run 时同步刷新存档:
2. `advance_puzzle_next_level`:进入下一关后更新同一条存档。 2. `advance_puzzle_next_level`:进入下一关后更新同一条存档。
3. `use_puzzle_runtime_prop(extendTime)`:续时成功后更新状态。 3. `use_puzzle_runtime_prop(extendTime)`:续时成功后更新状态。
4. `get_puzzle_run` 导致失败态落库时,也同步更新为失败存档。 4. `get_puzzle_run` 导致失败态落库时,也同步更新为失败存档。
5. `submit_puzzle_leaderboard_entry`:正式 run 提交成绩并把当前关标记为已通关时,先刷新下一关 handoff再按上面的通关投影规则同步存档。
排行榜提交只负责成绩与通关态,不新增存档规则;如果它把 run 状态更新为通关,也跟随 run 持久化刷新存档 前端在 `startPuzzleRun / usePuzzleProp / submitPuzzleLeaderboard / advancePuzzleLevel / getPuzzleRun` 成功后主动刷新存档列表,避免存档页停留在进入作品前或上一关的旧投影
## 验收 ## 验收
@@ -91,5 +94,5 @@ SpacetimeDB 拼图运行态每次持久化 run 时同步刷新存档:
3. 陶泥币余额不足时确认弹窗保留,并展示错误。 3. 陶泥币余额不足时确认弹窗保留,并展示错误。
4. 点击 `重新开始` 后当前关卡重新打乱并重置倒计时。 4. 点击 `重新开始` 后当前关卡重新打乱并重置倒计时。
5. 进入拼图作品后,存档页出现 `worldType = PUZZLE` 的拼图存档。 5. 进入拼图作品后,存档页出现 `worldType = PUZZLE` 的拼图存档。
6. 通过一关进入下一关,同一条存档更新到新关卡。 6. 通过一关后,只要后端确认同作品下一关存在,同一条存档立即更新到新关卡;没有同作品下一关时保留已完成关卡,等待玩家选择相似作品
7. 定向前端测试、Rust 拼图模块测试与编码检查通过。 7. 定向前端测试、Rust 拼图模块测试与编码检查通过。

View File

@@ -46,7 +46,7 @@
- `POST /api/runtime/puzzle/works/{profile_id}/point-incentive/claim` - `POST /api/runtime/puzzle/works/{profile_id}/point-incentive/claim`
- 返回更新后的 `PuzzleWorkProfile` - 返回更新后的 `PuzzleWorkProfile`
3. 创作页仅对已发布拼图作品显示积分激励块RPG、大鱼和草稿卡不显示。 3. 创作页仅对已发布拼图作品显示积分激励块RPG、大鱼和草稿卡不显示。
4. 领取成功后刷新对应拼图作品列表状态,按钮立即禁用或显示新的待领取数。 4. 领取成功后刷新对应拼图作品列表状态,按钮立即禁用或显示新的待领取数,并同步刷新个人钱包看板
5. `spacetime-client` 映射层继续兼容历史拼图运行快照:旧 `run_json` 若缺少 `started_at_ms`API 记录回填为非 0 值,避免前端计时器拿到无效开始时间。 5. `spacetime-client` 映射层继续兼容历史拼图运行快照:旧 `run_json` 若缺少 `started_at_ms`API 记录回填为非 0 值,避免前端计时器拿到无效开始时间。
## 5. 验收点 ## 5. 验收点
@@ -56,4 +56,5 @@
3. 待领取积分为 0 时领取按钮禁用。 3. 待领取积分为 0 时领取按钮禁用。
4. 非作者游玩他人拼图并使用付费道具后,该作品累计 half points 增加。 4. 非作者游玩他人拼图并使用付费道具后,该作品累计 half points 增加。
5. 作者领取后钱包增加向下取整后的整数陶泥币,作品待领取数归零或保留不足 1 的小数余额。 5. 作者领取后钱包增加向下取整后的整数陶泥币,作品待领取数归零或保留不足 1 的小数余额。
6. 修改后运行编码检查、SpacetimeDB 绑定生成、Rust 检查和必要前端测试。 6. 领取成功后顶部/我的页钱包余额随个人看板刷新。
7. 修改后运行编码检查、SpacetimeDB 绑定生成、Rust 检查和必要前端测试。

View File

@@ -3459,6 +3459,19 @@ pub fn current_puzzle_unix_micros() -> i64 {
(current_unix_ms() as i64).saturating_mul(1_000) (current_unix_ms() as i64).saturating_mul(1_000)
} }
pub fn puzzle_point_incentive_claimable_points(total_half_points: u64, claimed_points: u64) -> u64 {
total_half_points
.saturating_div(2)
.saturating_sub(claimed_points)
}
pub fn puzzle_point_incentive_total_after_spend(
total_half_points: u64,
spent_points: u64,
) -> u64 {
total_half_points.saturating_add(spent_points)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -3779,6 +3792,16 @@ mod tests {
assert_ne!(first_positions, second_positions); assert_ne!(first_positions, second_positions);
} }
#[test]
fn puzzle_point_incentive_uses_half_points_and_floor_claimable() {
// 中文注释:累计单位是 half point消耗 1 个陶泥币只让作者获得 0.5 个待结算陶泥币。
assert_eq!(puzzle_point_incentive_total_after_spend(0, 1), 1);
assert_eq!(puzzle_point_incentive_claimable_points(1, 0), 0);
assert_eq!(puzzle_point_incentive_claimable_points(2, 0), 1);
assert_eq!(puzzle_point_incentive_claimable_points(5, 1), 1);
assert_eq!(puzzle_point_incentive_claimable_points(5, 2), 0);
}
#[test] #[test]
fn initial_board_has_no_original_neighbor_pairs() { fn initial_board_has_no_original_neighbor_pairs() {
for grid_size in PUZZLE_SUPPORTED_GRID_SIZES { for grid_size in PUZZLE_SUPPORTED_GRID_SIZES {

View File

@@ -26,20 +26,20 @@ pub use mapper::{
CustomWorldPublishGateRecord, CustomWorldPublishWorldRecord, CustomWorldPublishGateRecord, CustomWorldPublishWorldRecord,
CustomWorldPublishWorldRecordInput, CustomWorldPublishedProfileCompileRecord, CustomWorldPublishWorldRecordInput, CustomWorldPublishedProfileCompileRecord,
CustomWorldResultPreviewBlockerRecord, CustomWorldSupportedActionRecord, CustomWorldResultPreviewBlockerRecord, CustomWorldSupportedActionRecord,
CustomWorldWorkSummaryRecord, NpcBattleInteractionRecord, NpcInteractionRecord, NpcStateRecord, CustomWorldWorkSummaryRecord, Match3DAgentMessageFinalizeRecordInput,
Match3DAgentMessageRecord, Match3DAgentMessageSubmitRecordInput,
Match3DAgentSessionCreateRecordInput, Match3DAgentSessionRecord, Match3DAnchorItemRecord,
Match3DAnchorPackRecord, Match3DClickConfirmationRecord, Match3DCompileDraftRecordInput,
Match3DCreatorConfigRecord, Match3DItemSnapshotRecord, Match3DResultDraftRecord,
Match3DRunClickRecordInput, Match3DRunRecord, Match3DRunRestartRecordInput,
Match3DRunStartRecordInput, Match3DRunStopRecordInput, Match3DRunTimeUpRecordInput,
Match3DTraySlotRecord, Match3DWorkProfileRecord, Match3DWorkUpdateRecordInput,
NpcBattleInteractionRecord, NpcInteractionRecord, NpcStateRecord,
PuzzleAgentMessageFinalizeRecordInput, PuzzleAgentMessageRecord, PuzzleAgentMessageFinalizeRecordInput, PuzzleAgentMessageRecord,
PuzzleAgentMessageSubmitRecordInput, PuzzleAgentSessionCreateRecordInput, PuzzleAgentMessageSubmitRecordInput, PuzzleAgentSessionCreateRecordInput,
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord, PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
PuzzleAnchorPackRecord, PuzzleBoardRecord, PuzzleCellPositionRecord, PuzzleCreatorIntentRecord, PuzzleAnchorPackRecord, PuzzleBoardRecord, PuzzleCellPositionRecord, PuzzleCreatorIntentRecord,
PuzzleDraftLevelRecord, PuzzleFormDraftRecord, PuzzleFormDraftSaveRecordInput, PuzzleDraftLevelRecord, PuzzleFormDraftRecord, PuzzleFormDraftSaveRecordInput,
Match3DAgentMessageFinalizeRecordInput, Match3DAgentMessageRecord,
Match3DAgentMessageSubmitRecordInput, Match3DAgentSessionCreateRecordInput,
Match3DAgentSessionRecord, Match3DAnchorItemRecord, Match3DAnchorPackRecord,
Match3DClickConfirmationRecord, Match3DCompileDraftRecordInput, Match3DCreatorConfigRecord,
Match3DItemSnapshotRecord, Match3DResultDraftRecord, Match3DRunClickRecordInput,
Match3DRunRecord, Match3DRunRestartRecordInput, Match3DRunStartRecordInput,
Match3DRunStopRecordInput, Match3DRunTimeUpRecordInput, Match3DTraySlotRecord,
Match3DWorkProfileRecord, Match3DWorkUpdateRecordInput,
PuzzleGeneratedImageCandidateRecord, PuzzleGeneratedImagesSaveRecordInput, PuzzleGeneratedImageCandidateRecord, PuzzleGeneratedImagesSaveRecordInput,
PuzzleLeaderboardEntryRecord, PuzzleLeaderboardSubmitRecordInput, PuzzleMergedGroupRecord, PuzzleLeaderboardEntryRecord, PuzzleLeaderboardSubmitRecordInput, PuzzleMergedGroupRecord,
PuzzlePieceStateRecord, PuzzlePublishRecordInput, PuzzleRecommendedNextWorkRecord, PuzzlePieceStateRecord, PuzzlePublishRecordInput, PuzzleRecommendedNextWorkRecord,

View File

@@ -2,17 +2,23 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)] #![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::runtime_profile_invite_code_admin_procedure_result_type::RuntimeProfileInviteCodeAdminProcedureResult;
use super::runtime_profile_invite_code_admin_upsert_input_type::RuntimeProfileInviteCodeAdminUpsertInput; use super::runtime_profile_invite_code_admin_upsert_input_type::RuntimeProfileInviteCodeAdminUpsertInput;
use super::runtime_profile_invite_code_admin_procedure_result_type::RuntimeProfileInviteCodeAdminProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)] #[sats(crate = __lib)]
struct AdminUpsertProfileInviteCodeArgs { struct AdminUpsertProfileInviteCodeArgs {
pub input: RuntimeProfileInviteCodeAdminUpsertInput, pub input: RuntimeProfileInviteCodeAdminUpsertInput,
} }
impl __sdk::InModule for AdminUpsertProfileInviteCodeArgs { impl __sdk::InModule for AdminUpsertProfileInviteCodeArgs {
type Module = super::RemoteModule; type Module = super::RemoteModule;
} }
@@ -22,19 +28,16 @@ impl __sdk::InModule for AdminUpsertProfileInviteCodeArgs {
/// ///
/// Implemented for [`super::RemoteProcedures`]. /// Implemented for [`super::RemoteProcedures`].
pub trait admin_upsert_profile_invite_code { pub trait admin_upsert_profile_invite_code {
fn admin_upsert_profile_invite_code(&self, input: RuntimeProfileInviteCodeAdminUpsertInput) { fn admin_upsert_profile_invite_code(&self, input: RuntimeProfileInviteCodeAdminUpsertInput,
self.admin_upsert_profile_invite_code_then(input, |_, _| {}); ) {
self.admin_upsert_profile_invite_code_then(input, |_, _| {});
} }
fn admin_upsert_profile_invite_code_then( fn admin_upsert_profile_invite_code_then(
&self, &self,
input: RuntimeProfileInviteCodeAdminUpsertInput, input: RuntimeProfileInviteCodeAdminUpsertInput,
__callback: impl FnOnce( __callback: impl FnOnce(&super::ProcedureEventContext, Result<RuntimeProfileInviteCodeAdminProcedureResult, __sdk::InternalError>) + Send + 'static,
&super::ProcedureEventContext,
Result<RuntimeProfileInviteCodeAdminProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
); );
} }
@@ -43,17 +46,12 @@ impl admin_upsert_profile_invite_code for super::RemoteProcedures {
&self, &self,
input: RuntimeProfileInviteCodeAdminUpsertInput, input: RuntimeProfileInviteCodeAdminUpsertInput,
__callback: impl FnOnce( __callback: impl FnOnce(&super::ProcedureEventContext, Result<RuntimeProfileInviteCodeAdminProcedureResult, __sdk::InternalError>) + Send + 'static,
&super::ProcedureEventContext,
Result<RuntimeProfileInviteCodeAdminProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) { ) {
self.imp self.imp.invoke_procedure_with_callback::<_, RuntimeProfileInviteCodeAdminProcedureResult>(
.invoke_procedure_with_callback::<_, RuntimeProfileInviteCodeAdminProcedureResult>( "admin_upsert_profile_invite_code",
"admin_upsert_profile_invite_code", AdminUpsertProfileInviteCodeArgs { input, },
AdminUpsertProfileInviteCodeArgs { input }, __callback,
__callback, );
);
} }
} }

View File

@@ -2,7 +2,12 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)] #![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
use super::runtime_profile_invite_code_snapshot_type::RuntimeProfileInviteCodeSnapshot; use super::runtime_profile_invite_code_snapshot_type::RuntimeProfileInviteCodeSnapshot;
@@ -10,10 +15,11 @@ use super::runtime_profile_invite_code_snapshot_type::RuntimeProfileInviteCodeSn
#[sats(crate = __lib)] #[sats(crate = __lib)]
pub struct RuntimeProfileInviteCodeAdminProcedureResult { pub struct RuntimeProfileInviteCodeAdminProcedureResult {
pub ok: bool, pub ok: bool,
pub record: Option<RuntimeProfileInviteCodeSnapshot>, pub record: Option::<RuntimeProfileInviteCodeSnapshot>,
pub error_message: Option<String>, pub error_message: Option::<String>,
} }
impl __sdk::InModule for RuntimeProfileInviteCodeAdminProcedureResult { impl __sdk::InModule for RuntimeProfileInviteCodeAdminProcedureResult {
type Module = super::RemoteModule; type Module = super::RemoteModule;
} }

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)] #![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)] #[sats(crate = __lib)]
@@ -13,6 +19,7 @@ pub struct RuntimeProfileInviteCodeAdminUpsertInput {
pub updated_at_micros: i64, pub updated_at_micros: i64,
} }
impl __sdk::InModule for RuntimeProfileInviteCodeAdminUpsertInput { impl __sdk::InModule for RuntimeProfileInviteCodeAdminUpsertInput {
type Module = super::RemoteModule; type Module = super::RemoteModule;
} }

View File

@@ -2,7 +2,13 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)] #![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; use spacetimedb_sdk::__codegen::{
self as __sdk,
__lib,
__sats,
__ws,
};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)] #[sats(crate = __lib)]
@@ -14,6 +20,7 @@ pub struct RuntimeProfileInviteCodeSnapshot {
pub updated_at_micros: i64, pub updated_at_micros: i64,
} }
impl __sdk::InModule for RuntimeProfileInviteCodeSnapshot { impl __sdk::InModule for RuntimeProfileInviteCodeSnapshot {
type Module = super::RemoteModule; type Module = super::RemoteModule;
} }

View File

@@ -1911,7 +1911,7 @@ fn claim_puzzle_work_point_incentive_tx(
return Err("无权领取该作品的积分激励".to_string()); return Err("无权领取该作品的积分激励".to_string());
} }
let claimable_points = puzzle_point_incentive_claimable_points( let claimable_points = module_puzzle::puzzle_point_incentive_claimable_points(
row.point_incentive_total_half_points, row.point_incentive_total_half_points,
row.point_incentive_claimed_points, row.point_incentive_claimed_points,
); );
@@ -2571,9 +2571,9 @@ fn upsert_puzzle_profile_save_archive(
"runtimeKind": "puzzle", "runtimeKind": "puzzle",
"runId": run.run_id, "runId": run.run_id,
"entryProfileId": run.entry_profile_id, "entryProfileId": run.entry_profile_id,
"currentProfileId": target.profile_id, "currentProfileId": target.profile_id.clone(),
"currentLevelIndex": target.level_index, "currentLevelIndex": target.level_index,
"currentLevelId": target.level_id, "currentLevelId": target.level_id.clone(),
"status": target.status.as_str(), "status": target.status.as_str(),
})) }))
.unwrap_or_else(|_| "{}".to_string()); .unwrap_or_else(|_| "{}".to_string());
@@ -2613,6 +2613,8 @@ fn resolve_puzzle_archive_target(
run: &PuzzleRunSnapshot, run: &PuzzleRunSnapshot,
current_level: &module_puzzle::PuzzleRuntimeLevelSnapshot, current_level: &module_puzzle::PuzzleRuntimeLevelSnapshot,
) -> Result<PuzzleArchiveTarget, String> { ) -> Result<PuzzleArchiveTarget, String> {
// 中文注释:通关后若已经算出同作品下一关,存档页直接投影到下一关入口;
// 跨作品候选需要玩家选择,不能在存档里提前替玩家切换作品。
let owner_user_id = resolve_puzzle_current_owner_user_id(ctx, &current_level.profile_id); let owner_user_id = resolve_puzzle_current_owner_user_id(ctx, &current_level.profile_id);
if current_level.status != PuzzleRuntimeLevelStatus::Cleared { if current_level.status != PuzzleRuntimeLevelStatus::Cleared {
return Ok(PuzzleArchiveTarget { return Ok(PuzzleArchiveTarget {
@@ -2697,12 +2699,6 @@ fn puzzle_archive_summary_text(status: PuzzleRuntimeLevelStatus) -> String {
.to_string() .to_string()
} }
fn puzzle_point_incentive_claimable_points(total_half_points: u64, claimed_points: u64) -> u64 {
total_half_points
.saturating_div(2)
.saturating_sub(claimed_points)
}
fn accrue_puzzle_point_incentive( fn accrue_puzzle_point_incentive(
ctx: &TxContext, ctx: &TxContext,
profile_id: &str, profile_id: &str,
@@ -2749,9 +2745,10 @@ fn accrue_puzzle_point_incentive(
play_count: row.play_count, play_count: row.play_count,
remix_count: row.remix_count, remix_count: row.remix_count,
like_count: row.like_count, like_count: row.like_count,
point_incentive_total_half_points: row point_incentive_total_half_points: module_puzzle::puzzle_point_incentive_total_after_spend(
.point_incentive_total_half_points row.point_incentive_total_half_points,
.saturating_add(spent_points), spent_points,
),
point_incentive_claimed_points: row.point_incentive_claimed_points, point_incentive_claimed_points: row.point_incentive_claimed_points,
anchor_pack_json: row.anchor_pack_json.clone(), anchor_pack_json: row.anchor_pack_json.clone(),
publish_ready: row.publish_ready, publish_ready: row.publish_ready,

View File

@@ -2071,12 +2071,13 @@ export function PlatformEntryFlowShellImpl({
? mergePuzzleServiceRuntimeState(currentRun, run) ? mergePuzzleServiceRuntimeState(currentRun, run)
: currentRun, : currentRun,
); );
void platformBootstrap.refreshSaveArchives();
} catch (error) { } catch (error) {
setPuzzleError( setPuzzleError(
resolvePuzzleErrorMessage(error, '同步拼图失败状态失败。'), resolvePuzzleErrorMessage(error, '同步拼图失败状态失败。'),
); );
} }
}, [puzzleRun, resolvePuzzleErrorMessage, setPuzzleError]); }, [platformBootstrap, puzzleRun, resolvePuzzleErrorMessage, setPuzzleError]);
const usePuzzleProp = useCallback( const usePuzzleProp = useCallback(
async (propKind: PuzzleRuntimePropKind) => { async (propKind: PuzzleRuntimePropKind) => {
@@ -2677,6 +2678,7 @@ export function PlatformEntryFlowShellImpl({
syncUpdatedPublicWorkDetail( syncUpdatedPublicWorkDetail(
mapPuzzleWorkToPublicWorkDetail(updatedWork), mapPuzzleWorkToPublicWorkDetail(updatedWork),
); );
void platformBootstrap.refreshProfileDashboard();
}) })
.catch((error) => { .catch((error) => {
setPuzzleError( setPuzzleError(
@@ -2690,6 +2692,7 @@ export function PlatformEntryFlowShellImpl({
}, },
[ [
claimingPuzzlePointIncentiveProfileId, claimingPuzzlePointIncentiveProfileId,
platformBootstrap,
resolvePuzzleErrorMessage, resolvePuzzleErrorMessage,
runProtectedAction, runProtectedAction,
setPuzzleError, setPuzzleError,