src/blackrock/exchange-views.ts

import {emptyDeskFilters,type DeskFilters} from './exchange-desk-model';
export type DeskState={category:string;column:string;direction:'asc'|'desc';filters:DeskFilters;density:'compact'|'comfortable';hidden:string[]};
export type ExchangeView={id:string;name:string;query:string;book:'buy'|'sell'|'recent';sort:string;days:string;desk:DeskState};
export const VIEW_KEY='rs-blackrock:exchange-views-v1';
export const defaultDesk=():DeskState=>({category:'all',column:'source',direction:'desc',filters:emptyDeskFilters(),density:'compact',hidden:['lastSellAt','lastTradeAt']});
const sorts=['buyValue','sellRecent','recent','volume','value','rises','falls'];
const columns=['name','bid','ask','buyQuantity','sellQuantity','buyValue','sellValue','lastPrice','volume','gross','lastSellAt','lastTradeAt','visited','source','rarity'];
export function readExchangeViews(raw:string|null):ExchangeView[]{
 try{const values=JSON.parse(raw??'[]');if(!Array.isArray(values))return [];
 return values.slice(0,20).flatMap((v:any)=>{
  if(!v||typeof v.id!=='string'||typeof v.name!=='string'||!v.name.trim()||!['buy','sell','recent'].includes(v.book)||!sorts.includes(v.sort)||!['1','7','30','90'].includes(v.days)||!v.desk)return [];
  const d=v.desk,filters=emptyDeskFilters();
  for(const k of ['minPrice','maxPrice','minQuantity','minValue'] as const)if(typeof d.filters?.[k]==='string'&&d.filters[k].length<40)filters[k]=d.filters[k];
  filters.twoSided=d.filters?.twoSided===true;filters.unvisited=d.filters?.unvisited===true;
  return [{id:v.id.slice(0,80),name:v.name.trim().slice(0,60),query:typeof v.query==='string'?v.query.slice(0,80):'',book:v.book,sort:v.sort,days:v.days,desk:{category:['all','rares','endgame','supplies','watched'].includes(d.category)?d.category:'all',column:columns.includes(d.column)?d.column:'source',direction:d.direction==='asc'?'asc':'desc',filters,density:d.density==='comfortable'?'comfortable':'compact',hidden:Array.isArray(d.hidden)?d.hidden.filter((s:unknown)=>typeof s==='string'&&columns.includes(s)&&s!=='name'):[]}} as ExchangeView];
 });}catch{return []}
}