1
0
forked from a/lifeto-shop
lifeto-shop/src/lib/lifeto/order.ts

471 lines
11 KiB
TypeScript
Raw Normal View History

2022-07-07 07:54:36 +00:00
import { LTOApi } from "./api"
import { v4 as uuidv4 } from 'uuid';
import { RefStore } from "../../state/state";
import { debug } from "loglevel";
export const TxnStates = ["PENDING","INFLIGHT","WORKING","ERROR","SUCCESS"] as const
export type TxnState = typeof TxnStates[number]
export interface TxnDetails {
item_uid: string | "galders"
count:number
origin:string
target:string
origin_path:string
target_path:string
2022-08-10 01:39:44 +00:00
origin_account:string
target_account:string
2022-07-07 07:54:36 +00:00
}
export abstract class Order {
action_id: string
details?:TxnDetails
created:Date
state: TxnState
constructor(details?:TxnDetails) {
this.state = "PENDING"
this.details = details
this.created = new Date()
this.action_id = uuidv4();
}
mark(t:TxnState) {
this.state = t
}
abstract tick(r:RefStore, api:LTOApi):Promise<any>
abstract status():string
abstract progress():[number, number]
abstract error():string
abstract order_type:OrderType
parse(i:any):Order {
this.action_id = i.action_id
this.details = i.details
this.created = new Date(i.created)
this.state = i.state
return this
}
}
export abstract class BasicOrder extends Order {
stage: number
err?: string
constructor(details:TxnDetails) {
super(details)
this.stage = 0
}
progress():[number,number]{
return [this.stage, 1]
}
status():string {
return this.state
}
error():string {
return this.err ? this.err : ""
}
parse(i:any):BasicOrder {
this.stage = i.stage
this.err = i.err
super.parse(i)
return this
}
}
/// start user defined
export const OrderTypes = ["InvalidOrder","BankItem","InternalXfer"]
export type OrderType = typeof OrderTypes[number]
export class InvalidOrder extends Order{
order_type = "InvalidOrder"
msg:string
constructor(msg: string){
super(undefined)
this.msg = msg
this.mark("ERROR")
}
status():string {
return "ERROR"
}
progress():[number, number] {
return [0,0]
}
error(): string {
return this.msg
}
async tick(r:RefStore, api:LTOApi):Promise<void> {
return
}
parse(i:any):InvalidOrder {
super.parse(i)
this.msg = i.msg
return this
}
}
export interface BasicResponse {
status: number
data: any
msg?: string
}
export interface InternalXferRequest {
item_uid:string
qty:string
account:string
new_char:string
}
export interface InternalXferResponse extends BasicResponse {}
export class InternalXfer extends BasicOrder{
order_type = "InternalXfer"
originalRequest:InternalXferRequest
originalResponse?:InternalXferResponse
constructor(details:TxnDetails) {
super(details)
this.originalRequest = {
item_uid: details.item_uid,
qty: details.count.toString(),
new_char: details.target,
account: details.origin,
}
}
async tick(r:RefStore, api:LTOApi):Promise<void> {
if(this.state !== "PENDING") {
return
}
this.mark("WORKING")
return api.BankAction<InternalXferRequest, InternalXferResponse>("internal-xfer-item",this.originalRequest)
.then((x:InternalXferResponse)=>{
if(x.status == 200){
this.originalResponse = x
this.stage = 1
this.mark("SUCCESS")
const origin_item = r.invs.value.get(this.details?.origin_path!)!.items[this.details?.item_uid!]!
origin_item.item_count = origin_item.item_count - this.details?.count!
r.dirty.value++
}else{
throw x.msg
}
})
.catch((e)=>{
debug("InternalXfer",e)
this.stage = 1
this.err = e
this.mark("ERROR")
})
}
parse(i:any):InternalXfer {
super.parse(i)
this.originalRequest = i.originalRequest
this.originalResponse = i.originalResponse
return this
}
}
export interface BankItemRequest {
item_uid:string
qty:string
account:string
}
export interface BankItemResponse extends BasicResponse {}
export class BankItem extends BasicOrder{
order_type = "BankItem";
originalRequest:BankItemRequest
originalResponse?:BankItemResponse
constructor(details:TxnDetails) {
super(details)
this.originalRequest = {
item_uid: details.item_uid,
qty: details.count.toString(),
account: details.target,
}
}
async tick(r:RefStore, api:LTOApi):Promise<void> {
if(this.state !== "PENDING" ){
return
}
this.mark("WORKING")
return api.BankAction<BankItemRequest, BankItemResponse>("bank-item",this.originalRequest)
.then((x)=>{
debug("BankItem",x)
if(x.status == 200){
this.stage = 1
this.originalResponse = x
this.mark("SUCCESS")
const origin_item = r.invs.value.get(this.details?.origin_path!)!.items[this.details?.item_uid!]!
origin_item.item_count = origin_item.item_count - this.details?.count!
r.dirty.value++
}else {
2022-08-10 01:39:44 +00:00
throw x.msg ? x.msg : "unknown error"
2022-07-07 07:54:36 +00:00
}
})
.catch((e)=>{
this.stage = 1
this.err = e
this.mark("ERROR")
})
}
parse(i:any):BankItem {
super.parse(i)
this.originalRequest = i.originalRequest
this.originalResponse = i.originalResponse
return this
}
}
2022-08-10 01:39:44 +00:00
export interface PrivateMarketRequest {
item_uid:string
qty:string
account:string
currency:string
price:number
private:number
}
export interface PrivateMarketResponse extends BasicResponse {}
export class PrivateMarket extends BasicOrder{
order_type = "PrivateMarket";
originalRequest:PrivateMarketRequest
originalResponse?:PrivateMarketResponse
listingId?: string
listingHash?: string
constructor(details:TxnDetails) {
super(details)
this.originalRequest = {
item_uid: details.item_uid,
qty: details.count.toString(),
account: details.origin_account,
private: 1,
currency: "0",
price: 1,
}
}
async tick(r:RefStore, api:LTOApi):Promise<void> {
if(this.state !== "PENDING" ){
return
}
this.mark("WORKING")
return api.BankAction<PrivateMarketRequest, PrivateMarketResponse>("sell-item",this.originalRequest)
.then((x)=>{
debug("PrivateMarket",x)
if(x.status == 200){
this.stage = 1
this.originalResponse = x
this.mark("SUCCESS")
this.listingId = x.data["listing_id"]
this.listingHash = x.data["hash"]
try{
const origin_item = r.invs.value.get(this.details?.origin_path!)!.items[this.details?.item_uid!]!
origin_item.item_count = origin_item.item_count - this.details?.count!
r.dirty.value++
}catch(e){
}
}else {
throw x.msg ? x.msg : "unknown error"
}
})
.catch((e)=>{
this.stage = 1
this.err = e
this.mark("ERROR")
})
}
parse(i:any):PrivateMarket {
super.parse(i)
this.originalRequest = i.originalRequest
this.originalResponse = i.originalResponse
this.listingId = i.listingId
this.listingHash = i.listingHash
return this
}
}
export interface MarketMoveRequest {
listing_id?: string
qty:string
account:string
character: string
}
export interface MarketMoveResponse extends BasicResponse {
item_uid: string
}
export class MarketMove extends PrivateMarket {
order_type = "MarketMove";
moveRequest:MarketMoveRequest
moveResponse?:MarketMoveResponse
moveStage:number
moveState: TxnState
newUid: string
constructor(details:TxnDetails) {
super(details)
this.moveStage = 0
this.moveState = "PENDING"
this.newUid = ""
this.moveRequest = {
qty: details.count.toString(),
account: details.target_account,
character: (details.target_path.includes("/")) ? details.target : "0" ,
listing_id: "", // not initially populated
}
}
async tick(r:RefStore, api:LTOApi):Promise<void> {
try {
await super.tick(r, api)
}catch(e){
return
}
switch(super.status()) {
case "SUCCESS":
break;
case "ERROR":
this.moveState = "ERROR"
return
default:
return
}
if(this.moveState !== "PENDING" ){
return
}
this.moveRequest.listing_id = `${this.listingId}-${this.listingHash}`
this.moveState = "WORKING"
return api.BankAction<MarketMoveRequest, MarketMoveResponse>("buy-from-order",this.moveRequest)
.then((x)=>{
debug("MarketMove",x)
this.moveResponse = x
if(x.status == 200){
this.moveStage = 1
this.moveState = "SUCCESS"
this.newUid = x.item_uid
r.dirty.value++
}else {
throw x ? x : "unknown error"
}
})
.catch((e)=>{
this.moveStage = 1
this.err = e
this.moveState = "ERROR"
})
}
progress():[number,number]{
return [this.stage + this.moveStage, 2]
}
status():string {
return this.moveState
}
parse(i:any):MarketMove {
super.parse(i)
this.moveRequest = i.moveRequest
this.moveResponse = i.moveResponse
this.moveState = i.moveState
this.moveStage = i.moveStage
return this
}
}
export class MarketMoveToChar extends MarketMove {
order_type = "MarketMoveToChar";
charRequest:InternalXferRequest
charResponse?:InternalXferResponse
charStage:number
charState: TxnState
constructor(details:TxnDetails) {
super(details)
this.charStage = 0
this.charState = "PENDING"
this.charRequest = {
item_uid: "",
qty: details.count.toString(),
new_char: details.target,
account: details.target_account,
}
}
async tick(r:RefStore, api:LTOApi):Promise<void> {
try {
await super.tick(r, api)
}catch(e){
return
}
switch(super.status()) {
case "SUCCESS":
break;
case "ERROR":
this.charState = "ERROR"
return
default:
return
}
if(this.charState !== "PENDING" ){
return
}
this.charState = "WORKING"
this.charRequest.item_uid = this.newUid
return api.BankAction<InternalXferRequest, InternalXferResponse>("internal-xfer-item",this.charRequest)
.then((x)=>{
debug("MarketMoveToChar",x)
this.charResponse = x
if(x.status == 200){
this.charStage = 1
this.charState = "SUCCESS"
}else {
throw x ? x : "unknown error"
}
})
.catch((e)=>{
this.charStage = 1
this.err = e
this.charState = "ERROR"
})
}
progress():[number,number]{
return [this.stage +this.moveStage+ this.charStage, 3]
}
status():string {
return this.charState
}
parse(i:any):MarketMoveToChar {
super.parse(i)
this.charRequest = i.charRequest
this.charResponse = i.charResponse
this.charState = i.charState
this.charStage = i.charStage
return this
}
}