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.
 
 
 

217 lines
8.2 KiB

// Prioritize cards within other cards. Each card has a .priorities array of other taskIds.
import { headerStyle } from './styles.js'
import { aoEnv } from '../ao-lib/settings.js'
import { getCard, postEvent } from '../ao-lib/api.js'
import { createCardInteractive } from './cards.js'
import { promptMenu } from './welcome.js'
// Prints the text (.name) of the first card prioritized in the logged-in users member card
export async function getTopPriorityText() {
return (await getFirstPriorityCard())?.name
}
// Makes an API request to get the first prioritized card in the member card of the logged-in user
async function getFirstPriorityCard() {
// Get the first priority of my member card
const memberId = aoEnv('AO_CLI_SESSION_MEMBERID')
if(!memberId) {
return 'Not logged in'
}
const fetchedCards = await getCard(memberId, 'priority')
if(fetchedCards === null) {
return null
}
if(!fetchedCards || fetchedCards.length < 2) {
return 'None'
}
return fetchedCards[1]
}
// Displays the priorities of the given taskId in a menu. Selecting a card shows a menu for that card. If taskId is null, member card is used.
export async function prioritiesMenu(taskId = null) {
console.log(`\n${headerStyle('My Priorities')}`)
let prioritiesChoices = []
const memberId = aoEnv('AO_CLI_SESSION_MEMBERID')
if(!memberId) {
console.log('Not logged in.')
return false
}
if(!taskId) {
// Get the priorities of my member card
taskId = memberId
}
const fetchedCards = await getCard(taskId, 'priorities')
if(!fetchedCards || fetchedCards.length < 1) {
console.log('Failed to fetch member card, this is bad.')
return false
}
const card = fetchedCards[0]
let priorityCards = fetchedCards.slice(1) // First card is member card itself
let priorities = card.priorities.slice()
priorities.reverse()
// Fix order - incoming card order is absolute order on server, not order within this card
priorityCards = priorities.map((priorityTaskId, i) => {
const priorityCard = priorityCards.find(p => p.taskId === priorityTaskId)
if(!priorityCard) {
return 'Missing card, repair your database'
}
return priorityCard
})
console.log('You have', priorityCards.length, 'priorities:')
prioritiesChoices = priorities.map((priorityTaskId, i) => {
const priorityCard = priorityCards.find(p => p.taskId === priorityTaskId)
if(!priorityCard) {
return 'Missing card, repair your database'
}
const shortenedName = priorityCard.name.substring(0, 70) + (priorityCard.name.length >= 70 ? '...' : '')
return {
title: shortenedName,
value: { index: i, card: priorityCard },
short: shortenedName
}
})
let firstIndexEchelonDecreases
let firstEchelon = priorityCards.length >= 1 ? priorityCards[0].echelon : null
if(firstEchelon) {
priorityCards.some((pc, i) => {
if(!pc.echelon || pc.echelon !== firstEchelon) {
firstIndexEchelonDecreases = i
return true
}
})
if(!isNaN(firstIndexEchelonDecreases)) {
prioritiesChoices.splice(firstIndexEchelonDecreases, 0, { title: '───', disabled: true })
}
}
prioritiesChoices.push(
{ title: 'Play priority', value: 'create_here', short: 'new priority' },
{ title: 'Back to Deck', value: false, short: 'back' }
)
const answer = await promptMenu(prioritiesChoices, 'My Priorities', undefined, undefined, 'Prioritized cards will be inserted above this line')
switch(answer) {
case false:
return false
case 'create_here':
let previousCardCreatedText
do {
previousCardCreatedText = await createCardInteractive(true)
} while(previousCardCreatedText != '\n')
return true
case 'Missing card, repair your database':
console.log('Database repair yet implemented, sorry.')
return true
}
let chosenTask = answer.card
const chosenTaskId = chosenTask.taskId
let previousAnswer
do {
previousAnswer = await priorityCardMenu(chosenTask, answer.index, priorityCards)
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 true
}
// Short action-oriented menu for cards in the priorities list
// Index is the position of the card in the list that it is in, used for fencepost case to display upboat contextually
// allPriorities is an array of task objects for the other priorities in the priorities list this card is in (adjacent to this card)
async function priorityCardMenu(card, index, allPriorities) {
if(!card) {
console.log('priorityCardMenu: 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)
let priorityChoices = []
if(index != 0) {
priorityChoices.push({ title: 'Prioritize (upboat)', value: 'upboat', short: 'upboat' })
}
priorityChoices.push(
{ title: isChecked ? 'Uncheck' : 'Check off', value: 'check', short: 'check!' },
{ title: 'Discard from priorities (downboat)', value: 'downboat', short: 'downboat' },
//{ title: 'Browse within', value: 'browse', short: 'browse' }
{ title: 'Back to Priorities', value: false, short: 'back' }
)
const answer = await promptMenu(priorityChoices, 'Priority: ' + card.name)
switch(answer) {
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 'upboat':
const { newPosition, newEchelonScore } = getNewHighestEchelonScore(card.echelon, allPriorities)
await postEvent({
type: 'task-prioritized',
taskId: taskId,
inId: memberId,
position: newPosition,
...(newEchelonScore && { echelon: newEchelonScore }),
})
return false
case 'downboat':
await postEvent({
type: 'task-refocused',
taskId: taskId,
inId: memberId,
})
return false
case 'browse':
break
default:
return false
}
return true
}
// Calculates and returns what the echelon score should be to prioritize a priority with the given echelon score to the correct place
// in tnhe given list of tasks.
// newPriorityEchelonScore is the .echolon of the task object about to be prioritized
// allPriorities is an array of task objects for the other priorities in the priorities list this card is in (adjacent to this card)
export function getNewHighestEchelonScore(newPriorityEchelon, allPriorities) {
let firstEchelonScore
let newEchelonScore
let newPosition = 0
for(let i = 0; i < allPriorities.length; i++) {
const priority = allPriorities[i]
if(i === 0) {
firstEchelonScore = priority.echelon
if(isNaN(firstEchelonScore)) {
// Top priority does have a (valid) echelon score, so echelon = 0 and the new echelon score should be 1
newEchelonScore = 1
break
}
if(!newPriorityEchelon || newPriorityEchelon < priority.echelon) {
// Prioritized card does not have an echelon score or it is less than the current priority's echelon score (so keep going to find insert position)
newEchelonScore = priority.echelon
//break
} else if(newPriorityEchelon && priority.echelon && newPriorityEchelon === priority.echelon) {
// Echelon of new priority is same as top priority, so increase it by one (and it goes to top)
newEchelonScore = priority.echelon + 1
break
} else if(newPriorityEchelon && priority.echelon && newPriorityEchelon > priority.echelon) {
// Echelon of new priority is already greater than the new priority, so its echelon doesn't change (and it goes to top)
break
}
}
if(priority.echelon !== firstEchelonScore) {
newPosition = i
break
}
}
return { newPosition, newEchelonScore }
}