2026-06-30 14:08:51 +08:00
< script setup lang = "ts" >
import { computed , onMounted , ref } from 'vue' ;
import { useTokenStore } from '@/stores/token' ;
import MessageBox from '@/components/MessageBox.vue' ;
import {
addOwn as apiCollectionAddOwn ,
getFullOwn as apiCollectionGetFullOwn ,
getSharing as apiCollectionGetSharing ,
getDetailOwn as apiCollectionGetDetailOwn ,
deleteOwn as apiCollectionDeleteOwn ,
updateOwn as apiCollectionUpdateOwn ,
addSharing as apiCollectionAddSharing ,
2026-06-30 15:41:34 +08:00
deleteSharing as apiCollectionDeleteSharing ,
type CollectionRow ,
2026-06-30 14:08:51 +08:00
} from '@/api/collection' ;
import OwnedItem from '@/components/collection/OwnedItem.vue' ;
import SharingItem from '@/components/collection/SharingItem.vue' ;
const ownedItems = ref < Map < string , CollectionRow > > ( new Map ( ) ) ;
const sharingItems = ref < string [ ] > ( [ ] ) ;
const sharingItemUuid = ref < string | null > ( null ) ;
const isSharingItem = computed ( ( ) => {
return typeof sharingItemUuid . value === 'string' ;
} )
const pendingOwnedItemName = ref < string > ( "" ) ;
const pendingSharingItemUsername = ref < string > ( "" ) ;
const token = useTokenStore ( ) ;
const messagebox = ref < InstanceType < typeof MessageBox > > ( ) ;
onMounted ( async ( ) => {
await refreshOwnedItem ( ) ;
} )
const refreshOwnedItem = async ( ) => {
// clean owned items
ownedItems . value . clear ( ) ;
// Fetch data
let rv = await apiCollectionGetFullOwn ( token . currentToken ) ;
if ( typeof rv === 'undefined' ) {
console . error ( "fail to fetch owned collection" ) ;
return ;
}
// Add into dict for rendering
for ( let item of rv ) {
ownedItems . value . set ( item [ 0 ] , item ) ;
}
// clean sharing status
sharingItemUuid . value = null ;
await refreshSharingItem ( ) ;
}
const addOwnedItem = async ( ) => {
let new _name = pendingOwnedItemName . value ;
if ( new _name === '' ) {
return
}
// first add it
let rv = await apiCollectionAddOwn ( token . currentToken , new _name ) ;
if ( typeof rv === 'undefined' ) {
messagebox . value ? . show ( "An add operation failed. It may caused by wrong arguments. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data." ) ;
return ;
}
// second get its detail
let rv2 = await apiCollectionGetDetailOwn ( token . currentToken , rv ) ;
if ( typeof rv2 === 'undefined' ) {
messagebox . value ? . show ( "A get operation failed. It may caused by server internal error or your limited permission. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data." ) ;
return ;
}
// render to list
ownedItems . value . set ( rv2 [ 0 ] , rv2 ) ;
}
const deleteOwnedItem = async ( uuid : string ) => {
let rv = await apiCollectionDeleteOwn ( token . currentToken , uuid , ownedItems . value . get ( uuid ) ! [ 2 ] ) ;
if ( rv ) {
messagebox . value ? . show ( "A delete operation failed. It may caused by no matched item. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data." ) ;
return ;
}
// remove from rendered list
ownedItems . value . delete ( uuid ) ;
// if we are editing sharing target of this item,
// we should escape this status
if ( sharingItemUuid . value === uuid ) {
sharingItemUuid . value = null ;
await refreshSharingItem ( ) ;
}
}
const shareOwnedItem = async ( uuid : string ) => {
// if there is no editing sharing target, set it.
// otherwise override it or toggle it according to its old value.
let old _value = sharingItemUuid . value ;
if ( typeof old _value === 'string' ) {
if ( old _value === uuid ) {
sharingItemUuid . value = null ;
} else {
sharingItemUuid . value = uuid ;
}
} else {
sharingItemUuid . value = uuid ;
}
// refresh sharing list
await refreshSharingItem ( ) ;
}
const updateOwnedItem = async ( uuid : string , name : string ) => {
let lastChange = ownedItems . value . get ( uuid ) ! [ 2 ] ;
let rv = await apiCollectionUpdateOwn ( token . currentToken , uuid , name , lastChange ) ;
if ( typeof rv === 'undefined' ) {
messagebox . value ? . show ( "An update operation failed. It may caused by wrong arguments or lost target. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data." ) ;
return ;
}
// return value is new lastChange, so we update it.
let row = ownedItems . value . get ( uuid ) ! ;
row [ 1 ] = name ;
row [ 2 ] = rv ;
// if current item is sharing, update it at the same time
if ( sharingItemUuid . value === uuid ) {
await refreshSharingItem ( ) ;
}
}
const refreshSharingItem = async ( ) => {
// clear sharing item
sharingItems . value = [ ] ;
// if we are not in sharing mode, return directly
if ( ! isSharingItem . value ) {
return ;
}
// fetch data
let uuid = sharingItemUuid . value ! ;
let rv = await apiCollectionGetSharing ( token . currentToken , uuid ) ;
if ( typeof rv === 'undefined' ) {
console . error ( ` fail to fetch sharing target for collection ${ uuid } ` ) ;
return ;
}
// render then
sharingItems . value = rv ;
}
const addSharingItem = async ( ) => {
let uuid = sharingItemUuid . value ! ;
let lastChange = ownedItems . value . get ( uuid ) ! [ 2 ] ;
let new _username = pendingSharingItemUsername . value ;
if ( new _username === '' ) {
return ;
}
// order adding
let rv = await apiCollectionAddSharing ( token . currentToken , uuid , new _username , lastChange ) ;
if ( typeof rv === 'undefined' ) {
messagebox . value ? . show ( "An add operation failed. It may caused by wrong arguments. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data." ) ;
return ;
}
// push new item in list
sharingItems . value . push ( new _username ) ;
// the return value is the new last change
ownedItems . value . get ( uuid ) ! [ 2 ] = rv ;
}
const deleteSharingItem = async ( username : string ) => {
let uuid = sharingItemUuid . value ! ;
let lastChange = ownedItems . value . get ( uuid ) ! [ 2 ] ;
// order deleting
let rv = await apiCollectionDeleteSharing ( token . currentToken , uuid , username , lastChange ) ;
if ( typeof rv === 'undefined' ) {
messagebox . value ? . show ( "A delete operation failed. It may caused by no matched item. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data." ) ;
return ;
}
// remove this item
sharingItems . value . splice ( sharingItems . value . indexOf ( username ) , 1 ) ;
// the return value is the new last change
ownedItems . value . get ( uuid ) ! [ 2 ] = rv ;
}
< / script >
2026-04-28 15:47:32 +08:00
< template >
2026-06-30 14:08:51 +08:00
< div class = "container" style = "display: flex; flex-flow: column; margin-top: 1.25rem;" >
< h1 class = "title" > Owned < / h1 >
< div class = "control-list" >
< div class = "field has-addons" >
< div class = "control" >
< input v-model = "pendingOwnedItemName" class="input" type="text" >
< / div >
< div class = "control" @click ="addOwnedItem" >
< a class = "button is-primary" >
< span class = "icon is-small" > < font-awesome-icon icon = "fas fa-plus" > < / font-awesome-icon > < / span >
< / a >
< / div >
< / div >
< div class = "control" @click ="refreshOwnedItem" >
< a class = "button is-primary" >
< span class = "icon is-small" > < font-awesome-icon icon = "fas fa-sync" > < / font-awesome-icon > < / span >
< / a >
< / div >
< / div >
< div v-for = "item in ownedItems.values()" style="display: flex; flex-flow: column; margin-top: 1.25rem;" >
< OwnedItem :uuid = "item[0]" :name = "item[1]" @delete ="deleteOwnedItem" @share ="shareOwnedItem"
@ update = "updateOwnedItem" / >
< / div >
< / div >
< div v-show = "isSharingItem" class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;" >
< h1 class = "title" > Sharing target < / h1 >
< label class = "label" >
< span > Editing : < / span >
< span > { { isSharingItem ? ownedItems . get ( sharingItemUuid ! ) ! [ 1 ] : "" } } < / span >
< / label >
< div class = "control-list" >
< div class = "field has-addons" >
< div class = "control" >
< input v-model = "pendingSharingItemUsername" class="input" type="text" >
< / div >
< div class = "control" @click ="addSharingItem" >
< a class = "button is-primary" >
< span class = "icon is-small" > < font-awesome-icon icon = "fas fa-plus" > < / font-awesome-icon > < / span >
< / a >
< / div >
< / div >
< div class = "control" @click ="refreshSharingItem" >
< a class = "button is-primary" >
< span class = "icon is-small" > < font-awesome-icon icon = "fas fa-sync" > < / font-awesome-icon > < / span >
< / a >
< / div >
< / div >
< div v-for = "item in sharingItems" style="display: flex; flex-flow: column; margin-top: 1.25rem;" >
< SharingItem :username = "item" @delete ="deleteSharingItem" / >
< / div >
< / div >
< MessageBox ref = "messagebox" / >
2026-04-28 15:47:32 +08:00
< / template >
2026-06-30 15:41:34 +08:00
< style scoped >
div . control - list {
display : flex ;
flex - flow : row ;
flex - wrap : wrap ;
}
div . control - list > * {
margin - right : 0.75 rem ;
margin - bottom : 0.75 rem ;
margin - left : 0 ! important ;
}
< / style >