optimize puzzle gallery access

This commit is contained in:
kdletters
2026-05-16 18:14:00 +08:00
parent 49ffa6b901
commit 7f16e88e57
7 changed files with 508 additions and 101 deletions

View File

@@ -142,24 +142,6 @@ use module_npc::{
NpcStanceProfile as DomainNpcStanceProfile, NpcStateSnapshot as DomainNpcStateSnapshot,
ResolveNpcInteractionInput as DomainResolveNpcInteractionInput,
};
use module_puzzle::{
PuzzleAgentMessageSnapshot as DomainPuzzleAgentMessageSnapshot,
PuzzleAgentSessionSnapshot as DomainPuzzleAgentSessionSnapshot,
PuzzleAgentSuggestedAction as DomainPuzzleAgentSuggestedAction,
PuzzleAnchorItem as DomainPuzzleAnchorItem, PuzzleAnchorPack as DomainPuzzleAnchorPack,
PuzzleBoardSnapshot as DomainPuzzleBoardSnapshot,
PuzzleCellPosition as DomainPuzzleCellPosition,
PuzzleCreatorIntent as DomainPuzzleCreatorIntent, PuzzleDraftLevel as DomainPuzzleDraftLevel,
PuzzleGeneratedImageCandidate as DomainPuzzleGeneratedImageCandidate,
PuzzleMergedGroupState as DomainPuzzleMergedGroupState,
PuzzlePieceState as DomainPuzzlePieceState, PuzzleResultDraft as DomainPuzzleResultDraft,
PuzzleResultPreviewBlocker as DomainPuzzleResultPreviewBlocker,
PuzzleResultPreviewEnvelope as DomainPuzzleResultPreviewEnvelope,
PuzzleResultPreviewFinding as DomainPuzzleResultPreviewFinding,
PuzzleRunSnapshot as DomainPuzzleRunSnapshot,
PuzzleRuntimeLevelSnapshot as DomainPuzzleRuntimeLevelSnapshot,
PuzzleWorkProfile as DomainPuzzleWorkProfile,
};
use module_runtime::{
AnalyticsMetricQueryResponse as DomainAnalyticsMetricQueryResponse, RuntimeBrowseHistoryRecord,
RuntimePlatformTheme as DomainRuntimePlatformTheme, RuntimeProfileDashboardRecord,
@@ -222,7 +204,7 @@ use module_story::{
build_story_continue_input, build_story_session_input, build_story_session_state_input,
};
use shared_kernel::format_timestamp_micros;
use spacetimedb_sdk::DbContext;
use spacetimedb_sdk::{DbContext, Table};
use tokio::{
sync::{OwnedSemaphorePermit, Semaphore, oneshot},
time::timeout,
@@ -285,6 +267,7 @@ struct PooledConnectionSlot {
struct PooledConnection {
connection: DbConnection,
_gallery_subscription: Vec<SubscriptionHandle>,
runner: Option<JoinHandle<()>>,
broken: Arc<AtomicBool>,
}
@@ -377,6 +360,26 @@ impl SpacetimeClient {
final_result
}
async fn read_after_connect<T>(
&self,
read: impl FnOnce(&DbConnection) -> Result<T, SpacetimeClientError> + Send + 'static,
) -> Result<T, SpacetimeClientError>
where
T: Send + 'static,
{
let lease = self.acquire_connection().await?;
let final_result = if let Some(connection) = lease.connection.as_ref() {
read(&connection.connection)
} else {
Err(SpacetimeClientError::Runtime(
"SpacetimeDB 连接租约缺少连接".to_string(),
))
};
self.release_connection(lease).await;
final_result
}
async fn acquire_connection(&self) -> Result<PooledConnectionLease, SpacetimeClientError> {
let permit = timeout(
self.config.procedure_timeout,
@@ -465,13 +468,58 @@ impl SpacetimeClient {
.map_err(|_| SpacetimeClientError::Timeout)?
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
let gallery_subscription = self
.subscribe_puzzle_gallery_views(&connection, broken.clone())
.await?;
Ok(PooledConnection {
connection,
_gallery_subscription: gallery_subscription,
runner: Some(runner),
broken,
})
}
async fn subscribe_puzzle_gallery_views(
&self,
connection: &DbConnection,
broken: Arc<AtomicBool>,
) -> Result<Vec<SubscriptionHandle>, SpacetimeClientError> {
let mut subscriptions = Vec::new();
for query in [
"SELECT * FROM puzzle_gallery_view",
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'",
] {
let (sender, receiver) = oneshot::channel::<Result<(), SpacetimeClientError>>();
let applied_sender = Arc::new(Mutex::new(Some(sender)));
let on_applied_sender = applied_sender.clone();
let on_error_sender = applied_sender.clone();
let broken_flag = broken.clone();
let subscription = connection
.subscription_builder()
.on_applied(move |_| {
send_connect_once(&on_applied_sender, Ok(()));
})
.on_error(move |_, error| {
broken_flag.store(true, Ordering::SeqCst);
send_connect_once(
&on_error_sender,
Err(SpacetimeClientError::Procedure(error.to_string())),
);
})
.subscribe(query);
timeout(self.config.procedure_timeout, receiver)
.await
.map_err(|_| SpacetimeClientError::Timeout)?
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
subscriptions.push(subscription);
}
Ok(subscriptions)
}
async fn release_connection(&self, mut lease: PooledConnectionLease) {
let mut slot_guard = self.pool.slots[lease.slot_index].lock().await;
slot_guard.in_use = false;

View File

@@ -95,6 +95,7 @@ pub mod auth_store_snapshot_type;
pub mod auth_store_snapshot_upsert_input_type;
pub mod authorize_database_migration_operator_procedure;
pub mod bark_battle_draft_config_row_type;
pub mod bark_battle_draft_config_snapshot_type;
pub mod bark_battle_draft_config_table;
pub mod bark_battle_draft_config_upsert_input_type;
pub mod bark_battle_draft_create_input_type;
@@ -107,8 +108,10 @@ pub mod bark_battle_published_config_row_type;
pub mod bark_battle_published_config_table;
pub mod bark_battle_run_finish_input_type;
pub mod bark_battle_run_get_input_type;
pub mod bark_battle_run_snapshot_type;
pub mod bark_battle_run_start_input_type;
pub mod bark_battle_runtime_config_get_input_type;
pub mod bark_battle_runtime_config_snapshot_type;
pub mod bark_battle_runtime_run_row_type;
pub mod bark_battle_runtime_run_table;
pub mod bark_battle_score_record_row_type;
@@ -160,16 +163,20 @@ pub mod big_fish_run_get_input_type;
pub mod big_fish_run_procedure_result_type;
pub mod big_fish_run_start_input_type;
pub mod big_fish_run_status_type;
pub mod big_fish_runtime_entity_snapshot_type;
pub mod big_fish_runtime_params_type;
pub mod big_fish_runtime_run_table;
pub mod big_fish_runtime_run_type;
pub mod big_fish_runtime_snapshot_type;
pub mod big_fish_session_create_input_type;
pub mod big_fish_session_get_input_type;
pub mod big_fish_session_procedure_result_type;
pub mod big_fish_session_snapshot_type;
pub mod big_fish_vector_2_type;
pub mod big_fish_work_delete_input_type;
pub mod big_fish_work_like_record_input_type;
pub mod big_fish_work_remix_input_type;
pub mod big_fish_work_summary_snapshot_type;
pub mod big_fish_works_list_input_type;
pub mod big_fish_works_procedure_result_type;
pub mod bind_asset_object_to_entity_and_return_procedure;
@@ -402,30 +409,38 @@ pub mod list_visual_novel_works_procedure;
pub mod mark_profile_recharge_order_paid_and_return_procedure;
pub mod match_3_d_agent_message_finalize_input_type;
pub mod match_3_d_agent_message_row_type;
pub mod match_3_d_agent_message_snapshot_type;
pub mod match_3_d_agent_message_submit_input_type;
pub mod match_3_d_agent_message_table;
pub mod match_3_d_agent_session_create_input_type;
pub mod match_3_d_agent_session_get_input_type;
pub mod match_3_d_agent_session_procedure_result_type;
pub mod match_3_d_agent_session_row_type;
pub mod match_3_d_agent_session_snapshot_type;
pub mod match_3_d_agent_session_table;
pub mod match_3_d_click_item_procedure_result_type;
pub mod match_3_d_creator_config_snapshot_type;
pub mod match_3_d_draft_compile_input_type;
pub mod match_3_d_draft_snapshot_type;
pub mod match_3_d_item_snapshot_type;
pub mod match_3_d_run_click_input_type;
pub mod match_3_d_run_get_input_type;
pub mod match_3_d_run_procedure_result_type;
pub mod match_3_d_run_restart_input_type;
pub mod match_3_d_run_snapshot_type;
pub mod match_3_d_run_start_input_type;
pub mod match_3_d_run_stop_input_type;
pub mod match_3_d_run_time_up_input_type;
pub mod match_3_d_runtime_run_row_type;
pub mod match_3_d_runtime_run_table;
pub mod match_3_d_tray_slot_snapshot_type;
pub mod match_3_d_work_delete_input_type;
pub mod match_3_d_work_get_input_type;
pub mod match_3_d_work_procedure_result_type;
pub mod match_3_d_work_profile_row_type;
pub mod match_3_d_work_profile_table;
pub mod match_3_d_work_publish_input_type;
pub mod match_3_d_work_snapshot_type;
pub mod match_3_d_work_update_input_type;
pub mod match_3_d_works_list_input_type;
pub mod match_3_d_works_procedure_result_type;
@@ -499,33 +514,58 @@ pub mod puzzle_agent_message_finalize_input_type;
pub mod puzzle_agent_message_kind_type;
pub mod puzzle_agent_message_role_type;
pub mod puzzle_agent_message_row_type;
pub mod puzzle_agent_message_snapshot_type;
pub mod puzzle_agent_message_submit_input_type;
pub mod puzzle_agent_message_table;
pub mod puzzle_agent_session_create_input_type;
pub mod puzzle_agent_session_get_input_type;
pub mod puzzle_agent_session_procedure_result_type;
pub mod puzzle_agent_session_row_type;
pub mod puzzle_agent_session_snapshot_type;
pub mod puzzle_agent_session_table;
pub mod puzzle_agent_stage_type;
pub mod puzzle_agent_suggested_action_type;
pub mod puzzle_anchor_item_type;
pub mod puzzle_anchor_pack_type;
pub mod puzzle_anchor_status_type;
pub mod puzzle_audio_asset_type;
pub mod puzzle_board_snapshot_type;
pub mod puzzle_cell_position_type;
pub mod puzzle_creator_intent_type;
pub mod puzzle_draft_compile_input_type;
pub mod puzzle_draft_level_type;
pub mod puzzle_event_kind_type;
pub mod puzzle_event_table;
pub mod puzzle_event_type;
pub mod puzzle_form_draft_save_input_type;
pub mod puzzle_form_draft_type;
pub mod puzzle_gallery_view_table;
pub mod puzzle_generated_image_candidate_type;
pub mod puzzle_generated_images_save_input_type;
pub mod puzzle_leaderboard_entry_row_type;
pub mod puzzle_leaderboard_entry_table;
pub mod puzzle_leaderboard_entry_type;
pub mod puzzle_leaderboard_submit_input_type;
pub mod puzzle_merged_group_state_type;
pub mod puzzle_piece_state_type;
pub mod puzzle_publication_status_type;
pub mod puzzle_publish_input_type;
pub mod puzzle_recommended_next_work_type;
pub mod puzzle_result_draft_type;
pub mod puzzle_result_preview_blocker_type;
pub mod puzzle_result_preview_envelope_type;
pub mod puzzle_result_preview_finding_type;
pub mod puzzle_run_drag_input_type;
pub mod puzzle_run_get_input_type;
pub mod puzzle_run_next_level_input_type;
pub mod puzzle_run_pause_input_type;
pub mod puzzle_run_procedure_result_type;
pub mod puzzle_run_prop_input_type;
pub mod puzzle_run_snapshot_type;
pub mod puzzle_run_start_input_type;
pub mod puzzle_run_swap_input_type;
pub mod puzzle_runtime_level_snapshot_type;
pub mod puzzle_runtime_level_status_type;
pub mod puzzle_runtime_run_row_type;
pub mod puzzle_runtime_run_table;
pub mod puzzle_select_cover_image_input_type;
@@ -537,6 +577,7 @@ pub mod puzzle_work_point_incentive_claim_input_type;
pub mod puzzle_work_procedure_result_type;
pub mod puzzle_work_profile_row_type;
pub mod puzzle_work_profile_table;
pub mod puzzle_work_profile_type;
pub mod puzzle_work_remix_input_type;
pub mod puzzle_work_upsert_input_type;
pub mod puzzle_works_list_input_type;
@@ -728,30 +769,41 @@ pub mod seed_analytics_date_dimensions_reducer;
pub mod select_puzzle_cover_image_procedure;
pub mod square_hole_agent_message_finalize_input_type;
pub mod square_hole_agent_message_row_type;
pub mod square_hole_agent_message_snapshot_type;
pub mod square_hole_agent_message_submit_input_type;
pub mod square_hole_agent_message_table;
pub mod square_hole_agent_session_create_input_type;
pub mod square_hole_agent_session_get_input_type;
pub mod square_hole_agent_session_procedure_result_type;
pub mod square_hole_agent_session_row_type;
pub mod square_hole_agent_session_snapshot_type;
pub mod square_hole_agent_session_table;
pub mod square_hole_creator_config_snapshot_type;
pub mod square_hole_draft_compile_input_type;
pub mod square_hole_draft_snapshot_type;
pub mod square_hole_drop_feedback_snapshot_type;
pub mod square_hole_drop_shape_procedure_result_type;
pub mod square_hole_hole_option_snapshot_type;
pub mod square_hole_hole_snapshot_type;
pub mod square_hole_run_drop_input_type;
pub mod square_hole_run_get_input_type;
pub mod square_hole_run_procedure_result_type;
pub mod square_hole_run_restart_input_type;
pub mod square_hole_run_snapshot_type;
pub mod square_hole_run_start_input_type;
pub mod square_hole_run_stop_input_type;
pub mod square_hole_run_time_up_input_type;
pub mod square_hole_runtime_run_row_type;
pub mod square_hole_runtime_run_table;
pub mod square_hole_shape_option_snapshot_type;
pub mod square_hole_shape_snapshot_type;
pub mod square_hole_work_delete_input_type;
pub mod square_hole_work_get_input_type;
pub mod square_hole_work_procedure_result_type;
pub mod square_hole_work_profile_row_type;
pub mod square_hole_work_profile_table;
pub mod square_hole_work_publish_input_type;
pub mod square_hole_work_snapshot_type;
pub mod square_hole_work_update_input_type;
pub mod square_hole_works_list_input_type;
pub mod square_hole_works_procedure_result_type;
@@ -828,24 +880,31 @@ pub mod user_browse_history_table;
pub mod user_browse_history_type;
pub mod visual_novel_agent_message_finalize_input_type;
pub mod visual_novel_agent_message_row_type;
pub mod visual_novel_agent_message_snapshot_type;
pub mod visual_novel_agent_message_submit_input_type;
pub mod visual_novel_agent_message_table;
pub mod visual_novel_agent_session_create_input_type;
pub mod visual_novel_agent_session_get_input_type;
pub mod visual_novel_agent_session_procedure_result_type;
pub mod visual_novel_agent_session_row_type;
pub mod visual_novel_agent_session_snapshot_type;
pub mod visual_novel_agent_session_table;
pub mod visual_novel_history_procedure_result_type;
pub mod visual_novel_json_field_type;
pub mod visual_novel_json_value_type;
pub mod visual_novel_run_get_input_type;
pub mod visual_novel_run_procedure_result_type;
pub mod visual_novel_run_snapshot_type;
pub mod visual_novel_run_snapshot_upsert_input_type;
pub mod visual_novel_run_start_input_type;
pub mod visual_novel_runtime_event_procedure_result_type;
pub mod visual_novel_runtime_event_record_input_type;
pub mod visual_novel_runtime_event_snapshot_type;
pub mod visual_novel_runtime_event_table;
pub mod visual_novel_runtime_event_type;
pub mod visual_novel_runtime_history_append_input_type;
pub mod visual_novel_runtime_history_entry_row_type;
pub mod visual_novel_runtime_history_entry_snapshot_type;
pub mod visual_novel_runtime_history_entry_table;
pub mod visual_novel_runtime_history_list_input_type;
pub mod visual_novel_runtime_run_row_type;
@@ -857,6 +916,7 @@ pub mod visual_novel_work_procedure_result_type;
pub mod visual_novel_work_profile_row_type;
pub mod visual_novel_work_profile_table;
pub mod visual_novel_work_publish_input_type;
pub mod visual_novel_work_snapshot_type;
pub mod visual_novel_work_update_input_type;
pub mod visual_novel_works_list_input_type;
pub mod visual_novel_works_procedure_result_type;
@@ -950,6 +1010,7 @@ pub use auth_store_snapshot_type::AuthStoreSnapshot;
pub use auth_store_snapshot_upsert_input_type::AuthStoreSnapshotUpsertInput;
pub use authorize_database_migration_operator_procedure::authorize_database_migration_operator;
pub use bark_battle_draft_config_row_type::BarkBattleDraftConfigRow;
pub use bark_battle_draft_config_snapshot_type::BarkBattleDraftConfigSnapshot;
pub use bark_battle_draft_config_table::*;
pub use bark_battle_draft_config_upsert_input_type::BarkBattleDraftConfigUpsertInput;
pub use bark_battle_draft_create_input_type::BarkBattleDraftCreateInput;
@@ -962,8 +1023,10 @@ pub use bark_battle_published_config_row_type::BarkBattlePublishedConfigRow;
pub use bark_battle_published_config_table::*;
pub use bark_battle_run_finish_input_type::BarkBattleRunFinishInput;
pub use bark_battle_run_get_input_type::BarkBattleRunGetInput;
pub use bark_battle_run_snapshot_type::BarkBattleRunSnapshot;
pub use bark_battle_run_start_input_type::BarkBattleRunStartInput;
pub use bark_battle_runtime_config_get_input_type::BarkBattleRuntimeConfigGetInput;
pub use bark_battle_runtime_config_snapshot_type::BarkBattleRuntimeConfigSnapshot;
pub use bark_battle_runtime_run_row_type::BarkBattleRuntimeRunRow;
pub use bark_battle_runtime_run_table::*;
pub use bark_battle_score_record_row_type::BarkBattleScoreRecordRow;
@@ -1015,16 +1078,20 @@ pub use big_fish_run_get_input_type::BigFishRunGetInput;
pub use big_fish_run_procedure_result_type::BigFishRunProcedureResult;
pub use big_fish_run_start_input_type::BigFishRunStartInput;
pub use big_fish_run_status_type::BigFishRunStatus;
pub use big_fish_runtime_entity_snapshot_type::BigFishRuntimeEntitySnapshot;
pub use big_fish_runtime_params_type::BigFishRuntimeParams;
pub use big_fish_runtime_run_table::*;
pub use big_fish_runtime_run_type::BigFishRuntimeRun;
pub use big_fish_runtime_snapshot_type::BigFishRuntimeSnapshot;
pub use big_fish_session_create_input_type::BigFishSessionCreateInput;
pub use big_fish_session_get_input_type::BigFishSessionGetInput;
pub use big_fish_session_procedure_result_type::BigFishSessionProcedureResult;
pub use big_fish_session_snapshot_type::BigFishSessionSnapshot;
pub use big_fish_vector_2_type::BigFishVector2;
pub use big_fish_work_delete_input_type::BigFishWorkDeleteInput;
pub use big_fish_work_like_record_input_type::BigFishWorkLikeRecordInput;
pub use big_fish_work_remix_input_type::BigFishWorkRemixInput;
pub use big_fish_work_summary_snapshot_type::BigFishWorkSummarySnapshot;
pub use big_fish_works_list_input_type::BigFishWorksListInput;
pub use big_fish_works_procedure_result_type::BigFishWorksProcedureResult;
pub use bind_asset_object_to_entity_and_return_procedure::bind_asset_object_to_entity_and_return;
@@ -1257,30 +1324,38 @@ pub use list_visual_novel_works_procedure::list_visual_novel_works;
pub use mark_profile_recharge_order_paid_and_return_procedure::mark_profile_recharge_order_paid_and_return;
pub use match_3_d_agent_message_finalize_input_type::Match3DAgentMessageFinalizeInput;
pub use match_3_d_agent_message_row_type::Match3DAgentMessageRow;
pub use match_3_d_agent_message_snapshot_type::Match3DAgentMessageSnapshot;
pub use match_3_d_agent_message_submit_input_type::Match3DAgentMessageSubmitInput;
pub use match_3_d_agent_message_table::*;
pub use match_3_d_agent_session_create_input_type::Match3DAgentSessionCreateInput;
pub use match_3_d_agent_session_get_input_type::Match3DAgentSessionGetInput;
pub use match_3_d_agent_session_procedure_result_type::Match3DAgentSessionProcedureResult;
pub use match_3_d_agent_session_row_type::Match3DAgentSessionRow;
pub use match_3_d_agent_session_snapshot_type::Match3DAgentSessionSnapshot;
pub use match_3_d_agent_session_table::*;
pub use match_3_d_click_item_procedure_result_type::Match3DClickItemProcedureResult;
pub use match_3_d_creator_config_snapshot_type::Match3DCreatorConfigSnapshot;
pub use match_3_d_draft_compile_input_type::Match3DDraftCompileInput;
pub use match_3_d_draft_snapshot_type::Match3DDraftSnapshot;
pub use match_3_d_item_snapshot_type::Match3DItemSnapshot;
pub use match_3_d_run_click_input_type::Match3DRunClickInput;
pub use match_3_d_run_get_input_type::Match3DRunGetInput;
pub use match_3_d_run_procedure_result_type::Match3DRunProcedureResult;
pub use match_3_d_run_restart_input_type::Match3DRunRestartInput;
pub use match_3_d_run_snapshot_type::Match3DRunSnapshot;
pub use match_3_d_run_start_input_type::Match3DRunStartInput;
pub use match_3_d_run_stop_input_type::Match3DRunStopInput;
pub use match_3_d_run_time_up_input_type::Match3DRunTimeUpInput;
pub use match_3_d_runtime_run_row_type::Match3DRuntimeRunRow;
pub use match_3_d_runtime_run_table::*;
pub use match_3_d_tray_slot_snapshot_type::Match3DTraySlotSnapshot;
pub use match_3_d_work_delete_input_type::Match3DWorkDeleteInput;
pub use match_3_d_work_get_input_type::Match3DWorkGetInput;
pub use match_3_d_work_procedure_result_type::Match3DWorkProcedureResult;
pub use match_3_d_work_profile_row_type::Match3DWorkProfileRow;
pub use match_3_d_work_profile_table::*;
pub use match_3_d_work_publish_input_type::Match3DWorkPublishInput;
pub use match_3_d_work_snapshot_type::Match3DWorkSnapshot;
pub use match_3_d_work_update_input_type::Match3DWorkUpdateInput;
pub use match_3_d_works_list_input_type::Match3DWorksListInput;
pub use match_3_d_works_procedure_result_type::Match3DWorksProcedureResult;
@@ -1354,33 +1429,58 @@ pub use puzzle_agent_message_finalize_input_type::PuzzleAgentMessageFinalizeInpu
pub use puzzle_agent_message_kind_type::PuzzleAgentMessageKind;
pub use puzzle_agent_message_role_type::PuzzleAgentMessageRole;
pub use puzzle_agent_message_row_type::PuzzleAgentMessageRow;
pub use puzzle_agent_message_snapshot_type::PuzzleAgentMessageSnapshot;
pub use puzzle_agent_message_submit_input_type::PuzzleAgentMessageSubmitInput;
pub use puzzle_agent_message_table::*;
pub use puzzle_agent_session_create_input_type::PuzzleAgentSessionCreateInput;
pub use puzzle_agent_session_get_input_type::PuzzleAgentSessionGetInput;
pub use puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult;
pub use puzzle_agent_session_row_type::PuzzleAgentSessionRow;
pub use puzzle_agent_session_snapshot_type::PuzzleAgentSessionSnapshot;
pub use puzzle_agent_session_table::*;
pub use puzzle_agent_stage_type::PuzzleAgentStage;
pub use puzzle_agent_suggested_action_type::PuzzleAgentSuggestedAction;
pub use puzzle_anchor_item_type::PuzzleAnchorItem;
pub use puzzle_anchor_pack_type::PuzzleAnchorPack;
pub use puzzle_anchor_status_type::PuzzleAnchorStatus;
pub use puzzle_audio_asset_type::PuzzleAudioAsset;
pub use puzzle_board_snapshot_type::PuzzleBoardSnapshot;
pub use puzzle_cell_position_type::PuzzleCellPosition;
pub use puzzle_creator_intent_type::PuzzleCreatorIntent;
pub use puzzle_draft_compile_input_type::PuzzleDraftCompileInput;
pub use puzzle_draft_level_type::PuzzleDraftLevel;
pub use puzzle_event_kind_type::PuzzleEventKind;
pub use puzzle_event_table::*;
pub use puzzle_event_type::PuzzleEvent;
pub use puzzle_form_draft_save_input_type::PuzzleFormDraftSaveInput;
pub use puzzle_form_draft_type::PuzzleFormDraft;
pub use puzzle_gallery_view_table::*;
pub use puzzle_generated_image_candidate_type::PuzzleGeneratedImageCandidate;
pub use puzzle_generated_images_save_input_type::PuzzleGeneratedImagesSaveInput;
pub use puzzle_leaderboard_entry_row_type::PuzzleLeaderboardEntryRow;
pub use puzzle_leaderboard_entry_table::*;
pub use puzzle_leaderboard_entry_type::PuzzleLeaderboardEntry;
pub use puzzle_leaderboard_submit_input_type::PuzzleLeaderboardSubmitInput;
pub use puzzle_merged_group_state_type::PuzzleMergedGroupState;
pub use puzzle_piece_state_type::PuzzlePieceState;
pub use puzzle_publication_status_type::PuzzlePublicationStatus;
pub use puzzle_publish_input_type::PuzzlePublishInput;
pub use puzzle_recommended_next_work_type::PuzzleRecommendedNextWork;
pub use puzzle_result_draft_type::PuzzleResultDraft;
pub use puzzle_result_preview_blocker_type::PuzzleResultPreviewBlocker;
pub use puzzle_result_preview_envelope_type::PuzzleResultPreviewEnvelope;
pub use puzzle_result_preview_finding_type::PuzzleResultPreviewFinding;
pub use puzzle_run_drag_input_type::PuzzleRunDragInput;
pub use puzzle_run_get_input_type::PuzzleRunGetInput;
pub use puzzle_run_next_level_input_type::PuzzleRunNextLevelInput;
pub use puzzle_run_pause_input_type::PuzzleRunPauseInput;
pub use puzzle_run_procedure_result_type::PuzzleRunProcedureResult;
pub use puzzle_run_prop_input_type::PuzzleRunPropInput;
pub use puzzle_run_snapshot_type::PuzzleRunSnapshot;
pub use puzzle_run_start_input_type::PuzzleRunStartInput;
pub use puzzle_run_swap_input_type::PuzzleRunSwapInput;
pub use puzzle_runtime_level_snapshot_type::PuzzleRuntimeLevelSnapshot;
pub use puzzle_runtime_level_status_type::PuzzleRuntimeLevelStatus;
pub use puzzle_runtime_run_row_type::PuzzleRuntimeRunRow;
pub use puzzle_runtime_run_table::*;
pub use puzzle_select_cover_image_input_type::PuzzleSelectCoverImageInput;
@@ -1392,6 +1492,7 @@ pub use puzzle_work_point_incentive_claim_input_type::PuzzleWorkPointIncentiveCl
pub use puzzle_work_procedure_result_type::PuzzleWorkProcedureResult;
pub use puzzle_work_profile_row_type::PuzzleWorkProfileRow;
pub use puzzle_work_profile_table::*;
pub use puzzle_work_profile_type::PuzzleWorkProfile;
pub use puzzle_work_remix_input_type::PuzzleWorkRemixInput;
pub use puzzle_work_upsert_input_type::PuzzleWorkUpsertInput;
pub use puzzle_works_list_input_type::PuzzleWorksListInput;
@@ -1583,30 +1684,41 @@ pub use seed_analytics_date_dimensions_reducer::seed_analytics_date_dimensions;
pub use select_puzzle_cover_image_procedure::select_puzzle_cover_image;
pub use square_hole_agent_message_finalize_input_type::SquareHoleAgentMessageFinalizeInput;
pub use square_hole_agent_message_row_type::SquareHoleAgentMessageRow;
pub use square_hole_agent_message_snapshot_type::SquareHoleAgentMessageSnapshot;
pub use square_hole_agent_message_submit_input_type::SquareHoleAgentMessageSubmitInput;
pub use square_hole_agent_message_table::*;
pub use square_hole_agent_session_create_input_type::SquareHoleAgentSessionCreateInput;
pub use square_hole_agent_session_get_input_type::SquareHoleAgentSessionGetInput;
pub use square_hole_agent_session_procedure_result_type::SquareHoleAgentSessionProcedureResult;
pub use square_hole_agent_session_row_type::SquareHoleAgentSessionRow;
pub use square_hole_agent_session_snapshot_type::SquareHoleAgentSessionSnapshot;
pub use square_hole_agent_session_table::*;
pub use square_hole_creator_config_snapshot_type::SquareHoleCreatorConfigSnapshot;
pub use square_hole_draft_compile_input_type::SquareHoleDraftCompileInput;
pub use square_hole_draft_snapshot_type::SquareHoleDraftSnapshot;
pub use square_hole_drop_feedback_snapshot_type::SquareHoleDropFeedbackSnapshot;
pub use square_hole_drop_shape_procedure_result_type::SquareHoleDropShapeProcedureResult;
pub use square_hole_hole_option_snapshot_type::SquareHoleHoleOptionSnapshot;
pub use square_hole_hole_snapshot_type::SquareHoleHoleSnapshot;
pub use square_hole_run_drop_input_type::SquareHoleRunDropInput;
pub use square_hole_run_get_input_type::SquareHoleRunGetInput;
pub use square_hole_run_procedure_result_type::SquareHoleRunProcedureResult;
pub use square_hole_run_restart_input_type::SquareHoleRunRestartInput;
pub use square_hole_run_snapshot_type::SquareHoleRunSnapshot;
pub use square_hole_run_start_input_type::SquareHoleRunStartInput;
pub use square_hole_run_stop_input_type::SquareHoleRunStopInput;
pub use square_hole_run_time_up_input_type::SquareHoleRunTimeUpInput;
pub use square_hole_runtime_run_row_type::SquareHoleRuntimeRunRow;
pub use square_hole_runtime_run_table::*;
pub use square_hole_shape_option_snapshot_type::SquareHoleShapeOptionSnapshot;
pub use square_hole_shape_snapshot_type::SquareHoleShapeSnapshot;
pub use square_hole_work_delete_input_type::SquareHoleWorkDeleteInput;
pub use square_hole_work_get_input_type::SquareHoleWorkGetInput;
pub use square_hole_work_procedure_result_type::SquareHoleWorkProcedureResult;
pub use square_hole_work_profile_row_type::SquareHoleWorkProfileRow;
pub use square_hole_work_profile_table::*;
pub use square_hole_work_publish_input_type::SquareHoleWorkPublishInput;
pub use square_hole_work_snapshot_type::SquareHoleWorkSnapshot;
pub use square_hole_work_update_input_type::SquareHoleWorkUpdateInput;
pub use square_hole_works_list_input_type::SquareHoleWorksListInput;
pub use square_hole_works_procedure_result_type::SquareHoleWorksProcedureResult;
@@ -1683,24 +1795,31 @@ pub use user_browse_history_table::*;
pub use user_browse_history_type::UserBrowseHistory;
pub use visual_novel_agent_message_finalize_input_type::VisualNovelAgentMessageFinalizeInput;
pub use visual_novel_agent_message_row_type::VisualNovelAgentMessageRow;
pub use visual_novel_agent_message_snapshot_type::VisualNovelAgentMessageSnapshot;
pub use visual_novel_agent_message_submit_input_type::VisualNovelAgentMessageSubmitInput;
pub use visual_novel_agent_message_table::*;
pub use visual_novel_agent_session_create_input_type::VisualNovelAgentSessionCreateInput;
pub use visual_novel_agent_session_get_input_type::VisualNovelAgentSessionGetInput;
pub use visual_novel_agent_session_procedure_result_type::VisualNovelAgentSessionProcedureResult;
pub use visual_novel_agent_session_row_type::VisualNovelAgentSessionRow;
pub use visual_novel_agent_session_snapshot_type::VisualNovelAgentSessionSnapshot;
pub use visual_novel_agent_session_table::*;
pub use visual_novel_history_procedure_result_type::VisualNovelHistoryProcedureResult;
pub use visual_novel_json_field_type::VisualNovelJsonField;
pub use visual_novel_json_value_type::VisualNovelJsonValue;
pub use visual_novel_run_get_input_type::VisualNovelRunGetInput;
pub use visual_novel_run_procedure_result_type::VisualNovelRunProcedureResult;
pub use visual_novel_run_snapshot_type::VisualNovelRunSnapshot;
pub use visual_novel_run_snapshot_upsert_input_type::VisualNovelRunSnapshotUpsertInput;
pub use visual_novel_run_start_input_type::VisualNovelRunStartInput;
pub use visual_novel_runtime_event_procedure_result_type::VisualNovelRuntimeEventProcedureResult;
pub use visual_novel_runtime_event_record_input_type::VisualNovelRuntimeEventRecordInput;
pub use visual_novel_runtime_event_snapshot_type::VisualNovelRuntimeEventSnapshot;
pub use visual_novel_runtime_event_table::*;
pub use visual_novel_runtime_event_type::VisualNovelRuntimeEvent;
pub use visual_novel_runtime_history_append_input_type::VisualNovelRuntimeHistoryAppendInput;
pub use visual_novel_runtime_history_entry_row_type::VisualNovelRuntimeHistoryEntryRow;
pub use visual_novel_runtime_history_entry_snapshot_type::VisualNovelRuntimeHistoryEntrySnapshot;
pub use visual_novel_runtime_history_entry_table::*;
pub use visual_novel_runtime_history_list_input_type::VisualNovelRuntimeHistoryListInput;
pub use visual_novel_runtime_run_row_type::VisualNovelRuntimeRunRow;
@@ -1712,6 +1831,7 @@ pub use visual_novel_work_procedure_result_type::VisualNovelWorkProcedureResult;
pub use visual_novel_work_profile_row_type::VisualNovelWorkProfileRow;
pub use visual_novel_work_profile_table::*;
pub use visual_novel_work_publish_input_type::VisualNovelWorkPublishInput;
pub use visual_novel_work_snapshot_type::VisualNovelWorkSnapshot;
pub use visual_novel_work_update_input_type::VisualNovelWorkUpdateInput;
pub use visual_novel_works_list_input_type::VisualNovelWorksListInput;
pub use visual_novel_works_procedure_result_type::VisualNovelWorksProcedureResult;
@@ -2052,6 +2172,7 @@ pub struct DbUpdate {
puzzle_agent_message: __sdk::TableUpdate<PuzzleAgentMessageRow>,
puzzle_agent_session: __sdk::TableUpdate<PuzzleAgentSessionRow>,
puzzle_event: __sdk::TableUpdate<PuzzleEvent>,
puzzle_gallery_view: __sdk::TableUpdate<PuzzleWorkProfile>,
puzzle_leaderboard_entry: __sdk::TableUpdate<PuzzleLeaderboardEntryRow>,
puzzle_runtime_run: __sdk::TableUpdate<PuzzleRuntimeRunRow>,
puzzle_work_profile: __sdk::TableUpdate<PuzzleWorkProfileRow>,
@@ -2287,6 +2408,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
"puzzle_event" => db_update
.puzzle_event
.append(puzzle_event_table::parse_table_update(table_update)?),
"puzzle_gallery_view" => db_update
.puzzle_gallery_view
.append(puzzle_gallery_view_table::parse_table_update(table_update)?),
"puzzle_leaderboard_entry" => db_update.puzzle_leaderboard_entry.append(
puzzle_leaderboard_entry_table::parse_table_update(table_update)?,
),
@@ -2842,6 +2966,10 @@ impl __sdk::DbUpdate for DbUpdate {
&self.visual_novel_work_profile,
)
.with_updates_by_pk(|row| &row.profile_id);
diff.puzzle_gallery_view = cache.apply_diff_to_table::<PuzzleWorkProfile>(
"puzzle_gallery_view",
&self.puzzle_gallery_view,
);
diff
}
@@ -3041,6 +3169,9 @@ impl __sdk::DbUpdate for DbUpdate {
"puzzle_event" => db_update
.puzzle_event
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_gallery_view" => db_update
.puzzle_gallery_view
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_leaderboard_entry" => db_update
.puzzle_leaderboard_entry
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
@@ -3321,6 +3452,9 @@ impl __sdk::DbUpdate for DbUpdate {
"puzzle_event" => db_update
.puzzle_event
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_gallery_view" => db_update
.puzzle_gallery_view
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_leaderboard_entry" => db_update
.puzzle_leaderboard_entry
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
@@ -3477,6 +3611,7 @@ pub struct AppliedDiff<'r> {
puzzle_agent_message: __sdk::TableAppliedDiff<'r, PuzzleAgentMessageRow>,
puzzle_agent_session: __sdk::TableAppliedDiff<'r, PuzzleAgentSessionRow>,
puzzle_event: __sdk::TableAppliedDiff<'r, PuzzleEvent>,
puzzle_gallery_view: __sdk::TableAppliedDiff<'r, PuzzleWorkProfile>,
puzzle_leaderboard_entry: __sdk::TableAppliedDiff<'r, PuzzleLeaderboardEntryRow>,
puzzle_runtime_run: __sdk::TableAppliedDiff<'r, PuzzleRuntimeRunRow>,
puzzle_work_profile: __sdk::TableAppliedDiff<'r, PuzzleWorkProfileRow>,
@@ -3824,6 +3959,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
&self.puzzle_event,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleWorkProfile>(
"puzzle_gallery_view",
&self.puzzle_gallery_view,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleLeaderboardEntryRow>(
"puzzle_leaderboard_entry",
&self.puzzle_leaderboard_entry,
@@ -4665,6 +4805,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
puzzle_agent_message_table::register_table(client_cache);
puzzle_agent_session_table::register_table(client_cache);
puzzle_event_table::register_table(client_cache);
puzzle_gallery_view_table::register_table(client_cache);
puzzle_leaderboard_entry_table::register_table(client_cache);
puzzle_runtime_run_table::register_table(client_cache);
puzzle_work_profile_table::register_table(client_cache);
@@ -4756,6 +4897,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
"puzzle_agent_message",
"puzzle_agent_session",
"puzzle_event",
"puzzle_gallery_view",
"puzzle_leaderboard_entry",
"puzzle_runtime_run",
"puzzle_work_profile",

View File

@@ -0,0 +1,116 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_anchor_pack_type::PuzzleAnchorPack;
use super::puzzle_draft_level_type::PuzzleDraftLevel;
use super::puzzle_publication_status_type::PuzzlePublicationStatus;
use super::puzzle_work_profile_type::PuzzleWorkProfile;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_gallery_view`.
///
/// Obtain a handle from the [`PuzzleGalleryViewTableAccess::puzzle_gallery_view`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_gallery_view()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_gallery_view().on_insert(...)`.
pub struct PuzzleGalleryViewTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleWorkProfile>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_gallery_view`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleGalleryViewTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleGalleryViewTableHandle`], which mediates access to the table `puzzle_gallery_view`.
fn puzzle_gallery_view(&self) -> PuzzleGalleryViewTableHandle<'_>;
}
impl PuzzleGalleryViewTableAccess for super::RemoteTables {
fn puzzle_gallery_view(&self) -> PuzzleGalleryViewTableHandle<'_> {
PuzzleGalleryViewTableHandle {
imp: self
.imp
.get_table::<PuzzleWorkProfile>("puzzle_gallery_view"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleGalleryViewInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleGalleryViewDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleGalleryViewTableHandle<'ctx> {
type Row = PuzzleWorkProfile;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleWorkProfile> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleGalleryViewInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleGalleryViewInsertCallbackId {
PuzzleGalleryViewInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleGalleryViewInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleGalleryViewDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleGalleryViewDeleteCallbackId {
PuzzleGalleryViewDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleGalleryViewDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table = client_cache.get_or_make_table::<PuzzleWorkProfile>("puzzle_gallery_view");
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleWorkProfile>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse("TableUpdate<PuzzleWorkProfile>", "TableUpdate")
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleWorkProfile`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_gallery_viewQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleWorkProfile`.
fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table<PuzzleWorkProfile>;
}
impl puzzle_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table<PuzzleWorkProfile> {
__sdk::__query_builder::Table::new("puzzle_gallery_view")
}
}

View File

@@ -5,6 +5,45 @@ use crate::module_bindings::delete_puzzle_work_procedure::delete_puzzle_work;
use crate::module_bindings::record_puzzle_work_like_procedure::record_puzzle_work_like;
use crate::module_bindings::remix_puzzle_work_procedure::remix_puzzle_work;
use crate::module_bindings::save_puzzle_ui_background_procedure::save_puzzle_ui_background;
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
const PUBLIC_WORK_PLAY_DAY_MICROS: i64 = 86_400_000_000;
const PUBLIC_WORK_RECENT_PLAY_WINDOW_DAYS: i64 = 7;
fn current_unix_micros() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_micros() as i64)
.unwrap_or(0)
}
fn current_public_work_day() -> i64 {
current_unix_micros().div_euclid(PUBLIC_WORK_PLAY_DAY_MICROS)
}
fn puzzle_gallery_recent_play_counts(connection: &DbConnection) -> HashMap<String, u32> {
let current_day = current_public_work_day();
let first_day = current_day - (PUBLIC_WORK_RECENT_PLAY_WINDOW_DAYS - 1);
let mut counts = HashMap::new();
for row in connection
.db()
.public_work_play_daily_stat()
.iter()
{
if row.source_type != "puzzle"
|| row.played_day < first_day
|| row.played_day > current_day
{
continue;
}
let entry: &mut u32 = counts.entry(row.profile_id).or_insert(0);
*entry = (*entry).saturating_add(row.play_count);
}
counts
}
impl SpacetimeClient {
pub async fn create_puzzle_agent_session(
@@ -397,15 +436,21 @@ impl SpacetimeClient {
pub async fn list_puzzle_gallery(
&self,
) -> Result<Vec<PuzzleWorkProfileRecord>, SpacetimeClientError> {
self.call_after_connect(move |connection, sender| {
connection
.procedures()
.list_puzzle_gallery_then(move |_, result| {
let mapped = result
.map_err(SpacetimeClientError::from_sdk_error)
.and_then(map_puzzle_works_procedure_result);
send_once(&sender, mapped);
});
self.read_after_connect(move |connection| {
let mut items = connection.db().puzzle_gallery_view().iter().collect::<Vec<_>>();
items.sort_by(|left, right| right.updated_at_micros.cmp(&left.updated_at_micros));
let recent_play_counts = puzzle_gallery_recent_play_counts(connection);
Ok(items
.into_iter()
.map(|item| {
let mut record = map_puzzle_work_profile(item);
record.recent_play_count_7d = recent_play_counts
.get(&record.profile_id)
.copied()
.unwrap_or(0);
record
})
.collect())
})
.await
}