|
|
|
// 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, postEvent } 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 postEvent({
|
|
|
|
type: 'task-grabbed',
|
|
|
|
taskId: taskId,
|
|
|
|
memberId: memberId
|
|
|
|
})
|
|
|
|
break
|
|
|
|
case 'drop':
|
|
|
|
await postEvent({
|
|
|
|
type: 'task-dropped',
|
|
|
|
taskId: taskId,
|
|
|
|
memberId: memberId
|
|
|
|
})
|
|
|
|
break
|
|
|
|
case 'check':
|
|
|
|
if(isChecked) {
|
|
|
|
await postEvent({
|
|
|
|
type: 'task-unclaimed',
|
|
|
|
taskId: taskId,
|
|
|
|
memberId: memberId
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
await postEvent({ type: 'task-claimed', taskId: taskId, memberId: memberId })
|
|
|
|
}
|
|
|
|
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 postEvent({
|
|
|
|
type: 'task-prioritized',
|
|
|
|
taskId: taskId,
|
|
|
|
inId: inId,
|
|
|
|
position: newPosition,
|
|
|
|
...(newEchelonScore && { echelon: newEchelonScore }),
|
|
|
|
})
|
|
|
|
return false
|
|
|
|
case 'downboat':
|
|
|
|
console.log(taskId, inId, 'discard')
|
|
|
|
await postEvent({
|
|
|
|
type: 'task-de-sub-tasked',
|
|
|
|
taskId: inId,
|
|
|
|
subTask: taskId,
|
|
|
|
})
|
|
|
|
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
|
|
|
|
}
|