diff --git a/README.md b/README.md index 50e42a2..4a8e0f1 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ These features work right now: * Detects your OS, with support for Debian/Ubuntu, Arch/Manjaro, and Fedora (MacOS planned) * Wraps the functionality of (some of) Zen's Alchemy suite of scripts (system configuration, AO installation) * Add `ao` alias for `ao-cli` (under Features→ao-cli) -* Enchant your 'cd' command to narrate your travels through the UNIX filesystem (under Features→ao-cli) (less annoying than it sounds, easy to disable) +* Optionally enchant your 'cd' command to narrate your travels through the UNIX filesystem and occasionally remind you of your top priority (under Features→ao-cli) * Easily add your existing systemctl services to the Features list so you can start and stop them from the AO Features menu +* Pedagogical codebase designed for teaching novice users. Code written to be read, with relevant contextual information mentioned in comments. Browse or download the code for ao-cli at the Repository link above. ## Upcoming Features diff --git a/package.json b/package.json index 236ce41..2c32681 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "autonomous", "autonomy", "DAO", - "CIC" + "CIC", + "pedagogy" ], "author": "Coalition of Invisible Colleges", "license": "AGPL-3.0-or-later", diff --git a/scripts/api.js b/scripts/api.js index 9dfc4b6..83707b2 100644 --- a/scripts/api.js +++ b/scripts/api.js @@ -65,7 +65,6 @@ export async function postRequest(endpoint, payload = null, verbose = true) { // Performs a post request to the /event endpoint, sending the given JSON object as the event export async function postEvent(event, verbose) { - console.log('about to post event') return await postRequest('/events', event, verbose) } @@ -473,14 +472,17 @@ export async function dropPile(taskId) { } // Priority feature -export async function prioritizeCard(taskId, inId, position = 0) { - return await postEvent({ +export async function prioritizeCard(taskId, inId, position = 0, echelon = null) { + const act = { type: 'task-prioritized', taskId: taskId, inId: inId, position: position, + ...(echelon && { echelon: echelon }), blame: currentMemberId - }) + } + console.log(act) + return await postEvent(act) } export async function prioritizePile(inId) { diff --git a/scripts/cards.js b/scripts/cards.js index 43c2c18..b05a7f3 100644 --- a/scripts/cards.js +++ b/scripts/cards.js @@ -3,6 +3,7 @@ import inquirer from 'inquirer' import { getCardByName, createCard, prioritizeCard } from './api.js' import { headerStyle } from './styles.js' import { prioritiesMenu } from './priority.js' +import { aoEnv } from './settings.js' // The card menu is complex and so has been split into this separate file export async function cardMenu() { @@ -52,7 +53,8 @@ async function browseMenu() { } // Ask the user to create a card, checks if it already exists, and then creates it if it doesn't -async function createCardInteractive(prioritized = true) { +export async function createCardInteractive(prioritized = true) { + console.log('createcardinteractive') const memberId = aoEnv('AO_CLI_SESSION_MEMBERID') if(!memberId) { console.log('Not logged in.') @@ -74,9 +76,9 @@ async function createCardInteractive(prioritized = true) { } if(prioritized) { console.log('Card already exists, prioritizing.') - const prioritizeResult = prioritizeCard(fetchedCards[0].taskId, memberId) + const prioritizeResult = await prioritizeCard(fetchedCards[0].taskId, memberId) if(!prioritizeResult.ok) { - console.log('May have failed to prioritize card.') + console.log('May have failed to prioritize card:', prioritizeResult) } } return false diff --git a/scripts/connect.js b/scripts/connect.js index ab4277f..1d49efb 100644 --- a/scripts/connect.js +++ b/scripts/connect.js @@ -130,86 +130,4 @@ async function connectInteractive() { const result = await connectToAo(onion, secret) console.log('result is', result.body) return true -} - -// Fetches a list of your AO server's p2p connections and displays it -async function connectionsMenu() { - console.log(`\n${headerStyle('AO P2P Connections')}`) - let connectionsChoices = [] - - const memberId = aoEnv('AO_CLI_SESSION_MEMBERID') - if(!memberId) { - console.log('Not logged in.') - return false - } - 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] - const priorityCards = fetchedCards.slice(1) // First card is member card itself - let priorities = card.priorities.slice() - priorities.reverse() - 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' - } - return { - name: priorityCard.name, - value: { index: i, card: priorityCard }, - short: priorityCard.name.substring(0, 70) + priorityCard.name.length >= 70 ? '...' : '' - } - }) - prioritiesChoices.push( - { name: 'Create priority', value: 'create_here', short: 'new priority' }, - { name: 'Back to Deck', value: false, short: 'back' } - ) - let answer - try { - answer = await inquirer.prompt({ - name: 'priorities_menu', - type: 'rawlist', - message: 'Please choose:', - choices: prioritiesChoices, - loop: false - }) - } catch(error) { - if (error === 'EVENT_INTERRUPTED') { - console.log('\nESC') - return false - } - } - switch(answer.priorities_menu) { - case false: - return false - case 'create_here': - let previousCardCreatedText - do { - console.log('previousCardCreatedText is', previousCardCreatedText) - previousCardCreatedText = await createCardInteractive() - } while(previousCardCreatedText != '\n') - return true - case 'Missing card, repair your database': - console.log('Database repair yet implemented, sorry.') - return true - } - let chosenTask = answer.priorities_menu.card - const chosenTaskId = chosenTask.taskId - let previousAnswer - do { - previousAnswer = await priorityCardMenu(chosenTask, answer.priorities_menu.index) - 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) - console.log('Card menu not yet implemented.') - return true } \ No newline at end of file diff --git a/scripts/features/index.js b/scripts/features/index.js index fb6758a..5cca8c7 100644 --- a/scripts/features/index.js +++ b/scripts/features/index.js @@ -123,10 +123,11 @@ function oneFeatureMenuChoices(name, feature, status, isCustom = false) { return null } - const running = typeof feature.isRunning === 'function' ? feature.isRunning() : feature.hasOwnProperty('isRunning') ? feature.isRunning : false + const installed = typeof feature.isInstall === 'function' ? feature.isInstalled : feature.hasOwnProperty('isInstalled') ? feature.isInstalled : status !== 'off' + const running = installed && typeof feature.isRunning === 'function' ? feature.isRunning() : feature.hasOwnProperty('isRunning') ? feature.isRunning : false if(running && typeof feature.stop === 'function') { featureChoices.push({ name: 'Stop ' + name, value: () => feature.stop() }) - } else if(!running && typeof feature.start === 'function') { + } else if(installed && !running && typeof feature.start === 'function') { featureChoices.push({ name: 'Start ' + name, value: () => feature.start() }) } diff --git a/scripts/priority.js b/scripts/priority.js index 7efdd3f..c42ddd4 100644 --- a/scripts/priority.js +++ b/scripts/priority.js @@ -3,6 +3,7 @@ import inquirer from 'inquirer' import { headerStyle } from './styles.js' import { aoEnv } from './settings.js' import { getCard, prioritizeCard, completeCard, uncheckCard, refocusCard } from './api.js' +import { createCardInteractive } from './cards.js' // Prints the text (.name) of the first card prioritized in the logged-in users member card export async function getTopPriorityText() { @@ -46,9 +47,17 @@ export async function prioritiesMenu(taskId = null) { return false } const card = fetchedCards[0] - const priorityCards = fetchedCards.slice(1) // First card is member card itself + 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) @@ -61,6 +70,19 @@ export async function prioritiesMenu(taskId = null) { short: priorityCard.name.substring(0, 70) + priorityCard.name.length >= 70 ? '...' : '' } }) + 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, new inquirer.Separator('───')) + } + } prioritiesChoices.push( { name: 'Create priority', value: 'create_here', short: 'new priority' }, { name: 'Back to Deck', value: false, short: 'back' } @@ -68,11 +90,11 @@ export async function prioritiesMenu(taskId = null) { let answer try { answer = await inquirer.prompt({ - name: 'priorities_menu', - type: 'rawlist', - message: 'Please choose:', - choices: prioritiesChoices, - loop: false + name: 'priorities_menu', + type: 'rawlist', + message: 'Please choose:', + choices: prioritiesChoices, + loop: false }) } catch(error) { if (error === 'EVENT_INTERRUPTED') { @@ -86,7 +108,6 @@ export async function prioritiesMenu(taskId = null) { case 'create_here': let previousCardCreatedText do { - console.log('previousCardCreatedText is', previousCardCreatedText) previousCardCreatedText = await createCardInteractive() } while(previousCardCreatedText != '\n') return true @@ -98,7 +119,7 @@ export async function prioritiesMenu(taskId = null) { const chosenTaskId = chosenTask.taskId let previousAnswer do { - previousAnswer = await priorityCardMenu(chosenTask, answer.priorities_menu.index) + previousAnswer = await priorityCardMenu(chosenTask, answer.priorities_menu.index, priorityCards) if(previousAnswer) { const fetchedCards = await getCard(chosenTaskId, false) if(!fetchedCards || fetchedCards.length < 1) { @@ -114,7 +135,7 @@ export async function prioritiesMenu(taskId = null) { // 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 -async function priorityCardMenu(card, index) { +async function priorityCardMenu(card, index, allPriorities) { if(!card) { console.log('priorityCardMenu: card is required.') return false @@ -161,7 +182,49 @@ async function priorityCardMenu(card, index) { } break case 'upboat': - await prioritizeCard(taskId, memberId) + console.log('upboat') + let firstEchelonScore + let newEchelonScore + let newPosition = 0 + console.log('upboat2') + console.log('card is', card) + //console.log(allPriorities) + breakHere: + for(let i = 0; i < allPriorities.length; i++) { + console.log('upboat3') + const priority = allPriorities[i] + console.log('priority is', priority) + if(i === 0) { + console.log('upboat3.1') + firstEchelonScore = priority.echelon + console.log('upboat3.11115', priority.name, priority.echelon, typeof priority.echelon) + if(isNaN(firstEchelonScore)) { + console.log('upboat3.2') + newEchelonScore = 1 + break breakHere + } + if(!card.echelon || card.echelon < priority.echelon) { + console.log('upboat3.3') + newEchelonScore = priority.echelon + } else if(card.echelon && priority.echelon && card.echelon === priority.echelon) { + console.log('upboat3.4') + newEchelonScore = priority.echelon + 1 + break breakHere + } else if(card.echelon && priority.echelon && card.echelon > priority.echelon) { + console.log('upboat3.5') + break breakHere + } + } + console.log('upboat4') + if(priority.echelon !== firstEchelonScore) { + newPosition = i + break breakHere + } + console.log('upboat5') + } + console.log('upboat6') + console.log('newPosition is', newPosition, 'and newEchelonScore is', newEchelonScore) + await prioritizeCard(taskId, memberId, newPosition, newEchelonScore) return false case 'downboat': await refocusCard(taskId, memberId) @@ -171,5 +234,6 @@ async function priorityCardMenu(card, index) { default: return false } + console.log('broken') return true } diff --git a/scripts/services.js b/scripts/services.js index 0844a29..9baf31e 100644 --- a/scripts/services.js +++ b/scripts/services.js @@ -114,6 +114,7 @@ export default class SystemServiceManager { willBeUninstall(verbose = true) { this.assertInitialized() console.log('Deleting service file. You may be asked for you sudo password.') + this.stop() try { execSync('sudo rm ' + this.servicePath()) if(verbose) console.log("Deleted service file.") @@ -222,4 +223,4 @@ export async function addCustomServiceInteractive() { } addCustomService(serviceName) return false -} \ No newline at end of file +}