forked from a/lifeto-shop
473 lines
11 KiB
TypeScript
473 lines
11 KiB
TypeScript
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
|
|
|
|
origin_account:string
|
|
target_account:string
|
|
}
|
|
|
|
export interface Envelope<REQ,RESP> {
|
|
req: REQ
|
|
resp: RESP
|
|
state: TxnState
|
|
}
|
|
|
|
|
|
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", "PrivateMarket","MarketMove", "MarketMoveToChar"]
|
|
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!
|
|
}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!
|
|
}else {
|
|
throw x.msg ? x.msg : "unknown error"
|
|
}
|
|
})
|
|
.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
|
|
}
|
|
}
|
|
|
|
|
|
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!
|
|
}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
|
|
}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
|
|
}
|
|
}
|