collection panes: add delete action, fix edit mode state
- add remove from collection with confirmation - sidebar overflow menu for delete action - better tracking of which item is selected to avoid state bugs
This commit is contained in:
parent
7558aef509
commit
b5d0b7c0e7
3 changed files with 209 additions and 72 deletions
|
|
@ -8,9 +8,12 @@
|
|||
*
|
||||
* The "My Collection" tab includes an edit mode using CharacterEditPane.
|
||||
*/
|
||||
import { untrack } from 'svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import type { CollectionCharacter, ExtendedMastery } from '$lib/types/api/collection'
|
||||
import { useUpdateCollectionCharacter } from '$lib/api/mutations/collection.mutations'
|
||||
import {
|
||||
useUpdateCollectionCharacter,
|
||||
useRemoveCharacterFromCollection
|
||||
} from '$lib/api/mutations/collection.mutations'
|
||||
import SegmentedControl from '$lib/components/ui/segmented-control/SegmentedControl.svelte'
|
||||
import Segment from '$lib/components/ui/segmented-control/Segment.svelte'
|
||||
import ItemHeader from '$lib/components/sidebar/details/ItemHeader.svelte'
|
||||
|
|
@ -38,20 +41,30 @@
|
|||
// Local state for the character - updated when mutation succeeds
|
||||
let character = $state<CollectionCharacter>(initialCharacter)
|
||||
|
||||
// Track which character we're displaying to detect when a different one is selected
|
||||
let currentCharacterId = $state(initialCharacter.id)
|
||||
|
||||
// Tab state
|
||||
let selectedTab = $state<'info' | 'collection'>('collection')
|
||||
|
||||
// Edit mode state
|
||||
let isEditing = $state(false)
|
||||
|
||||
// Sync local state when a different character is selected
|
||||
// Reference to the edit pane component for calling save()
|
||||
let editPaneRef: ReturnType<typeof CharacterEditPane> | undefined = $state()
|
||||
|
||||
// Sync local state only when a DIFFERENT character is selected (not on every re-render)
|
||||
$effect(() => {
|
||||
character = initialCharacter
|
||||
isEditing = false
|
||||
if (initialCharacter.id !== currentCharacterId) {
|
||||
character = initialCharacter
|
||||
currentCharacterId = initialCharacter.id
|
||||
isEditing = false
|
||||
}
|
||||
})
|
||||
|
||||
// Update mutation
|
||||
// Mutations
|
||||
const updateMutation = useUpdateCollectionCharacter()
|
||||
const deleteMutation = useRemoveCharacterFromCollection()
|
||||
|
||||
// Derived values
|
||||
const characterData = $derived(character.character)
|
||||
|
|
@ -142,13 +155,57 @@
|
|||
character = updatedCharacter
|
||||
|
||||
isEditing = false
|
||||
updateActionVisibility()
|
||||
} catch (error) {
|
||||
console.error('Failed to update collection character:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Enter edit mode and update header action
|
||||
function enterEditMode() {
|
||||
isEditing = true
|
||||
// Update header to show Save button
|
||||
sidebar.setAction(() => editPaneRef?.save(), 'Save', elementName)
|
||||
}
|
||||
|
||||
// Handle delete from collection
|
||||
function handleDelete() {
|
||||
if (confirm('Are you sure you want to remove this character from your collection?')) {
|
||||
deleteMutation.mutate(character.id, {
|
||||
onSuccess: () => {
|
||||
onClose?.()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Update action visibility when tab or edit state changes
|
||||
function updateActionVisibility() {
|
||||
if (isOwner && selectedTab === 'collection') {
|
||||
if (isEditing) {
|
||||
// Show Save button when editing, hide overflow menu
|
||||
sidebar.setAction(() => editPaneRef?.save(), 'Save', elementName)
|
||||
sidebar.clearOverflowMenu()
|
||||
} else {
|
||||
// Show Edit button and overflow menu when viewing
|
||||
sidebar.setAction(enterEditMode, 'Edit', elementName)
|
||||
sidebar.setOverflowMenu([
|
||||
{
|
||||
label: 'Remove from collection',
|
||||
handler: handleDelete,
|
||||
variant: 'danger'
|
||||
}
|
||||
])
|
||||
}
|
||||
} else {
|
||||
sidebar.clearAction()
|
||||
sidebar.clearOverflowMenu()
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
isEditing = false
|
||||
updateActionVisibility()
|
||||
}
|
||||
|
||||
function handleTabChange(value: string) {
|
||||
|
|
@ -157,6 +214,8 @@
|
|||
if (isEditing) {
|
||||
isEditing = false
|
||||
}
|
||||
// Update sidebar action visibility after state change
|
||||
updateActionVisibility()
|
||||
}
|
||||
|
||||
function getRingLabel(ring: ExtendedMastery | null): string {
|
||||
|
|
@ -215,25 +274,14 @@
|
|||
character.earring && character.earring.modifier != null && character.earring.modifier !== 0
|
||||
)
|
||||
|
||||
// Update sidebar header action based on current state
|
||||
$effect(() => {
|
||||
// Capture reactive dependencies we want to track
|
||||
const shouldShowEdit = isOwner && selectedTab === 'collection' && !isEditing
|
||||
const element = elementName
|
||||
// Use untrack to avoid infinite loop when sidebar.setAction mutates pane state
|
||||
untrack(() => {
|
||||
if (shouldShowEdit) {
|
||||
sidebar.setAction(() => (isEditing = true), 'Edit', element)
|
||||
} else {
|
||||
sidebar.clearAction()
|
||||
}
|
||||
})
|
||||
})
|
||||
// Set up sidebar action on mount and clean up on destroy
|
||||
onMount(() => {
|
||||
// Initial setup - show Edit button if on collection tab and owner
|
||||
updateActionVisibility()
|
||||
|
||||
// Clean up sidebar action when component is destroyed
|
||||
$effect(() => {
|
||||
return () => {
|
||||
sidebar.clearAction()
|
||||
sidebar.clearOverflowMenu()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -276,12 +324,11 @@
|
|||
{:else if isEditing}
|
||||
<!-- Edit mode -->
|
||||
<CharacterEditPane
|
||||
bind:this={editPaneRef}
|
||||
{characterData}
|
||||
{currentValues}
|
||||
showPerpetuity={true}
|
||||
onSave={handleSave}
|
||||
onCancel={handleCancel}
|
||||
saving={updateMutation.isPending}
|
||||
/>
|
||||
{:else}
|
||||
<!-- My Collection view: user's customizations -->
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@
|
|||
*
|
||||
* The "My Collection" tab includes an edit mode using SummonEditPane.
|
||||
*/
|
||||
import { untrack } from 'svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import type { CollectionSummon } from '$lib/types/api/collection'
|
||||
import { useUpdateCollectionSummon } from '$lib/api/mutations/collection.mutations'
|
||||
import {
|
||||
useUpdateCollectionSummon,
|
||||
useRemoveSummonFromCollection
|
||||
} from '$lib/api/mutations/collection.mutations'
|
||||
import SegmentedControl from '$lib/components/ui/segmented-control/SegmentedControl.svelte'
|
||||
import Segment from '$lib/components/ui/segmented-control/Segment.svelte'
|
||||
import ItemHeader from '$lib/components/sidebar/details/ItemHeader.svelte'
|
||||
|
|
@ -36,20 +39,30 @@
|
|||
// Local state for the summon - updated when mutation succeeds
|
||||
let summon = $state<CollectionSummon>(initialSummon)
|
||||
|
||||
// Track which summon we're displaying to detect when a different one is selected
|
||||
let currentSummonId = $state(initialSummon.id)
|
||||
|
||||
// Tab state
|
||||
let selectedTab = $state<'info' | 'collection'>('collection')
|
||||
|
||||
// Edit mode state
|
||||
let isEditing = $state(false)
|
||||
|
||||
// Sync local state when a different summon is selected
|
||||
// Reference to the edit pane component for calling save()
|
||||
let editPaneRef: ReturnType<typeof SummonEditPane> | undefined = $state()
|
||||
|
||||
// Sync local state only when a DIFFERENT summon is selected (not on every re-render)
|
||||
$effect(() => {
|
||||
summon = initialSummon
|
||||
isEditing = false
|
||||
if (initialSummon.id !== currentSummonId) {
|
||||
summon = initialSummon
|
||||
currentSummonId = initialSummon.id
|
||||
isEditing = false
|
||||
}
|
||||
})
|
||||
|
||||
// Update mutation
|
||||
// Mutations
|
||||
const updateMutation = useUpdateCollectionSummon()
|
||||
const deleteMutation = useRemoveSummonFromCollection()
|
||||
|
||||
// Derived values
|
||||
const summonData = $derived(summon.summon)
|
||||
|
|
@ -92,13 +105,57 @@
|
|||
summon = updatedSummon
|
||||
|
||||
isEditing = false
|
||||
updateActionVisibility()
|
||||
} catch (error) {
|
||||
console.error('Failed to update collection summon:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Enter edit mode and update header action
|
||||
function enterEditMode() {
|
||||
isEditing = true
|
||||
// Update header to show Save button
|
||||
sidebar.setAction(() => editPaneRef?.save(), 'Save', elementName)
|
||||
}
|
||||
|
||||
// Handle delete from collection
|
||||
function handleDelete() {
|
||||
if (confirm('Are you sure you want to remove this summon from your collection?')) {
|
||||
deleteMutation.mutate(summon.id, {
|
||||
onSuccess: () => {
|
||||
onClose?.()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Update action visibility when tab or edit state changes
|
||||
function updateActionVisibility() {
|
||||
if (isOwner && selectedTab === 'collection') {
|
||||
if (isEditing) {
|
||||
// Show Save button when editing, hide overflow menu
|
||||
sidebar.setAction(() => editPaneRef?.save(), 'Save', elementName)
|
||||
sidebar.clearOverflowMenu()
|
||||
} else {
|
||||
// Show Edit button and overflow menu when viewing
|
||||
sidebar.setAction(enterEditMode, 'Edit', elementName)
|
||||
sidebar.setOverflowMenu([
|
||||
{
|
||||
label: 'Remove from collection',
|
||||
handler: handleDelete,
|
||||
variant: 'danger'
|
||||
}
|
||||
])
|
||||
}
|
||||
} else {
|
||||
sidebar.clearAction()
|
||||
sidebar.clearOverflowMenu()
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
isEditing = false
|
||||
updateActionVisibility()
|
||||
}
|
||||
|
||||
function handleTabChange(value: string) {
|
||||
|
|
@ -106,27 +163,16 @@
|
|||
if (isEditing) {
|
||||
isEditing = false
|
||||
}
|
||||
updateActionVisibility()
|
||||
}
|
||||
|
||||
// Update sidebar header action
|
||||
$effect(() => {
|
||||
// Capture reactive dependencies we want to track
|
||||
const shouldShowEdit = isOwner && selectedTab === 'collection' && !isEditing
|
||||
const element = elementName
|
||||
// Use untrack to avoid infinite loop when sidebar.setAction mutates pane state
|
||||
untrack(() => {
|
||||
if (shouldShowEdit) {
|
||||
sidebar.setAction(() => (isEditing = true), 'Edit', element)
|
||||
} else {
|
||||
sidebar.clearAction()
|
||||
}
|
||||
})
|
||||
})
|
||||
// Set up sidebar action on mount and clean up on destroy
|
||||
onMount(() => {
|
||||
updateActionVisibility()
|
||||
|
||||
// Clean up sidebar action when component is destroyed
|
||||
$effect(() => {
|
||||
return () => {
|
||||
sidebar.clearAction()
|
||||
sidebar.clearOverflowMenu()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -165,11 +211,10 @@
|
|||
</div>
|
||||
{:else if isEditing}
|
||||
<SummonEditPane
|
||||
bind:this={editPaneRef}
|
||||
{summonData}
|
||||
{currentValues}
|
||||
onSave={handleSave}
|
||||
onCancel={handleCancel}
|
||||
saving={updateMutation.isPending}
|
||||
/>
|
||||
{:else}
|
||||
<div class="collection-view">
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@
|
|||
*
|
||||
* The "My Collection" tab includes an edit mode using WeaponEditPane.
|
||||
*/
|
||||
import { untrack } from 'svelte'
|
||||
import { onMount } from 'svelte'
|
||||
import type { CollectionWeapon } from '$lib/types/api/collection'
|
||||
import type { SimpleAxSkill } from '$lib/types/api/entities'
|
||||
import { useUpdateCollectionWeapon } from '$lib/api/mutations/collection.mutations'
|
||||
import {
|
||||
useUpdateCollectionWeapon,
|
||||
useRemoveWeaponFromCollection
|
||||
} from '$lib/api/mutations/collection.mutations'
|
||||
import SegmentedControl from '$lib/components/ui/segmented-control/SegmentedControl.svelte'
|
||||
import Segment from '$lib/components/ui/segmented-control/Segment.svelte'
|
||||
import ItemHeader from '$lib/components/sidebar/details/ItemHeader.svelte'
|
||||
|
|
@ -39,20 +42,30 @@
|
|||
// Local state for the weapon - updated when mutation succeeds
|
||||
let weapon = $state<CollectionWeapon>(initialWeapon)
|
||||
|
||||
// Track which weapon we're displaying to detect when a different one is selected
|
||||
let currentWeaponId = $state(initialWeapon.id)
|
||||
|
||||
// Tab state
|
||||
let selectedTab = $state<'info' | 'collection'>('collection')
|
||||
|
||||
// Edit mode state
|
||||
let isEditing = $state(false)
|
||||
|
||||
// Sync local state when a different weapon is selected
|
||||
// Reference to the edit pane component for calling save()
|
||||
let editPaneRef: ReturnType<typeof WeaponEditPane> | undefined = $state()
|
||||
|
||||
// Sync local state only when a DIFFERENT weapon is selected (not on every re-render)
|
||||
$effect(() => {
|
||||
weapon = initialWeapon
|
||||
isEditing = false
|
||||
if (initialWeapon.id !== currentWeaponId) {
|
||||
weapon = initialWeapon
|
||||
currentWeaponId = initialWeapon.id
|
||||
isEditing = false
|
||||
}
|
||||
})
|
||||
|
||||
// Update mutation
|
||||
// Mutations
|
||||
const updateMutation = useUpdateCollectionWeapon()
|
||||
const deleteMutation = useRemoveWeaponFromCollection()
|
||||
|
||||
// Derived values
|
||||
const weaponData = $derived(weapon.weapon)
|
||||
|
|
@ -148,13 +161,57 @@
|
|||
weapon = updatedWeapon
|
||||
|
||||
isEditing = false
|
||||
updateActionVisibility()
|
||||
} catch (error) {
|
||||
console.error('Failed to update collection weapon:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Enter edit mode and update header action
|
||||
function enterEditMode() {
|
||||
isEditing = true
|
||||
// Update header to show Save button
|
||||
sidebar.setAction(() => editPaneRef?.save(), 'Save', elementName)
|
||||
}
|
||||
|
||||
// Handle delete from collection
|
||||
function handleDelete() {
|
||||
if (confirm('Are you sure you want to remove this weapon from your collection?')) {
|
||||
deleteMutation.mutate(weapon.id, {
|
||||
onSuccess: () => {
|
||||
onClose?.()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Update action visibility when tab or edit state changes
|
||||
function updateActionVisibility() {
|
||||
if (isOwner && selectedTab === 'collection') {
|
||||
if (isEditing) {
|
||||
// Show Save button when editing, hide overflow menu
|
||||
sidebar.setAction(() => editPaneRef?.save(), 'Save', elementName)
|
||||
sidebar.clearOverflowMenu()
|
||||
} else {
|
||||
// Show Edit button and overflow menu when viewing
|
||||
sidebar.setAction(enterEditMode, 'Edit', elementName)
|
||||
sidebar.setOverflowMenu([
|
||||
{
|
||||
label: 'Remove from collection',
|
||||
handler: handleDelete,
|
||||
variant: 'danger'
|
||||
}
|
||||
])
|
||||
}
|
||||
} else {
|
||||
sidebar.clearAction()
|
||||
sidebar.clearOverflowMenu()
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
isEditing = false
|
||||
updateActionVisibility()
|
||||
}
|
||||
|
||||
function handleTabChange(value: string) {
|
||||
|
|
@ -162,6 +219,7 @@
|
|||
if (isEditing) {
|
||||
isEditing = false
|
||||
}
|
||||
updateActionVisibility()
|
||||
}
|
||||
|
||||
function getAwakeningType(): string {
|
||||
|
|
@ -192,25 +250,13 @@
|
|||
const hasAxSkills = $derived((weapon.ax?.length ?? 0) > 0 && weapon.ax?.some(ax => ax.modifier >= 0))
|
||||
const canChangeElement = $derived(weaponData?.element === 0)
|
||||
|
||||
// Update sidebar header action
|
||||
$effect(() => {
|
||||
// Capture reactive dependencies we want to track
|
||||
const shouldShowEdit = isOwner && selectedTab === 'collection' && !isEditing
|
||||
const element = elementName
|
||||
// Use untrack to avoid infinite loop when sidebar.setAction mutates pane state
|
||||
untrack(() => {
|
||||
if (shouldShowEdit) {
|
||||
sidebar.setAction(() => (isEditing = true), 'Edit', element)
|
||||
} else {
|
||||
sidebar.clearAction()
|
||||
}
|
||||
})
|
||||
})
|
||||
// Set up sidebar action on mount and clean up on destroy
|
||||
onMount(() => {
|
||||
updateActionVisibility()
|
||||
|
||||
// Clean up sidebar action when component is destroyed
|
||||
$effect(() => {
|
||||
return () => {
|
||||
sidebar.clearAction()
|
||||
sidebar.clearOverflowMenu()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -250,11 +296,10 @@
|
|||
</div>
|
||||
{:else if isEditing}
|
||||
<WeaponEditPane
|
||||
bind:this={editPaneRef}
|
||||
{weaponData}
|
||||
{currentValues}
|
||||
onSave={handleSave}
|
||||
onCancel={handleCancel}
|
||||
saving={updateMutation.isPending}
|
||||
/>
|
||||
{:else}
|
||||
<div class="collection-view">
|
||||
|
|
|
|||
Loading…
Reference in a new issue