An interactive command-line interface (CLI) tool to help you install, use, and administer an AO instance.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

178 lines
7.0 KiB

// View and create cards with the .subtask of other cards, which is an array of taskIds
import { headerStyle } from './styles.js'
import { aoEnv } from '../ao-lib/settings.js'
import { getCard, playCard, completeCard, uncheckCard, discardCardFromCard, prioritizeCard, grabCard, dropCard } from '../ao-lib/api.js'
import { getNewHighestEchelonScore } from './priority.js'
import { createCardInteractive } from './cards.js'
import { promptMenu } from './welcome.js'
import { tagCardInteractive, viewTagMenu } from './tags.js'
// Displays the subtasks of the given taskId in a menu. Selecting a card shows a menu for that card. If taskId is null, member card is used.
// The terms tasks and cards are used mostly interchangeably in the code. For the user, 'subcards' is preferred for clarity/generality,
// but can be used to refer either to the .subTasks or all of the cards in another card (including subTasks, priorities, completed, and pinned)
export async function subcardsMenu(taskId = null, previousIndex = null) {
console.log(`\n${headerStyle('Cards in My Hand')}`)
let subtaskChoices = []
const memberId = aoEnv('AO_CLI_SESSION_MEMBERID')
if(!memberId) {
console.log('Not logged in.')
return false
}
if(!taskId) {
// Get the subtasks of my member card
taskId = memberId
}
const fetchedCards = await getCard(taskId, 'subcards') // will fetch both priorities and subtasks in one array
if(!fetchedCards || fetchedCards.length < 1) {
console.log('Failed to fetch the specified card, this is bad.')
return false
}
const card = fetchedCards[0] // first card is the requested card itself
// Separate fetched cards into correct ordered list of priorities and subtasks
let priorityCards = card.priorities.map((priorityTaskId, i) => {
const priorityCard = fetchedCards.find(p => p.taskId === priorityTaskId)
if(!priorityCard) {
return 'Missing card, repair your database'
}
return priorityCard
})
priorityCards.reverse()
const subtaskCards = card.subTasks.map((subtaskTaskId, i) => {
const subtaskCard = fetchedCards.find(st => st.taskId === subtaskTaskId)
if(!subtaskCard) {
return 'Missing card, repair your database'
}
return subtaskCard
})
console.log('There are', subtaskCards.length, 'subcards in this card')
subtaskChoices = subtaskCards.map((subtaskCard, i) => {
const shortenedName = subtaskCard.name.substring(0, 70) + (subtaskCard.name.length >= 70 ? '...' : '')
return {
title: shortenedName,
value: { index: i, card: subtaskCard },
short: shortenedName
}
})
subtaskChoices.push(
{ title: 'Play card here', value: 'create_here', short: 'new card' },
{ title: 'Back Up', value: false, short: 'back' }
)
const cardName = taskId === memberId ? 'My Hand' : card.name.substring(0, 70).toTitleCase()
const answer = await promptMenu(subtaskChoices, 'Cards in ' + cardName, undefined, previousIndex)
switch(answer) {
case false:
return false
case 'create_here':
let previousCardCreatedText
do {
previousCardCreatedText = await createCardInteractive()
console.log('done with round')
} while(previousCardCreatedText != '\n')
console.log('returning true')
return answer.index
case 'Missing card, repair your database':
console.log('Database repair yet implemented, sorry.')
return answer.index
}
if(answer === false) {
return previousIndex
}
let chosenTask = answer.card
const chosenTaskId = chosenTask.taskId
let previousAnswer
do {
previousAnswer = await subtaskCardMenu(chosenTask, answer.index, taskId, priorityCards) // send priorities for echelon info in case they upboat
if(previousAnswer) {
const fetchedCards = await getCard(chosenTaskId, false)
if(!fetchedCards || fetchedCards.length < 1) {
console.log('The card has disappeared. Maybe it was deleted, or cards held by no one are automatically cleaned up every five minutes.')
return false
}
chosenTask = fetchedCards[0]
}
} while(previousAnswer !== false)
return answer.index
}
// Short action-oriented menu for cards in the subtasks list
// Index is the position of the card in the list that it is in, used for fencepost case to display upboat contextually
// inId is the taskId of the parent card that we are in contextually as we look at the given card in its list
// allPriorities is an array of task objects for the other priorities in the .priorities for the card this card is in (adjacent to this card)
async function subtaskCardMenu(card, index, inId, allPriorities) {
if(!card) {
console.log('subtaskCardMenu: card is required.')
return false
}
const taskId = card.taskId
const memberId = aoEnv('AO_CLI_SESSION_MEMBERID')
if(!memberId) {
console.log('Not logged in.')
return false
}
const isChecked = card.claimed.includes(memberId)
const guild = card.guild === true ? card.name.toTitleCase() : card.guild || false
let subtaskChoices = []
const isInDeck = card.deck.includes(memberId)
if(!isInDeck) {
subtaskChoices.push({ title: 'Grab card (add to my deck)', value: 'grab', short: 'grab card' })
}
subtaskChoices.push(
{ title: 'Discard from hand (downboat)', value: 'downboat', short: 'downboat' },
{ title: 'Prioritize (upboat)', value: 'upboat', short: 'upboat' },
{ title: isChecked ? 'Uncheck' : 'Check off', value: 'check', short: 'check!' },
{ title: guild ? 'Tag: ' + guild : 'Tag', value: 'tag', short: 'tag' }
)
if(guild) {
subtaskChoices.push({ title: 'View tag', value: 'view_tag', short: 'view tag' })
}
if(isInDeck) {
subtaskChoices.push({ title: 'Remove from my deck', value: 'drop', short: 'drop card' })
}
subtaskChoices.push(
{ title: 'Browse within', value: 'browse', short: 'browse' },
{ title: 'Back Up', value: false, short: 'back' }
)
const answer = await promptMenu(subtaskChoices, 'Card: ' + card.name)
switch(answer) {
case 'grab':
await grabCard(taskId)
break
case 'drop':
await dropCard(taskId)
break
case 'check':
if(isChecked) {
await uncheckCard(taskId)
} else {
await completeCard(taskId)
}
break
case 'tag':
await tagCardInteractive(card)
break
case 'view_tag':
while(await viewTagMenu(card.guild))
break
case 'upboat':
console.log('upboat')
const { newPosition, newEchelonScore } = getNewHighestEchelonScore(card.echelon, allPriorities)
//console.log('newPosition is', newPosition, 'and newEchelonScore is', newEchelonScore)
await prioritizeCard(taskId, inId, newPosition, newEchelonScore)
return false
case 'downboat':
console.log(taskId, inId, 'discard')
await discardCardFromCard(taskId, inId)
return false
case 'browse':
let previousAnswer
do {
previousAnswer = await subcardsMenu(taskId, previousAnswer) // undefined taskId defaults to member card
} while(previousAnswer !== false) {}
break
break
default:
return false
}
return true
}