调整图片画布路由和画布数据模型
将图片画布入口改为 /editor/canvas 新增 editor_canvas 表并关联 editor_project 默认画布 更新 project API 响应中的 canvas 快照兼容层 统一图片画布侧栏列表项和图标按钮组件 同步前端测试、SpacetimeDB bindings、技术文档和 TRACKING 记录
This commit is contained in:
@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
use shared_kernel::build_prefixed_uuid_id;
|
||||
use spacetime_client::{
|
||||
EditorCanvasViewportRecord, EditorProjectCreateRecordInput, EditorProjectGetRecordInput,
|
||||
EditorProjectLayoutSaveRecordInput, EditorProjectRecord,
|
||||
EditorCanvasRecord, EditorCanvasViewportRecord, EditorProjectCreateRecordInput,
|
||||
EditorProjectGetRecordInput, EditorProjectLayoutSaveRecordInput, EditorProjectRecord,
|
||||
EditorProjectResourceCreateRecordInput, EditorProjectResourceRecord, SpacetimeClientError,
|
||||
};
|
||||
|
||||
@@ -120,12 +120,25 @@ pub struct EditorImageGenerationResponse {
|
||||
pub struct EditorProjectPayload {
|
||||
project_id: String,
|
||||
title: String,
|
||||
canvas: EditorCanvasPayload,
|
||||
viewport: EditorCanvasViewportPayload,
|
||||
layers: Value,
|
||||
resources: Vec<EditorProjectResourcePayload>,
|
||||
updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EditorCanvasPayload {
|
||||
canvas_id: String,
|
||||
project_id: String,
|
||||
title: String,
|
||||
viewport: EditorCanvasViewportPayload,
|
||||
layers: Value,
|
||||
created_at: String,
|
||||
updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EditorProjectResourcePayload {
|
||||
@@ -417,9 +430,11 @@ pub async fn edit_editor_image(
|
||||
}
|
||||
|
||||
fn editor_project_payload_from_record(record: EditorProjectRecord) -> EditorProjectPayload {
|
||||
let canvas = editor_canvas_payload_from_record(record.canvas);
|
||||
EditorProjectPayload {
|
||||
project_id: record.project_id,
|
||||
title: record.title,
|
||||
canvas,
|
||||
viewport: EditorCanvasViewportPayload {
|
||||
x: record.viewport.x,
|
||||
y: record.viewport.y,
|
||||
@@ -435,6 +450,22 @@ fn editor_project_payload_from_record(record: EditorProjectRecord) -> EditorProj
|
||||
}
|
||||
}
|
||||
|
||||
fn editor_canvas_payload_from_record(record: EditorCanvasRecord) -> EditorCanvasPayload {
|
||||
EditorCanvasPayload {
|
||||
canvas_id: record.canvas_id,
|
||||
project_id: record.project_id,
|
||||
title: record.title,
|
||||
viewport: EditorCanvasViewportPayload {
|
||||
x: record.viewport.x,
|
||||
y: record.viewport.y,
|
||||
scale: record.viewport.scale,
|
||||
},
|
||||
layers: record.layers,
|
||||
created_at: record.created_at,
|
||||
updated_at: record.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn editor_project_resource_payload_from_record(
|
||||
record: EditorProjectResourceRecord,
|
||||
) -> EditorProjectResourcePayload {
|
||||
|
||||
@@ -30,9 +30,9 @@ pub use mapper::{
|
||||
CustomWorldPublishGateRecord, CustomWorldPublishWorldRecord,
|
||||
CustomWorldPublishWorldRecordInput, CustomWorldPublishedProfileCompileRecord,
|
||||
CustomWorldResultPreviewBlockerRecord, CustomWorldSupportedActionRecord,
|
||||
CustomWorldWorkSummaryRecord, EditorCanvasViewportRecord, EditorProjectCreateRecordInput,
|
||||
EditorProjectGetRecordInput, EditorProjectLayoutSaveRecordInput, EditorProjectRecord,
|
||||
EditorProjectResourceCreateRecordInput, EditorProjectResourceRecord,
|
||||
CustomWorldWorkSummaryRecord, EditorCanvasRecord, EditorCanvasViewportRecord,
|
||||
EditorProjectCreateRecordInput, EditorProjectGetRecordInput, EditorProjectLayoutSaveRecordInput,
|
||||
EditorProjectRecord, EditorProjectResourceCreateRecordInput, EditorProjectResourceRecord,
|
||||
ExternalGenerationJobClaimRecordInput,
|
||||
ExternalGenerationJobCompleteRecordInput, ExternalGenerationJobEnqueueRecordInput,
|
||||
ExternalGenerationJobFailRecordInput, ExternalGenerationJobGetRecordInput,
|
||||
|
||||
@@ -43,8 +43,8 @@ pub use self::combat::{
|
||||
ResolveCombatActionRecord,
|
||||
};
|
||||
pub use self::editor_project::{
|
||||
EditorCanvasViewportRecord, EditorProjectCreateRecordInput, EditorProjectGetRecordInput,
|
||||
EditorProjectLayoutSaveRecordInput, EditorProjectRecord,
|
||||
EditorCanvasRecord, EditorCanvasViewportRecord, EditorProjectCreateRecordInput,
|
||||
EditorProjectGetRecordInput, EditorProjectLayoutSaveRecordInput, EditorProjectRecord,
|
||||
EditorProjectResourceCreateRecordInput, EditorProjectResourceRecord,
|
||||
};
|
||||
pub use self::common::{
|
||||
|
||||
@@ -12,6 +12,7 @@ pub struct EditorProjectRecord {
|
||||
pub project_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub title: String,
|
||||
pub canvas: EditorCanvasRecord,
|
||||
pub viewport: EditorCanvasViewportRecord,
|
||||
pub layers: serde_json::Value,
|
||||
pub resources: Vec<EditorProjectResourceRecord>,
|
||||
@@ -19,6 +20,17 @@ pub struct EditorProjectRecord {
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct EditorCanvasRecord {
|
||||
pub canvas_id: String,
|
||||
pub project_id: String,
|
||||
pub title: String,
|
||||
pub viewport: EditorCanvasViewportRecord,
|
||||
pub layers: serde_json::Value,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct EditorProjectResourceRecord {
|
||||
pub resource_id: String,
|
||||
@@ -178,19 +190,31 @@ pub(crate) fn map_editor_project_resource_procedure_result(
|
||||
fn map_editor_project_snapshot(
|
||||
snapshot: EditorProjectSnapshot,
|
||||
) -> Result<EditorProjectRecord, SpacetimeClientError> {
|
||||
let layers = serde_json::from_str(&snapshot.layers_json).map_err(|error| {
|
||||
let layers: serde_json::Value =
|
||||
serde_json::from_str(&snapshot.canvas.layers_json).map_err(|error| {
|
||||
SpacetimeClientError::validation_failed(format!("图片画布图层布局 JSON 无法解析:{error}"))
|
||||
})?;
|
||||
let viewport = EditorCanvasViewportRecord {
|
||||
x: snapshot.canvas.viewport.x,
|
||||
y: snapshot.canvas.viewport.y,
|
||||
scale: snapshot.canvas.viewport.scale,
|
||||
};
|
||||
let canvas = EditorCanvasRecord {
|
||||
canvas_id: snapshot.canvas.canvas_id,
|
||||
project_id: snapshot.canvas.project_id,
|
||||
title: snapshot.canvas.title,
|
||||
viewport: viewport.clone(),
|
||||
layers: layers.clone(),
|
||||
created_at: format_timestamp_micros(snapshot.canvas.created_at_micros),
|
||||
updated_at: format_timestamp_micros(snapshot.canvas.updated_at_micros),
|
||||
};
|
||||
|
||||
Ok(EditorProjectRecord {
|
||||
project_id: snapshot.project_id,
|
||||
owner_user_id: snapshot.owner_user_id,
|
||||
title: snapshot.title,
|
||||
viewport: EditorCanvasViewportRecord {
|
||||
x: snapshot.viewport.x,
|
||||
y: snapshot.viewport.y,
|
||||
scale: snapshot.viewport.scale,
|
||||
},
|
||||
canvas,
|
||||
viewport,
|
||||
layers,
|
||||
resources: snapshot
|
||||
.resources
|
||||
|
||||
@@ -347,6 +347,9 @@ pub mod delete_visual_novel_work_procedure;
|
||||
pub mod delete_wooden_fish_work_procedure;
|
||||
pub mod drag_puzzle_piece_or_group_procedure;
|
||||
pub mod drop_square_hole_shape_procedure;
|
||||
pub mod editor_canvas_snapshot_type;
|
||||
pub mod editor_canvas_table;
|
||||
pub mod editor_canvas_type;
|
||||
pub mod editor_project_create_input_type;
|
||||
pub mod editor_project_get_input_type;
|
||||
pub mod editor_project_get_recent_input_type;
|
||||
@@ -362,7 +365,6 @@ pub mod editor_project_table;
|
||||
pub mod editor_project_type;
|
||||
pub mod editor_project_viewport_snapshot_type;
|
||||
pub mod enqueue_external_generation_job_and_return_procedure;
|
||||
|
||||
pub mod ensure_analytics_date_dimension_for_date_reducer;
|
||||
pub mod equip_inventory_item_input_type;
|
||||
pub mod execute_custom_world_agent_action_procedure;
|
||||
@@ -409,7 +411,6 @@ pub mod get_custom_world_library_detail_procedure;
|
||||
pub mod get_editor_project_and_return_procedure;
|
||||
pub mod get_external_generation_job_and_return_procedure;
|
||||
pub mod get_external_generation_queue_stats_and_return_procedure;
|
||||
|
||||
pub mod get_jump_hop_agent_session_procedure;
|
||||
pub mod get_jump_hop_leaderboard_procedure;
|
||||
pub mod get_jump_hop_run_procedure;
|
||||
@@ -1504,6 +1505,9 @@ pub use delete_visual_novel_work_procedure::delete_visual_novel_work;
|
||||
pub use delete_wooden_fish_work_procedure::delete_wooden_fish_work;
|
||||
pub use drag_puzzle_piece_or_group_procedure::drag_puzzle_piece_or_group;
|
||||
pub use drop_square_hole_shape_procedure::drop_square_hole_shape;
|
||||
pub use editor_canvas_snapshot_type::EditorCanvasSnapshot;
|
||||
pub use editor_canvas_table::*;
|
||||
pub use editor_canvas_type::EditorCanvas;
|
||||
pub use editor_project_create_input_type::EditorProjectCreateInput;
|
||||
pub use editor_project_get_input_type::EditorProjectGetInput;
|
||||
pub use editor_project_get_recent_input_type::EditorProjectGetRecentInput;
|
||||
@@ -1519,7 +1523,6 @@ pub use editor_project_table::*;
|
||||
pub use editor_project_type::EditorProject;
|
||||
pub use editor_project_viewport_snapshot_type::EditorProjectViewportSnapshot;
|
||||
pub use enqueue_external_generation_job_and_return_procedure::enqueue_external_generation_job_and_return;
|
||||
|
||||
pub use ensure_analytics_date_dimension_for_date_reducer::ensure_analytics_date_dimension_for_date;
|
||||
pub use equip_inventory_item_input_type::EquipInventoryItemInput;
|
||||
pub use execute_custom_world_agent_action_procedure::execute_custom_world_agent_action;
|
||||
@@ -1566,7 +1569,6 @@ pub use get_custom_world_library_detail_procedure::get_custom_world_library_deta
|
||||
pub use get_editor_project_and_return_procedure::get_editor_project_and_return;
|
||||
pub use get_external_generation_job_and_return_procedure::get_external_generation_job_and_return;
|
||||
pub use get_external_generation_queue_stats_and_return_procedure::get_external_generation_queue_stats_and_return;
|
||||
|
||||
pub use get_jump_hop_agent_session_procedure::get_jump_hop_agent_session;
|
||||
pub use get_jump_hop_leaderboard_procedure::get_jump_hop_leaderboard;
|
||||
pub use get_jump_hop_run_procedure::get_jump_hop_run;
|
||||
@@ -2631,10 +2633,10 @@ pub struct DbUpdate {
|
||||
custom_world_session: __sdk::TableUpdate<CustomWorldSession>,
|
||||
database_migration_import_chunk: __sdk::TableUpdate<DatabaseMigrationImportChunk>,
|
||||
database_migration_operator: __sdk::TableUpdate<DatabaseMigrationOperator>,
|
||||
editor_canvas: __sdk::TableUpdate<EditorCanvas>,
|
||||
editor_project: __sdk::TableUpdate<EditorProject>,
|
||||
editor_project_resource: __sdk::TableUpdate<EditorProjectResource>,
|
||||
external_generation_job: __sdk::TableUpdate<ExternalGenerationJob>,
|
||||
|
||||
inventory_slot: __sdk::TableUpdate<InventorySlot>,
|
||||
jump_hop_agent_session: __sdk::TableUpdate<JumpHopAgentSessionRow>,
|
||||
jump_hop_event: __sdk::TableUpdate<JumpHopEventRow>,
|
||||
@@ -2847,6 +2849,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
||||
"database_migration_operator" => db_update.database_migration_operator.append(
|
||||
database_migration_operator_table::parse_table_update(table_update)?,
|
||||
),
|
||||
"editor_canvas" => db_update
|
||||
.editor_canvas
|
||||
.append(editor_canvas_table::parse_table_update(table_update)?),
|
||||
"editor_project" => db_update
|
||||
.editor_project
|
||||
.append(editor_project_table::parse_table_update(table_update)?),
|
||||
@@ -3316,6 +3321,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
&self.database_migration_operator,
|
||||
)
|
||||
.with_updates_by_pk(|row| &row.operator_identity);
|
||||
diff.editor_canvas = cache
|
||||
.apply_diff_to_table::<EditorCanvas>("editor_canvas", &self.editor_canvas)
|
||||
.with_updates_by_pk(|row| &row.canvas_id);
|
||||
diff.editor_project = cache
|
||||
.apply_diff_to_table::<EditorProject>("editor_project", &self.editor_project)
|
||||
.with_updates_by_pk(|row| &row.project_id);
|
||||
@@ -3331,7 +3339,6 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
&self.external_generation_job,
|
||||
)
|
||||
.with_updates_by_pk(|row| &row.job_id);
|
||||
|
||||
diff.inventory_slot = cache
|
||||
.apply_diff_to_table::<InventorySlot>("inventory_slot", &self.inventory_slot)
|
||||
.with_updates_by_pk(|row| &row.slot_id);
|
||||
@@ -3859,6 +3866,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"database_migration_operator" => db_update
|
||||
.database_migration_operator
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"editor_canvas" => db_update
|
||||
.editor_canvas
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"editor_project" => db_update
|
||||
.editor_project
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
@@ -4235,6 +4245,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"database_migration_operator" => db_update
|
||||
.database_migration_operator
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"editor_canvas" => db_update
|
||||
.editor_canvas
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"editor_project" => db_update
|
||||
.editor_project
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
@@ -4537,10 +4550,10 @@ pub struct AppliedDiff<'r> {
|
||||
custom_world_session: __sdk::TableAppliedDiff<'r, CustomWorldSession>,
|
||||
database_migration_import_chunk: __sdk::TableAppliedDiff<'r, DatabaseMigrationImportChunk>,
|
||||
database_migration_operator: __sdk::TableAppliedDiff<'r, DatabaseMigrationOperator>,
|
||||
editor_canvas: __sdk::TableAppliedDiff<'r, EditorCanvas>,
|
||||
editor_project: __sdk::TableAppliedDiff<'r, EditorProject>,
|
||||
editor_project_resource: __sdk::TableAppliedDiff<'r, EditorProjectResource>,
|
||||
external_generation_job: __sdk::TableAppliedDiff<'r, ExternalGenerationJob>,
|
||||
|
||||
inventory_slot: __sdk::TableAppliedDiff<'r, InventorySlot>,
|
||||
jump_hop_agent_session: __sdk::TableAppliedDiff<'r, JumpHopAgentSessionRow>,
|
||||
jump_hop_event: __sdk::TableAppliedDiff<'r, JumpHopEventRow>,
|
||||
@@ -4821,6 +4834,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
||||
&self.database_migration_operator,
|
||||
event,
|
||||
);
|
||||
callbacks.invoke_table_row_callbacks::<EditorCanvas>(
|
||||
"editor_canvas",
|
||||
&self.editor_canvas,
|
||||
event,
|
||||
);
|
||||
callbacks.invoke_table_row_callbacks::<EditorProject>(
|
||||
"editor_project",
|
||||
&self.editor_project,
|
||||
@@ -5918,10 +5936,10 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
||||
custom_world_session_table::register_table(client_cache);
|
||||
database_migration_import_chunk_table::register_table(client_cache);
|
||||
database_migration_operator_table::register_table(client_cache);
|
||||
editor_canvas_table::register_table(client_cache);
|
||||
editor_project_table::register_table(client_cache);
|
||||
editor_project_resource_table::register_table(client_cache);
|
||||
external_generation_job_table::register_table(client_cache);
|
||||
|
||||
inventory_slot_table::register_table(client_cache);
|
||||
jump_hop_agent_session_table::register_table(client_cache);
|
||||
jump_hop_event_table::register_table(client_cache);
|
||||
@@ -6042,10 +6060,10 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
||||
"custom_world_session",
|
||||
"database_migration_import_chunk",
|
||||
"database_migration_operator",
|
||||
"editor_canvas",
|
||||
"editor_project",
|
||||
"editor_project_resource",
|
||||
"external_generation_job",
|
||||
|
||||
"inventory_slot",
|
||||
"jump_hop_agent_session",
|
||||
"jump_hop_event",
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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 spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::editor_project_viewport_snapshot_type::EditorProjectViewportSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct EditorCanvasSnapshot {
|
||||
pub canvas_id: String,
|
||||
pub project_id: String,
|
||||
pub title: String,
|
||||
pub viewport: EditorProjectViewportSnapshot,
|
||||
pub layers_json: String,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for EditorCanvasSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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::editor_canvas_type::EditorCanvas;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `editor_canvas`.
|
||||
///
|
||||
/// Obtain a handle from the [`EditorCanvasTableAccess::editor_canvas`] method on [`super::RemoteTables`],
|
||||
/// like `ctx.db.editor_canvas()`.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.editor_canvas().on_insert(...)`.
|
||||
pub struct EditorCanvasTableHandle<'ctx> {
|
||||
imp: __sdk::TableHandle<EditorCanvas>,
|
||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the table `editor_canvas`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteTables`].
|
||||
pub trait EditorCanvasTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Obtain a [`EditorCanvasTableHandle`], which mediates access to the table `editor_canvas`.
|
||||
fn editor_canvas(&self) -> EditorCanvasTableHandle<'_>;
|
||||
}
|
||||
|
||||
impl EditorCanvasTableAccess for super::RemoteTables {
|
||||
fn editor_canvas(&self) -> EditorCanvasTableHandle<'_> {
|
||||
EditorCanvasTableHandle {
|
||||
imp: self.imp.get_table::<EditorCanvas>("editor_canvas"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EditorCanvasInsertCallbackId(__sdk::CallbackId);
|
||||
pub struct EditorCanvasDeleteCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::Table for EditorCanvasTableHandle<'ctx> {
|
||||
type Row = EditorCanvas;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = EditorCanvas> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = EditorCanvasInsertCallbackId;
|
||||
|
||||
fn on_insert(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> EditorCanvasInsertCallbackId {
|
||||
EditorCanvasInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_insert(&self, callback: EditorCanvasInsertCallbackId) {
|
||||
self.imp.remove_on_insert(callback.0)
|
||||
}
|
||||
|
||||
type DeleteCallbackId = EditorCanvasDeleteCallbackId;
|
||||
|
||||
fn on_delete(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||
) -> EditorCanvasDeleteCallbackId {
|
||||
EditorCanvasDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_delete(&self, callback: EditorCanvasDeleteCallbackId) {
|
||||
self.imp.remove_on_delete(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EditorCanvasUpdateCallbackId(__sdk::CallbackId);
|
||||
|
||||
impl<'ctx> __sdk::TableWithPrimaryKey for EditorCanvasTableHandle<'ctx> {
|
||||
type UpdateCallbackId = EditorCanvasUpdateCallbackId;
|
||||
|
||||
fn on_update(
|
||||
&self,
|
||||
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
|
||||
) -> EditorCanvasUpdateCallbackId {
|
||||
EditorCanvasUpdateCallbackId(self.imp.on_update(Box::new(callback)))
|
||||
}
|
||||
|
||||
fn remove_on_update(&self, callback: EditorCanvasUpdateCallbackId) {
|
||||
self.imp.remove_on_update(callback.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `canvas_id` unique index on the table `editor_canvas`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`EditorCanvasCanvasIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.editor_canvas().canvas_id().find(...)`.
|
||||
pub struct EditorCanvasCanvasIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<EditorCanvas, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> EditorCanvasTableHandle<'ctx> {
|
||||
/// Get a handle on the `canvas_id` unique index on the table `editor_canvas`.
|
||||
pub fn canvas_id(&self) -> EditorCanvasCanvasIdUnique<'ctx> {
|
||||
EditorCanvasCanvasIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("canvas_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> EditorCanvasCanvasIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `canvas_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<EditorCanvas> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
let _table = client_cache.get_or_make_table::<EditorCanvas>("editor_canvas");
|
||||
_table.add_unique_constraint::<String>("canvas_id", |row| &row.canvas_id);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<EditorCanvas>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse("TableUpdate<EditorCanvas>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `EditorCanvas`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait editor_canvasQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `EditorCanvas`.
|
||||
fn editor_canvas(&self) -> __sdk::__query_builder::Table<EditorCanvas>;
|
||||
}
|
||||
|
||||
impl editor_canvasQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn editor_canvas(&self) -> __sdk::__query_builder::Table<EditorCanvas> {
|
||||
__sdk::__query_builder::Table::new("editor_canvas")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct EditorCanvas {
|
||||
pub canvas_id: String,
|
||||
pub project_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub title: String,
|
||||
pub viewport_x: f64,
|
||||
pub viewport_y: f64,
|
||||
pub viewport_scale: f64,
|
||||
pub layers_json: String,
|
||||
pub created_at: __sdk::Timestamp,
|
||||
pub updated_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for EditorCanvas {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
/// Column accessor struct for the table `EditorCanvas`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
pub struct EditorCanvasCols {
|
||||
pub canvas_id: __sdk::__query_builder::Col<EditorCanvas, String>,
|
||||
pub project_id: __sdk::__query_builder::Col<EditorCanvas, String>,
|
||||
pub owner_user_id: __sdk::__query_builder::Col<EditorCanvas, String>,
|
||||
pub title: __sdk::__query_builder::Col<EditorCanvas, String>,
|
||||
pub viewport_x: __sdk::__query_builder::Col<EditorCanvas, f64>,
|
||||
pub viewport_y: __sdk::__query_builder::Col<EditorCanvas, f64>,
|
||||
pub viewport_scale: __sdk::__query_builder::Col<EditorCanvas, f64>,
|
||||
pub layers_json: __sdk::__query_builder::Col<EditorCanvas, String>,
|
||||
pub created_at: __sdk::__query_builder::Col<EditorCanvas, __sdk::Timestamp>,
|
||||
pub updated_at: __sdk::__query_builder::Col<EditorCanvas, __sdk::Timestamp>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasCols for EditorCanvas {
|
||||
type Cols = EditorCanvasCols;
|
||||
fn cols(table_name: &'static str) -> Self::Cols {
|
||||
EditorCanvasCols {
|
||||
canvas_id: __sdk::__query_builder::Col::new(table_name, "canvas_id"),
|
||||
project_id: __sdk::__query_builder::Col::new(table_name, "project_id"),
|
||||
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
|
||||
title: __sdk::__query_builder::Col::new(table_name, "title"),
|
||||
viewport_x: __sdk::__query_builder::Col::new(table_name, "viewport_x"),
|
||||
viewport_y: __sdk::__query_builder::Col::new(table_name, "viewport_y"),
|
||||
viewport_scale: __sdk::__query_builder::Col::new(table_name, "viewport_scale"),
|
||||
layers_json: __sdk::__query_builder::Col::new(table_name, "layers_json"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indexed column accessor struct for the table `EditorCanvas`.
|
||||
///
|
||||
/// Provides typed access to indexed columns for query building.
|
||||
pub struct EditorCanvasIxCols {
|
||||
pub canvas_id: __sdk::__query_builder::IxCol<EditorCanvas, String>,
|
||||
pub owner_user_id: __sdk::__query_builder::IxCol<EditorCanvas, String>,
|
||||
pub project_id: __sdk::__query_builder::IxCol<EditorCanvas, String>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasIxCols for EditorCanvas {
|
||||
type IxCols = EditorCanvasIxCols;
|
||||
fn ix_cols(table_name: &'static str) -> Self::IxCols {
|
||||
EditorCanvasIxCols {
|
||||
canvas_id: __sdk::__query_builder::IxCol::new(table_name, "canvas_id"),
|
||||
owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"),
|
||||
project_id: __sdk::__query_builder::IxCol::new(table_name, "project_id"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for EditorCanvas {}
|
||||
@@ -4,8 +4,8 @@
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::editor_canvas_snapshot_type::EditorCanvasSnapshot;
|
||||
use super::editor_project_resource_snapshot_type::EditorProjectResourceSnapshot;
|
||||
use super::editor_project_viewport_snapshot_type::EditorProjectViewportSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -13,8 +13,7 @@ pub struct EditorProjectSnapshot {
|
||||
pub project_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub title: String,
|
||||
pub viewport: EditorProjectViewportSnapshot,
|
||||
pub layers_json: String,
|
||||
pub canvas: EditorCanvasSnapshot,
|
||||
pub resources: Vec<EditorProjectResourceSnapshot>,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::*;
|
||||
|
||||
const EDITOR_PROJECT_DEFAULT_TITLE: &str = "未命名画布";
|
||||
const EDITOR_CANVAS_DEFAULT_TITLE: &str = "默认画布";
|
||||
const EDITOR_PROJECT_MAX_TITLE_CHARS: usize = 80;
|
||||
const EDITOR_PROJECT_MAX_LAYOUT_JSON_BYTES: usize = 256 * 1024;
|
||||
const EDITOR_PROJECT_SOURCE_TYPES: [&str; 3] = ["uploaded", "generated", "mock_generated"];
|
||||
@@ -22,6 +23,25 @@ pub struct EditorProject {
|
||||
updated_at: Timestamp,
|
||||
}
|
||||
|
||||
#[spacetimedb::table(
|
||||
accessor = editor_canvas,
|
||||
index(accessor = by_editor_canvas_project_id, btree(columns = [project_id])),
|
||||
index(accessor = by_editor_canvas_owner_user_id, btree(columns = [owner_user_id]))
|
||||
)]
|
||||
pub struct EditorCanvas {
|
||||
#[primary_key]
|
||||
canvas_id: String,
|
||||
project_id: String,
|
||||
owner_user_id: String,
|
||||
title: String,
|
||||
viewport_x: f64,
|
||||
viewport_y: f64,
|
||||
viewport_scale: f64,
|
||||
layers_json: String,
|
||||
created_at: Timestamp,
|
||||
updated_at: Timestamp,
|
||||
}
|
||||
|
||||
#[spacetimedb::table(
|
||||
accessor = editor_project_resource,
|
||||
index(accessor = by_editor_project_resource_project_id, btree(columns = [project_id])),
|
||||
@@ -123,13 +143,23 @@ pub struct EditorProjectResourceSnapshot {
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, SpacetimeType)]
|
||||
pub struct EditorCanvasSnapshot {
|
||||
pub canvas_id: String,
|
||||
pub project_id: String,
|
||||
pub title: String,
|
||||
pub viewport: EditorProjectViewportSnapshot,
|
||||
pub layers_json: String,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, SpacetimeType)]
|
||||
pub struct EditorProjectSnapshot {
|
||||
pub project_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub title: String,
|
||||
pub viewport: EditorProjectViewportSnapshot,
|
||||
pub layers_json: String,
|
||||
pub canvas: EditorCanvasSnapshot,
|
||||
pub resources: Vec<EditorProjectResourceSnapshot>,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
@@ -233,7 +263,7 @@ fn create_editor_project(
|
||||
|
||||
ctx.db.editor_project().insert(EditorProject {
|
||||
project_id: project_id.clone(),
|
||||
owner_user_id,
|
||||
owner_user_id: owner_user_id.clone(),
|
||||
title,
|
||||
viewport_x: 0.0,
|
||||
viewport_y: 0.0,
|
||||
@@ -242,6 +272,13 @@ fn create_editor_project(
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
});
|
||||
ensure_default_canvas(
|
||||
ctx,
|
||||
project_id.as_str(),
|
||||
owner_user_id.as_str(),
|
||||
None,
|
||||
now,
|
||||
)?;
|
||||
|
||||
build_project_snapshot(ctx, project_id.as_str())
|
||||
}
|
||||
@@ -290,7 +327,28 @@ fn save_editor_project_layout(
|
||||
let owner_user_id = normalize_required(&input.owner_user_id, "editor_project.owner_user_id")?;
|
||||
let layers_json = normalize_layout_json(input.layers_json)?;
|
||||
let project = require_owned_project(ctx, project_id.as_str(), owner_user_id.as_str())?;
|
||||
let now = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros);
|
||||
let canvas = ensure_default_canvas(
|
||||
ctx,
|
||||
project.project_id.as_str(),
|
||||
project.owner_user_id.as_str(),
|
||||
Some(&project),
|
||||
now,
|
||||
)?;
|
||||
|
||||
ctx.db.editor_canvas().canvas_id().delete(&canvas.canvas_id);
|
||||
ctx.db.editor_canvas().insert(EditorCanvas {
|
||||
canvas_id: canvas.canvas_id,
|
||||
project_id: project.project_id.clone(),
|
||||
owner_user_id: project.owner_user_id.clone(),
|
||||
title: canvas.title,
|
||||
viewport_x: input.viewport.x,
|
||||
viewport_y: input.viewport.y,
|
||||
viewport_scale: input.viewport.scale.clamp(0.01, 8.0),
|
||||
layers_json: layers_json.clone(),
|
||||
created_at: canvas.created_at,
|
||||
updated_at: now,
|
||||
});
|
||||
ctx.db.editor_project().project_id().delete(&project_id);
|
||||
ctx.db.editor_project().insert(EditorProject {
|
||||
project_id: project.project_id.clone(),
|
||||
@@ -301,7 +359,7 @@ fn save_editor_project_layout(
|
||||
viewport_scale: input.viewport.scale.clamp(0.01, 8.0),
|
||||
layers_json,
|
||||
created_at: project.created_at,
|
||||
updated_at: Timestamp::from_micros_since_unix_epoch(input.updated_at_micros),
|
||||
updated_at: now,
|
||||
});
|
||||
|
||||
build_project_snapshot(ctx, project_id.as_str())
|
||||
@@ -384,6 +442,13 @@ fn build_project_snapshot(
|
||||
.project_id()
|
||||
.find(&project_key)
|
||||
.ok_or_else(|| "图片画布工程不存在".to_string())?;
|
||||
let canvas = ensure_default_canvas(
|
||||
ctx,
|
||||
project.project_id.as_str(),
|
||||
project.owner_user_id.as_str(),
|
||||
Some(&project),
|
||||
project.created_at,
|
||||
)?;
|
||||
let mut resources = ctx
|
||||
.db
|
||||
.editor_project_resource()
|
||||
@@ -401,18 +466,77 @@ fn build_project_snapshot(
|
||||
project_id: project.project_id,
|
||||
owner_user_id: project.owner_user_id,
|
||||
title: project.title,
|
||||
viewport: EditorProjectViewportSnapshot {
|
||||
x: project.viewport_x,
|
||||
y: project.viewport_y,
|
||||
scale: project.viewport_scale,
|
||||
},
|
||||
layers_json: project.layers_json,
|
||||
canvas: canvas_snapshot_from_row(canvas),
|
||||
resources,
|
||||
created_at_micros: project.created_at.to_micros_since_unix_epoch(),
|
||||
updated_at_micros: project.updated_at.to_micros_since_unix_epoch(),
|
||||
})
|
||||
}
|
||||
|
||||
fn ensure_default_canvas(
|
||||
ctx: &ReducerContext,
|
||||
project_id: &str,
|
||||
owner_user_id: &str,
|
||||
legacy_project: Option<&EditorProject>,
|
||||
now: Timestamp,
|
||||
) -> Result<EditorCanvas, String> {
|
||||
let canvas_id = default_canvas_id(project_id);
|
||||
if let Some(canvas) = ctx.db.editor_canvas().canvas_id().find(&canvas_id) {
|
||||
return Ok(canvas);
|
||||
}
|
||||
|
||||
let legacy_view = legacy_project
|
||||
.map(|project| {
|
||||
(
|
||||
project.viewport_x,
|
||||
project.viewport_y,
|
||||
project.viewport_scale,
|
||||
project.layers_json.clone(),
|
||||
project.created_at,
|
||||
project.updated_at,
|
||||
)
|
||||
})
|
||||
.unwrap_or((0.0, 0.0, 1.0, "[]".to_string(), now, now));
|
||||
let canvas = EditorCanvas {
|
||||
canvas_id: canvas_id.clone(),
|
||||
project_id: project_id.to_string(),
|
||||
owner_user_id: owner_user_id.to_string(),
|
||||
title: EDITOR_CANVAS_DEFAULT_TITLE.to_string(),
|
||||
viewport_x: legacy_view.0,
|
||||
viewport_y: legacy_view.1,
|
||||
viewport_scale: legacy_view.2,
|
||||
layers_json: legacy_view.3,
|
||||
created_at: legacy_view.4,
|
||||
updated_at: legacy_view.5,
|
||||
};
|
||||
ctx.db.editor_canvas().insert(canvas);
|
||||
ctx.db
|
||||
.editor_canvas()
|
||||
.canvas_id()
|
||||
.find(&canvas_id)
|
||||
.ok_or_else(|| "图片画布创建失败".to_string())
|
||||
}
|
||||
|
||||
fn default_canvas_id(project_id: &str) -> String {
|
||||
format!("{project_id}:canvas:default")
|
||||
}
|
||||
|
||||
fn canvas_snapshot_from_row(row: EditorCanvas) -> EditorCanvasSnapshot {
|
||||
EditorCanvasSnapshot {
|
||||
canvas_id: row.canvas_id,
|
||||
project_id: row.project_id,
|
||||
title: row.title,
|
||||
viewport: EditorProjectViewportSnapshot {
|
||||
x: row.viewport_x,
|
||||
y: row.viewport_y,
|
||||
scale: row.viewport_scale,
|
||||
},
|
||||
layers_json: row.layers_json,
|
||||
created_at_micros: row.created_at.to_micros_since_unix_epoch(),
|
||||
updated_at_micros: row.updated_at.to_micros_since_unix_epoch(),
|
||||
}
|
||||
}
|
||||
|
||||
fn require_owned_project(
|
||||
ctx: &ReducerContext,
|
||||
project_id: &str,
|
||||
|
||||
@@ -230,6 +230,7 @@ macro_rules! migration_tables {
|
||||
asset_entity_binding,
|
||||
asset_event,
|
||||
editor_project,
|
||||
editor_canvas,
|
||||
editor_project_resource,
|
||||
puzzle_agent_session,
|
||||
puzzle_background_compile_task,
|
||||
|
||||
Reference in New Issue
Block a user