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
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 } |
|
}
|
|
|