import request from 'superagent'
import { v1 as uuidV1 } from 'uuid'
import { io } from 'socket.io-client'
import { createHash , hmacHex } from './crypto.js'
import { isObject } from './util.js'
import { aoEnv } from './settings.js'
// The AO API server endpoint this ao-cli client will attempt to connect to
export const AO _DEFAULT _HOSTNAME = 'localhost:8003'
const HOSTNAME = aoEnv ( 'AO_CLI_TARGET_HOSTNAME' ) || AO _DEFAULT _HOSTNAME
const [ HOST , PORT ] = HOSTNAME . split ( ':' )
// The AO API server websocket endpoint this ao-cli client will attempt to connect to
const AO _SOCKET _URL = 'http://' + AO _DEFAULT _HOSTNAME // was process.env.NODE_ENV === 'development' ? 'http://' + AO_DEFAULT_HOSTNAME : '/'
export const socket = io ( AO _SOCKET _URL , {
autoConnect : false
} )
// The current connected state of the websocket, can be 'attemptingAuthentication' | 'authenticationSuccess' | 'authenticationFailed'
export let socketStatus
// Load the current session cookies from the AO .env file
let currentMemberId = aoEnv ( 'AO_CLI_SESSION_MEMBERID' )
let currentSessionId = aoEnv ( 'AO_CLI_SESSION_ID' )
let currentSessionToken = aoEnv ( 'AO_CLI_SESSION_TOKEN' )
// Performs a GET request to the specified endpoint, sending the given payload
export async function getRequest ( endpoint , payload = null , alternateHost = null , verbose = true ) {
const target = alternateHost || HOSTNAME
try {
if ( payload ) {
return await request
. get ( target + endpoint )
. send ( payload )
} else {
return await request . get ( target + endpoint )
}
} catch ( err ) {
if ( verbose ) console . log ( 'request failed' , err )
return null
}
}
// Performs a POST request to the specified endpoint, sending the given payload
export async function postRequest ( endpoint , payload = null , verbose = true ) {
if ( ! currentSessionToken ) {
if ( verbose ) console . log ( 'Session token not set, API not ready.' )
return new Promise ( ( ) => null )
}
try {
if ( payload ) {
return await request
. post ( HOSTNAME + endpoint )
. send ( payload )
. set ( 'authorization' , currentSessionToken )
. set ( 'session' , currentSessionId )
} else {
return await request . post ( HOSTNAME + endpoint )
. set ( 'authorization' , currentSessionToken )
. set ( 'session' , currentSessionId )
}
} catch ( err ) {
if ( verbose ) console . log ( 'request failed' , err )
return null
}
}
// Performs a post request to the /event endpoint, sending the given JSON object as the event
export async function postEvent ( event , verbose ) {
return await postRequest ( '/events' , event , verbose )
}
// Attempts login with the given username and password combo. If successful, returns the generated session and token (login cookies).
export async function createSession ( user , pass ) {
const session = uuidV1 ( )
let sessionKey = createHash ( session + createHash ( pass ) )
const token = hmacHex ( session , sessionKey )
const result = await request
. post ( HOSTNAME + '/session' )
. set ( 'authorization' , token )
. set ( 'session' , session )
. set ( 'name' , user )
. on ( 'error' , ( ) => false )
currentMemberId = result . body . memberId
currentSessionToken = token
currentSessionId = session
return { session , token , memberId : currentMemberId }
}
export async function logout ( ) {
return await postRequest ( '/logout' )
}
// AO p2p over tor features
export async function nameAo ( newName ) {
return await postEvent ( {
type : 'ao-named' ,
alias : newName
} )
}
// When you call startSocketListeners, it will either attempt to connect and authenticate on a web socket, or fail and return.
// onAuthenticated is a function that will be called when the client authenticates on the web socket (logs in/connects).
// In your onAuthenticated function, you should trigger fetchState or other initial fetching of state from server.
// eventCallback is a function (ev) => {} that will be called whenever an event is received on the socket.
// In this way, initial state can be fetched and then updates received after that can be used to update the local state model.
export async function startSocketListeners ( onAuthenticated , onEvent , verbose = true ) {
if ( typeof onAuthenticated !== 'function' || typeof onEvent !== 'function' ) {
console . log ( 'startSocketListeners requires two callback functions as arguments.' )
return
}
socket . connect ( )
socket . on ( 'connect' , ( ) => {
if ( verbose ) console . log ( 'websocket connected' )
socketStatus = 'attemptingAuthentication'
if ( ! currentSessionId || ! currentSessionToken ) {
if ( verbose ) console . log ( 'No current session, must log in to authenticate and use socket.' )
return
}
socket . emit ( 'authentication' , {
session : currentSessionId ,
token : currentSessionToken ,
} )
} )
socket . on ( 'authenticated' , ( ) => {
if ( verbose ) console . log ( 'websocket authenticated' )
socketStatus = 'authenticationSuccess'
socket . on ( 'eventstream' , onEvent )
onAuthenticated ( )
} )
socket . on ( 'disconnect' , reason => {
if ( verbose ) console . log ( 'websocket disconnected' )
socketStatus = 'authenticationFailed'
socket . connect ( )
} )
}
// Requests the public bootstrap list by making a public (not logged in) GET request to this or the specified server
export async function getAoBootstrapList ( serverOnion = null ) {
const result = await getRequest ( '/bootstrap' , undefined , serverOnion , false )
if ( ! result || ! result . ok || result . body . addresses . length < 1 ) {
return null
}
return result . body . addresses
}
// Gets the bootsrap list from the specified or our server, then recursively bootstraps from each address on the list
// The AO network is small right now so this shouldn't cause any problems for a while
export async function bootstrap ( serverOnion = null ) {
if ( ! serverOnion ) serverOnion = HOSTNAME
let alreadyQueried = [ serverOnion ]
let onionList = await getAoBootstrapList ( serverOnion )
if ( ! onionList ) {
return null
}
for ( let i = 0 ; i < onionList . length ; i ++ ) {
const onion = onionList [ i ]
let more = await bootstrap ( onion )
if ( ! more ) continue
more = more . filter ( onion => ! onionList . concat ( alreadyQueried ) . includes ( onion ) )
onionList . concat ( more )
}
return onionList
}
export async function shadowchat ( room , message , username ) {
return await postEvent ( {
type : 'shadowchat' ,
room : room ,
name : username ,
message : message ,
} )
}
export async function connectToAo ( address , secret ) {
return await postEvent ( {
type : 'ao-outbound-connected' ,
address : address ,
secret : secret
} )
}
export async function deleteAoConnection ( address ) {
return await postEvent ( {
type : 'ao-disconnected' ,
address : address
} )
}
export async function relayEventToOtherAo ( address , event ) {
return await postEvent ( {
type : 'ao-relay' ,
address : address ,
ev : event
} )
}
export async function linkCardOnAo ( taskId , address ) {
return await postEvent ( {
type : 'ao-linked' ,
address : address ,
taskId : taskId
} )
}
// Avatar and presence features
export async function bark ( ) {
return await postEvent ( {
type : 'doge-barked' ,
memberId : currentMemberId
} )
}
export async function hopped ( taskId ) {
return await postEvent ( {
type : 'doge-hopped' ,
memberId : currentMemberId ,
taskId : taskId
} )
}
export async function mute ( ) {
return await updateMemberField ( 'muted' , true )
}
export async function unmute ( ) {
return await updateMemberField ( 'muted' , false )
}
// Memes feature
export async function fetchMeme ( memeHash , progressCallback ) {
return request
. get ( HOSTNAME + '/meme/' + memeHash )
. responseType ( 'blob' )
. set ( 'Authorization' , currentSessionToken )
. on ( 'progress' , function ( e ) {
progressCallback ( e . percent )
} )
. then ( res => {
console . log ( 'got meme! res is ' , res )
return res . body
} )
}
export async function downloadMeme ( memeHash ) {
return request
. get ( HOSTNAME + '/download/' + memeHash )
. set ( 'Authorization' , currentSessionToken )
. then ( res => {
// console.log('got meme! res is ', res)
return res
} )
}
export async function uploadMemes ( formData , progressCallback ) {
return postRequest ( '/upload' , formData )
. on ( 'progress' , function ( e ) {
console . log ( 'Percentage done: ' , e )
if ( e && e . hasOwnProperty ( 'percent' ) && e . percent >= 0 ) {
progressCallback ( e . percent )
}
} )
. on ( 'error' , err => {
console . log ( 'Upload failed with error:' , err )
return false
} )
. then ( res => {
console . log ( 'sent files. res is' , res )
return res
} )
}
export async function cacheMeme ( taskId ) {
return await postEvent ( {
type : 'meme-cached' ,
taskId
} )
}
// Cards feature
// Returns the card and other cards as specified by the alsoGetRelevant arg
// If multiple cards are returned, they will be returned in their global deck order (global creation order on server)
export async function getCard ( taskId , alsoGetRelevant = 'subcards' ) {
taskId = taskId . trim ( ) . toLowerCase ( )
let payload = { taskId : taskId }
const result = await postRequest ( '/fetchTaskByID' , payload , false ) // todo: change to flat text, not JSON (?)
if ( ! result || ! result . body ) {
//console.log('Error fetching task.')
return null
}
if ( alsoGetRelevant ) {
let relevantCards = await getAllRelevantCards ( result . body , alsoGetRelevant )
return [ result . body , ... relevantCards ]
}
return [ result . body ]
}
// Cards feature
export async function getCardByName ( taskName , alsoGetRelevant = 'subcards' ) {
taskName = taskName . trim ( )
let payload = { taskName : taskName }
const result = await postRequest ( '/fetchTaskByName_exact' , payload , false ) // todo: change to flat text, not JSON (?)
if ( ! result || ! result . body || result . statusCode === 204 || result . statusCode === 400 ) {
//console.log('Error fetching task.')
return null
}
if ( alsoGetRelevant ) {
let relevantCards = await getAllRelevantCards ( result . body , alsoGetRelevant )
return [ result . body , ... relevantCards ]
}
return [ result . body ]
}
// Fetches all cards related to the given card object, i.e., cards that could be seen or navigated to immediately from that card
// scope = 'priority' returns only the first/top priority card within the specified card
// scope = 'priorities' returns only the priorities within the specified card
// scope = 'subcards' returns all subcards (priorities, pinned, subTasks, completed)
// Further scopes are not currently needed because the server also includes some related cards with each send
// If existingTasks: Map<string, Task> is provided, those cards will be skipped
// Returns the new cards that were fetched (not any existingTasks), plus cards the server chooses to also include
export async function getAllRelevantCards (
seedTask ,
scope = 'priorities' ,
existingTasks
) {
if ( existingTasks === undefined ) {
existingTasks = new Map ( )
}
let taskIdsToFetch
// Choose which taskIds we are going to request from the server
switch ( scope ) {
case 'priority' :
taskIdsToFetch = new Set ( [ seedTask . priorities . at ( - 1 ) ] )
break
case 'priorities' :
taskIdsToFetch = new Set ( seedTask . priorities )
break
case 'subcards' :
taskIdsToFetch = new Set (
seedTask . priorities . concat ( seedTask . subTasks , seedTask . completed )
)
if ( seedTask . pins && seedTask . pins . length >= 1 ) {
seedTask . pins . forEach ( pin => {
taskIdsToFetch . add ( pin . taskId )
} )
}
}
// Filter out the taskIds for tasks we already have
taskIdsToFetch = [ ... taskIdsToFetch ] . filter ( taskId => {
if ( ! taskId ) {
return false
}
const existingTask = existingTasks . get ( taskId )
return ! existingTask
} )
if ( taskIdsToFetch . length < 1 ) {
return [ ]
}
// Fetch the cards
try {
const result = await postRequest ( '/fetchTasks' , { taskIds : taskIdsToFetch } )
// Filter again (overlapping queries or intelligent server can cause duplicates to be returned)
const newTasksOnly = result . body . filter (
fetchedTask => ! existingTasks . get ( fetchedTask . taskId )
)
return newTasksOnly
} catch ( error ) {
console . log ( 'Error fetching relevant tasks:' , { taskIdsToFetch , error } )
return null
}
}
export async function createCard (
name ,
anonymous = false ,
prioritized = false
) {
return await postEvent ( {
type : 'task-created' ,
name : name ,
color : 'blue' ,
deck : ( anonymous || ! currentMemberId ) ? [ ] : [ currentMemberId ] ,
inId : anonymous ? null : currentMemberId || null ,
prioritized : prioritized ,
} , false )
}
// This is different from only setting a card property because when a card's color changes, it is bumped to the top of the .subTasks color pile
export async function colorCard ( taskId , color ) {
return await postEvent ( {
type : 'task-colored' ,
taskId : taskId ,
color : color ,
inId : null , // add this when we have context, mutation works on server
blame : currentMemberId
} )
}
// Set arbitrary metadata on a card
export async function setCardProperty ( taskId , property , value ) {
return await postEvent ( {
type : 'task-property-set' ,
taskId : taskId ,
property : property ,
value : value ,
blame : currentMemberId
} )
}
// Card send feature
export async function passCard ( taskId , toMemberId ) {
return await postEvent ( {
type : 'task-passed' ,
taskId : taskId ,
toMemberId : toMemberId ,
fromMemberId : currentMemberId
} )
}
// Send an immediate bark and notification to the member if possible, reminding them that they have an unread message from you (no automated pings)
export async function remindMember ( memberId ) {
return await postEvent ( {
type : 'member-reminded' ,
toMemberId : memberId ,
fromMemberId : currentMemberId
} )
}
// Cards-in-cards feature
export async function playCard ( from = null , to ) {
return await postEvent ( {
type : 'task-played' ,
from : from ,
to : to ,
memberId : currentMemberId
} )
}
export async function discardCardFromCard ( taskId , inId ) {
return await postEvent ( {
type : 'task-de-sub-tasked' ,
taskId : taskId ,
subTaskId ,
blame : currentMemberId
} )
}
// Empties a card's priorities and subtasks
export async function emptyCard ( taskId ) {
return await postEvent ( {
type : 'task-emptied' ,
taskId : taskId ,
blame : currentMemberId
} )
}
export async function swapCard ( inId , taskId1 , taskId2 ) {
return await postEvent ( {
type : 'task-swapped' ,
taskId : inId ,
swapId1 : taskId1 ,
swapId2 : taskId2 ,
blame : currentMemberId
} )
}
export async function bumpCard ( taskId , inId , direction ) {
return await postEvent ( {
type : 'task-bumped' ,
taskId : inId ,
bumpId : taskId ,
direction : direction ,
blame : currentMemberId
} )
}
// Deck features
export async function grabCard ( taskId ) {
return await postEvent ( {
type : 'task-grabbed' ,
taskId : taskId ,
memberId : currentMemberId
} )
}
export async function grabPile ( taskId ) {
return await postEvent ( {
type : 'pile-grabbed' ,
taskId : taskId ,
memberId : currentMemberId
} )
}
export async function dropCard ( taskId ) {
return await postEvent ( {
type : 'task-dropped' ,
taskId : taskId ,
memberId : currentMemberId
} )
}
export async function removeCards ( taskIds ) {
return await postEvent ( {
type : 'tasks-removed' ,
taskIds : taskIds ,
memberId : currentMemberId
} )
}
export async function dropPile ( taskId ) {
return await postEvent ( {
type : 'pile-dropped' ,
taskId : taskId ,
memberId : currentMemberId
} )
}
// Priority feature
export async function prioritizeCard ( taskId , inId , position = 0 , echelon = null ) {
const act = {
type : 'task-prioritized' ,
taskId : taskId ,
inId : inId ,
position : position ,
... ( echelon && { echelon : echelon } ) ,
blame : currentMemberId
}
console . log ( act )
return await postEvent ( act )
}
export async function prioritizePile ( inId ) {
return await postEvent ( {
type : 'task-prioritized' ,
inId : inId
} )
}
export async function refocusCard ( taskId , inId ) {
return await postEvent ( {
type : 'task-refocused' ,
taskId : taskId ,
inId : inId ,
blame : currentMemberId
} )
}
export async function refocusPile ( inId ) {
return await postEvent ( {
type : 'pile-refocused' ,
inId : inId
} )
}
export async function allocatePriority ( inId , taskId , points = 1 ) {
return await postEvent ( {
type : 'task-allocated' ,
taskId : inId ,
allocatedId : taskId ,
amount : points ,
blame : currentMemberId
//inId: inId,
} )
}
// Guilds feature
export async function titleMissionCard ( taskId , newTitle ) {
return await postEvent ( {
type : 'task-guilded' ,
taskId : taskId ,
guild : newTitle ,
blame : currentMemberId
} )
}
// Checkmarks feature
export async function completeCard ( taskId ) {
return await postEvent ( {
type : 'task-claimed' ,
taskId : taskId ,
memberId : currentMemberId
} )
}
export async function uncheckCard ( taskId ) {
return await postEvent ( {
type : 'task-unclaimed' ,
taskId : taskId ,
memberId : currentMemberId
} )
}
export async function setClaimInterval ( taskId , newClaimInterval ) {
return await setCardProperty ( taskId , claimInterval , newClaimInterval )
}
// Hardware resources feature
export async function createResource (
resourceId ,
name ,
charged ,
secret ,
trackStock
) {
return await postEvent ( {
type : 'resource-created' ,
resourceId : resourceId ,
name : name ,
charged : charged ,
secret : secret ,
trackStock : trackStock ,
blame : currentMemberId
} )
}
export async function useResource ( resourceId , amount , charged , notes = '' ) {
return await postEvent ( {
type : 'resource-used' ,
resourceId : resourceId ,
memberId : currentMemberId ,
amount : amount ,
charged : charged ,
notes : notes
} )
}
export async function stockResource ( resourceId , amount , paid , notes = '' ) {
return await postEvent ( {
type : 'resource-stocked' ,
resourceId : resourceId ,
memberId : currentMemberId ,
amount : amount ,
paid : paid ,
notes : notes
} )
}
export async function purgeResource ( resourceId ) {
return await postEvent ( {
type : 'resource-purged' ,
resourceId : resourceId ,
blame : currentMemberId
} )
}
export async function bookResource ( taskId , startTime , endTime ) {
return await postEvent ( {
type : 'resource-booked' ,
resourceId : taskId ,
memberId : currentMemberId ,
startTs : startTime ,
endTs : endTime
} )
}
// Member account features
export async function updateMemberField ( field , newValue ) {
if ( field === 'secret' ) {
newValue = createHash ( newValue )
}
return await postEvent ( {
type : 'member-field-updated' ,
memberId : currentMemberId ,
field : field ,
newfield : newValue
} )
}
// Member admin features
export async function createMember ( name , fob = '' ) {
const secret = createHash ( name )
return await postEvent ( {
type : 'member-created' ,
name ,
secret ,
fob
} )
}
export async function activateMember ( memberId ) {
return await postEvent ( {
type : 'member-activated' ,
memberId : memberId
} )
}
export async function deactivateMember ( memberId ) {
return await postEvent ( {
type : 'member-deactivated' ,
memberId : memberId
} )
}
// senpai function
export async function resetPassword ( memberId ) {
return await postEvent ( {
type : 'member-secret-reset' ,
kohaiId : memberId ,
senpaiId : currentMemberId
} )
}
// senpai function
export async function promoteMember ( memberId ) {
return await postEvent ( {
type : 'member-promoted' ,
kohaiId : memberId ,
senpaiId : currentMemberId
} )
}
// senpai function
export async function banMember ( memberId ) {
return await postEvent ( {
type : 'member-banned' ,
kohaiId : memberId ,
senpaiId : currentMemberId
} )
}
// senpai function
export async function unbanMember ( memberId ) {
return await postEvent ( {
type : 'member-unbanned' ,
kohaiId : memberId ,
senpaiId : currentMemberId
} )
}
// senpai function
export async function purgeMember ( memberId ) {
return await postEvent ( {
type : 'member-purged' ,
memberId : memberId ,
blame : currentMemberId
} )
}
// Each member has a list of tickers. Each ticker is a string.
// Sets the ticker at position tickerListIndex to symbol coinSymbol.
export async function setTicker ( fromCoin , toCoin , tickerListIndex ) {
return await postEvent ( {
type : 'member-ticker-set' ,
memberId : currentMemberId ,
fromCoin : fromCoin ,
toCoin : toCoin ,
index : tickerListIndex
} )
}
// Timeclock features
export async function clockTime ( seconds , taskId , date ) {
return await postEvent ( {
type : 'task-time-clocked' ,
taskId : taskId ,
memberId : currentMemberId ,
seconds : seconds ,
date : date
} )
}
export async function startTimeClock ( taskId , inId ) {
return await postEvent ( {
type : 'task-started' ,
taskId : taskId ,
inId : inId ,
memberId : currentMemberId
} )
}
export async function stopTimeClock ( taskId ) {
return await postEvent ( {
type : 'task-stopped' ,
taskId : taskId ,
memberId : currentMemberId
} )
}
// Group membership features
export async function assignMembership ( taskId , memberId , level ) {
return await postEvent ( {
type : 'task-membership' ,
taskId : taskId ,
memberId : memberId ,
level : level ,
blame : currentMemberId
} )
}
export async function stashCard ( taskId , inId , level ) {
return await postEvent ( {
type : 'task-stashed' ,
taskId : taskId ,
inId : inId ,
level : level ,
blame : currentMemberId
} )
}
export async function unstashCard ( taskId , inId , level ) {
return await postEvent ( {
type : 'task-unstashed' ,
taskId : taskId ,
inId : inId ,
level : level ,
blame : currentMemberId
} )
}
// Unreads feature
export async function visitCard ( taskId , inChat = false , notify = false ) {
return await postEvent ( {
type : 'task-visited' ,
taskId : taskId ,
memberId : currentMemberId ,
area : inChat ? 1 : 0 ,
notify : notify
} )
}
/ *
export async function markSeen ( taskId ) {
const task = aoStore . hashMap . get ( taskId )
const act = {
type : 'task-seen' ,
taskId : taskId ,
memberId : currentMemberId ,
}
// console.log('card marked seen')
return await postEvent ( act )
}
* /
// Pinboard feature
export async function resizeGrid ( taskId , newHeight , newWidth , newSize ) {
return await postEvent ( {
type : 'grid-resized' ,
taskId : taskId ,
height : newHeight ,
width : newWidth ,
size : newSize || 9
} )
}
export async function createCardWithGrid ( name , height , width ) {
return await postEvent ( {
type : 'grid-created' ,
name : name ,
height : height ,
width : width ,
color : 'blue' ,
deck : [ currentMemberId ]
} )
}
export async function addGridToCard ( taskId , height , width , spread = 'pyramid' ) {
return await postEvent ( {
type : 'grid-added' ,
taskId : taskId ,
spread : spread ,
height : height ,
width : width
} )
}
export async function removeGridFromCard ( taskId ) {
return await postEvent ( {
type : 'grid-removed' ,
taskId : taskId
} )
}
// This function encodes whatever is passed by the search box as a URIComponent and passes it to a search endpoint, returning the response when supplied
export async function search ( querystring , take = 10 , skip = 0 ) {
const qs = encodeURIComponent ( querystring )
const params = ` ?take= ${ take } &skip= ${ skip } `
return await postRequest ( '/search/' + qs + params )
}
export async function requestBtcQr ( taskId ) {
return await postEvent ( {
type : 'address-updated' ,
taskId
} )
}
export async function requestLightningInvoice ( taskId , amount = 0 ) {
return await postEvent ( {
type : 'invoice-created' ,
taskId ,
amount : amount
} )
}
// Proposals features
export async function signCard ( taskId , opinion = 1 ) {
return await postEvent ( {
type : 'task-signed' ,
taskId : taskId ,
memberId : currentMemberId ,
opinion : opinion
} )
}
export async function setQuorum ( quorum ) {
return await postEvent ( {
type : 'quorum-set' ,
quorum : quorum
} )
}
/ * r e a c t i o n (
( ) => {
//return aoStore.state.socketState
} ,
socketState => console . log ( 'AO: client/api.ts: socketState: ' + socketState )
)
console . log ( "NODE_ENV is" , process . env . NODE _ENV ) * /
//const api = new AoApi(socket)
//export default api
/ * a s y n c f e t c h S t a t e ( ) {
const session = window . localStorage . getItem ( 'session' )
const token = window . localStorage . getItem ( 'token' )
const user = window . localStorage . getItem ( 'user' )
if ( session && token && user ) {
return request
. post ( '/state' )
. set ( 'Authorization' , token )
. then ( res => {
aoStore . state . user = user
// console.log(
// 'AO: client/api.ts: fetchState: initial state: ',
// res.body
// )
let dataPackageToSendToClient = res . body
// Get the memberId
let memberId
dataPackageToSendToClient . stateToSend . sessions . forEach ( sessionItem => {
if ( session === sessionItem . session ) {
memberId = sessionItem . ownerId
}
} )
// See if we got our member card
let foundMemberCard
if ( ! memberId ) {
console . log ( "memberId missing when loading state" )
} else {
dataPackageToSendToClient . stateToSend . tasks . forEach ( task => {
if ( task . taskId === memberId ) {
foundMemberCard = task
}
} )
}
if ( foundMemberCard ) {
console . log ( "State includes member card:" , foundMemberCard )
} else {
console . log ( "State does not include member card" )
}
aoStore . initializeState ( dataPackageToSendToClient . stateToSend )
let metaData = dataPackageToSendToClient . metaData
aoStore . memberDeckSize = metaData . memberDeckSize
aoStore . bookmarksTaskId = metaData . bookmarksTaskId
return true
} )
. catch ( ( ) => false )
}
return Promise . resolve ( false )
} * /
/ *
export async createCardIfDoesNotExist (
name ,
color ,
anonymous
) {
return new Promise ( ( resolve , reject ) => {
aoStore . getTaskByName _async ( name , ( task ) => {
if ( isObject ( task ) ) {
console . log ( "task exists!" )
resolve ( task )
} else {
const act = {
type : 'task-created' ,
name : name ,
color : color || 'blue' ,
deck : [ currentMemberId ] ,
inId : null ,
prioritized : false ,
}
postEvent ( act ) . then ( ( res ) => {
aoStore . getTaskByName _async ( name , ( task ) => {
resolve ( task )
} )
} )
}
} )
} )
}
* /
/ *
export async createAndPlayCard ( name , color , anonymous , to ) {
return new Promise ( ( resolve , reject ) => {
this . createCardIfDoesNotExist ( name , color , anonymous ) . then ( success => {
aoStore . getTaskByName _async ( name , found => {
to . taskId = found . taskId
resolve ( this . playCard ( null , to ) )
} )
} )
} )
}
* /
/ * a s y n c f i n d O r C r e a t e C a r d I n C a r d (
name ,
inId ,
prioritizedpostEvent ( act ) = false ,
color = 'blue' ,
anonymous
) {
return new Promise ( ( resolve , reject ) => {
aoStore . getTaskByName _async ( name , found => {
console . log ( 'gotTaskByName name was ' , name , 'and found is ' , found )
let act
if ( found ) {
if ( prioritized ) {
resolve ( this . prioritizeCard ( found . taskId , inId ) )
return
} else {
act = {
type : 'task-sub-tasked' ,
taskId : inId ,
subTask : found . taskId ,
memberId : anonymous ? null : currentMemberId ,
}
}
} else {
act = {
type : 'task-created' ,
name : name ,
color : color ,
deck : anonymous ? [ ] : [ currentMemberId ] ,
inId : inId ,
prioritized : prioritized ,
}
}
resolve (
postEvent ( act ) . then ( res => return res )
)
} )
} )
} * /
/ * a s y n c f u n c t i o n p i n C a r d T o G r i d (
x ,
y ,
name ,
inId
) {
return new Promise ( ( resolve , reject ) => {
aoStore . getTaskByName _async ( name , ( task ) => {
console . log ( 'gotTaskByName name was ' , name , 'and found is ' , task )
// console.log("AO: client/api.ts: pinCardToGrid: ", {x, y, name, inId, task})
if ( isObject ( task ) ) {
const fromLocation
const toLocation : CardLocation = {
taskId . taskId ,
inId : inId ,
coords : { y , x }
}
playCard ( )
} else {
const act = {
type : 'task-created' ,
name : name ,
color : 'blue' ,
deck : [ currentMemberId ] ,
inId : inId ,
prioritized : false ,
}
postEvent ( act ) . then ( res => {
const taskId = JSON . parse ( res . text ) . event . taskId
const gridAct = {
type : 'grid-pin' ,
inId : inId ,
taskId : taskId ,
x : x ,
y : y ,
memberId : currentMemberId ,
}
resolve (
request
. post ( '/events' )
. set ( 'Authorization' , currentSessionToken )
. send ( gridAct )
)
} )
}
} )
} )
} * /
/ * a s y n c f u n c t i o n u n p i n C a r d F r o m G r i d (
x ,
y ,
inId
) {
return await postEvent ( {
type : 'grid-unpin' ,
x ,
y ,
inId ,
} )
} * /