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.
200 lines
7.4 KiB
200 lines
7.4 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, 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 |
|
}
|
|
|