reset to default skin btn

This commit is contained in:
2026-08-31 01:28:14 +08:00
parent 25eb21755f
commit f82d1041ae
4 changed files with 82 additions and 6 deletions
+1
View File
@@ -75,6 +75,7 @@ fn specta_builder() -> tauri_specta::Builder<tauri::Wry> {
remotes::delete_remote, remotes::delete_remote,
profile::get_profile, profile::get_profile,
profile::update_profile, profile::update_profile,
profile::reset_profile_skin,
keypair::get_public_key, keypair::get_public_key,
network::list_statuses, network::list_statuses,
network::list_friend_statuses, network::list_friend_statuses,
+51
View File
@@ -52,6 +52,15 @@ async fn update(
get(database, public_key).await get(database, public_key).await
} }
async fn reset_skin(database: &AppDatabase, public_key: &str) -> Result<User, sqlx::Error> {
sqlx::query("UPDATE profile SET skin_hash = NULL WHERE id = ?1")
.bind(PROFILE_ID)
.execute(database.pool())
.await?;
get(database, public_key).await
}
fn emit_changed(handle: &AppHandle, profile: User) -> Result<(), String> { fn emit_changed(handle: &AppHandle, profile: User) -> Result<(), String> {
ProfileChanged { profile } ProfileChanged { profile }
.emit(handle) .emit(handle)
@@ -94,6 +103,22 @@ pub async fn update_profile(
Ok(profile) Ok(profile)
} }
#[tauri::command]
#[specta::specta]
pub async fn reset_profile_skin(
handle: AppHandle,
database: State<'_, AppDatabase>,
keypair: State<'_, AppKeypair>,
network: State<'_, Network>,
) -> Result<User, String> {
let profile = reset_skin(&database, keypair.public_key())
.await
.map_err(db::command_error)?;
network.update_profile(profile.clone());
emit_changed(&handle, profile.clone())?;
Ok(profile)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use sqlx::sqlite::SqlitePoolOptions; use sqlx::sqlite::SqlitePoolOptions;
@@ -162,4 +187,30 @@ mod tests {
assert_eq!(columns, vec!["id", "display_name", "skin_hash"]); assert_eq!(columns, vec!["id", "display_name", "skin_hash"]);
} }
#[tokio::test]
async fn resetting_skin_preserves_the_display_name() {
let database = database().await;
update(
&database,
"public-key",
"Wind".to_owned(),
Some("a".repeat(64)),
)
.await
.expect("set profile skin");
let profile = reset_skin(&database, "public-key")
.await
.expect("reset profile skin");
assert_eq!(
profile,
User {
id: "public-key".to_owned(),
display_name: "Wind".to_owned(),
skin_hash: None,
}
);
}
} }
+3
View File
@@ -38,6 +38,9 @@ async getProfile() : Promise<User> {
async updateProfile(displayName: string, skinData: number[] | null) : Promise<User> { async updateProfile(displayName: string, skinData: number[] | null) : Promise<User> {
return await TAURI_INVOKE("update_profile", { displayName, skinData }); return await TAURI_INVOKE("update_profile", { displayName, skinData });
}, },
async resetProfileSkin() : Promise<User> {
return await TAURI_INVOKE("reset_profile_skin");
},
async getPublicKey() : Promise<string> { async getPublicKey() : Promise<string> {
return await TAURI_INVOKE("get_public_key"); return await TAURI_INVOKE("get_public_key");
}, },
@@ -18,6 +18,7 @@
let skinFile = $state<File | null>(null); let skinFile = $state<File | null>(null);
let skinPreviewUrl = $state<string | null>(null); let skinPreviewUrl = $state<string | null>(null);
let skinInput = $state<HTMLInputElement | null>(null); let skinInput = $state<HTMLInputElement | null>(null);
let pendingSkinReset = $state(false);
let dirty = $state(false); let dirty = $state(false);
let busy = $state(false); let busy = $state(false);
let error = $state(""); let error = $state("");
@@ -45,7 +46,7 @@
} }
function updateDirty() { function updateDirty() {
dirty = hasConfigurationChanges() || skinFile !== null; dirty = hasConfigurationChanges() || skinFile !== null || pendingSkinReset;
error = ""; error = "";
} }
@@ -58,16 +59,24 @@
function selectSkinFile(file: File | null) { function selectSkinFile(file: File | null) {
clearSkinDraft(); clearSkinDraft();
pendingSkinReset = false;
skinFile = file; skinFile = file;
skinPreviewUrl = file ? URL.createObjectURL(file) : null; skinPreviewUrl = file ? URL.createObjectURL(file) : null;
updateDirty(); updateDirty();
} }
function resetSkin() {
clearSkinDraft();
pendingSkinReset = true;
updateDirty();
}
function reset() { function reset() {
puppetScale = $sceneConfiguration.puppetScale; puppetScale = $sceneConfiguration.puppetScale;
puppetOpacity = $sceneConfiguration.puppetOpacity; puppetOpacity = $sceneConfiguration.puppetOpacity;
puppetMovementMode = $sceneConfiguration.puppetMovementMode; puppetMovementMode = $sceneConfiguration.puppetMovementMode;
clearSkinDraft(); clearSkinDraft();
pendingSkinReset = false;
dirty = false; dirty = false;
error = ""; error = "";
} }
@@ -78,7 +87,10 @@
busy = true; busy = true;
error = ""; error = "";
try { try {
if (skinFile) { if (pendingSkinReset) {
await commands.resetProfileSkin();
pendingSkinReset = false;
} else if (skinFile) {
const currentProfile = await commands.getProfile(); const currentProfile = await commands.getProfile();
const skinData = Array.from( const skinData = Array.from(
new Uint8Array(await skinFile.arrayBuffer()), new Uint8Array(await skinFile.arrayBuffer()),
@@ -100,7 +112,8 @@
dirty = false; dirty = false;
return true; return true;
} catch (cause) { } catch (cause) {
dirty = hasConfigurationChanges() || skinFile !== null; dirty =
hasConfigurationChanges() || skinFile !== null || pendingSkinReset;
error = String(cause); error = String(cause);
return false; return false;
} finally { } finally {
@@ -141,7 +154,7 @@
<div class="size-full z-10"> <div class="size-full z-10">
<PuppetPreview <PuppetPreview
userId={$profile.id} userId={$profile.id}
skinHash={$profile.skinHash} skinHash={pendingSkinReset ? null : $profile.skinHash}
skinSource={skinPreviewUrl} skinSource={skinPreviewUrl}
scale={puppetScale} scale={puppetScale}
opacity={puppetOpacity} opacity={puppetOpacity}
@@ -192,14 +205,22 @@
/> />
</div> </div>
</fieldset> </fieldset>
<div> <div class="grid grid-cols-[minmax(0,1fr)_auto] gap-2">
<button <button
class="btn w-full" class="btn w-full"
type="button" type="button"
disabled={!$profile || busy} disabled={!$profile || busy}
onclick={() => skinInput?.click()} onclick={() => skinInput?.click()}
> >
Choose a custom skin Choose a skin
</button>
<button
class="btn"
type="button"
disabled={!$profile || busy || (!$profile.skinHash && !skinFile)}
onclick={resetSkin}
>
Reset
</button> </button>
<input <input
id="skin-file" id="skin-file"