diff --git a/assets/css/new-challenge.css b/assets/css/new-challenge.css index 6aee7246..ba1c5235 100644 --- a/assets/css/new-challenge.css +++ b/assets/css/new-challenge.css @@ -125,16 +125,8 @@ gre7-border.border-box code { padding-right: 12px; } - #directory-path:empty { - display: none; - } - - #directory-path:before { - content: 'Verifying: '; - } - #path-required-warning, - #directory-path { + #div-selected-dir{ padding: 6px 12px 6px 10px; vertical-align: middle; margin-bottom: 0; @@ -146,18 +138,6 @@ gre7-border.border-box code { color: #8cf; } - #path-required-warning { - display: none; - } - - #path-required-warning.show { - display: inline; - } - - #path-required-warning.show + #directory-path { - display: none; - } - .verify-fail:before { content: '✗ '; } diff --git a/docs.md b/docs.md index 3162271e..e18fedbb 100644 --- a/docs.md +++ b/docs.md @@ -15,8 +15,7 @@ How does this thing work? Below are general descriptions of directories and cont - **partials** HTML bits that are shared between either challenges or non-challenges pages, to be used in order to generate full HTML for pages. - **i18n** The translation-json-files. Translations are managed on Transifex. - **layouts** Handlebars templates to compile pages. -- **tests** App's test files. -- **empty-data.json** The starter file that is duplicated and stored on the user's computer with their challenge completed statuses as they go through the lessons. +- **empty-user-data.json** The starter file that is duplicated and stored on the user's computer with their challenge completed statuses as they go through the lessons. - **main.js** App's main process file which spins up the renderer view for the pages. - **package.json** App's details and dependencies. @@ -33,4 +32,4 @@ The pages that the app displays are HTML, just like a website. The `assets` dire The `lib` directory contains scripts that each page uses. Inside of `lib/verify` are scripts for each challenge that tell it how to verify that challenge. The scripts `helpers.js` and `user-data.js` are shared between scripts. **Templating** -There are scripts, templates and partials involved with generating the HTML pages. The main content for the challenges and non-challenge pages are within `resources/content/challenges` and `resources/content/pages`. The directory `resources/layouts` contains the templates, `resources/partials` the partials that are combined with the main content files according to the template. The scripts `lib/build-page.js` and `lib/build-challenges.js` put it all in motion to generate the final HTML output which is placed inside a `built`-folder. You can run these scripts from the command line. +There are scripts, templates and partials involved with generating the HTML pages. The main content for the challenges and non-challenge pages are within `resources/content/challenges` and `resources/content/pages`. The directory `resources/layouts` contains the templates, `resources/partials` the partials that are combined with the main content files according to the template. The scripts `lib/build/build-pages.js` and `lib/build/build-challenges.js` put it all in motion to generate the final HTML output which is placed inside a `built`-folder. You can run these scripts from the command line. diff --git a/empty-data.json b/empty-data.json deleted file mode 100644 index 57117965..00000000 --- a/empty-data.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "get_git": { - "completed": false, - "current_challenge": false, - "next_challenge": "repository", - "previous_challenge": "merge_tada" - }, - "repository": { - "completed": false, - "current_challenge": false, - "next_challenge": "commit_to_it", - "previous_challenge": "get_git" - }, - "commit_to_it": { - "completed": false, - "current_challenge": false, - "next_challenge": "githubbin", - "previous_challenge": "repository" - }, - "githubbin": { - "completed": false, - "current_challenge": false, - "next_challenge": "remote_control", - "previous_challenge": "commit_to_it" - }, - "remote_control": { - "completed": false, - "current_challenge": false, - "next_challenge": "forks_and_clones", - "previous_challenge": "githubbin" - }, - "forks_and_clones": { - "completed": false, - "current_challenge": false, - "next_challenge": "branches_arent_just_for_birds", - "previous_challenge": "remote_control" - }, - "branches_arent_just_for_birds": { - "completed": false, - "current_challenge": false, - "next_challenge": "its_a_small_world", - "previous_challenge": "forks_and_clones" - }, - "its_a_small_world": { - "completed": false, - "current_challenge": false, - "next_challenge": "pull_never_out_of_date", - "previous_challenge": "branches_arent_just_for_birds" - }, - "pull_never_out_of_date": { - "completed": false, - "current_challenge": false, - "next_challenge": "requesting_you_pull_please", - "previous_challenge": "its_a_small_world" - }, - "requesting_you_pull_please": { - "completed": false, - "current_challenge": false, - "next_challenge": "merge_tada", - "previous_challenge": "pull_never_out_of_date" - }, - "merge_tada": { - "completed": false, - "current_challenge": false, - "next_challenge": "get_git", - "previous_challenge": "requesting_you_pull_please" - } -} diff --git a/empty-user-data.json b/empty-user-data.json new file mode 100644 index 00000000..8ad69675 --- /dev/null +++ b/empty-user-data.json @@ -0,0 +1,46 @@ +{ + "get_git": { + "challengeId": 1, + "completed": false + }, + "repository": { + "challengeId": 2, + "completed": false + }, + "commit_to_it": { + "challengeId": 3, + "completed": false + }, + "githubbin": { + "challengeId": 4, + "completed": false + }, + "remote_control": { + "challengeId": 5, + "completed": false + }, + "forks_and_clones": { + "challengeId": 6, + "completed": false + }, + "branches_arent_just_for_birds": { + "challengeId": 7, + "completed": false + }, + "its_a_small_world": { + "challengeId": 8, + "completed": false + }, + "pull_never_out_of_date": { + "challengeId": 9, + "completed": false + }, + "requesting_you_pull_please": { + "challengeId": 10, + "completed": false + }, + "merge_tada": { + "challengeId": 11, + "completed": false + } +} diff --git a/lib/build-challenges.js b/lib/build/build-challenges.js similarity index 97% rename from lib/build-challenges.js rename to lib/build/build-challenges.js index ea7984aa..61034c83 100644 --- a/lib/build-challenges.js +++ b/lib/build/build-challenges.js @@ -1,13 +1,14 @@ /* + * Runs in: Node - Build Application * This file builds out the challenge web pages. A simple static site * generator. It uses `partials` and `layouts`. */ const fs = require('fs') const path = require('path') const Handlebars = require('handlebars') -const { getLocaleMenu } = require('./i18n-helpers.js') +const { getLocaleMenu } = require('./build-helpers.js') -const basepath = path.normalize(path.join(__dirname, '..')) +const basepath = path.normalize(path.join(__dirname, '../..')) const inputFolder = path.join(basepath, 'resources', 'content', 'challenges') const outputFolder = path.join(basepath, 'built', 'challenges') const partialsFolder = path.join(basepath, 'resources', 'content', 'partials') diff --git a/lib/i18n-helpers.js b/lib/build/build-helpers.js similarity index 64% rename from lib/i18n-helpers.js rename to lib/build/build-helpers.js index b46aebfc..a9e76089 100644 --- a/lib/i18n-helpers.js +++ b/lib/build/build-helpers.js @@ -1,13 +1,14 @@ /* - * Some helper functions to be used where necessary + * Runs in: Node - Build Application + * Some helper functions to be used in builds */ /** * Build locale menu * @return {string} */ -const getLocaleMenu = function () { - const { appLanguages } = require('../config/i18next.config') +function getLocaleMenu () { + const { appLanguages } = require('../../config/i18next.config') let localeMenu = '' Object.entries(appLanguages).forEach(([languageKey, language]) => { diff --git a/lib/build-pages.js b/lib/build/build-pages.js similarity index 93% rename from lib/build-pages.js rename to lib/build/build-pages.js index d1302e1c..fc5496b0 100644 --- a/lib/build-pages.js +++ b/lib/build/build-pages.js @@ -1,13 +1,14 @@ /* + * Runs in: Node - Build Application * This file builds out the general web pages (like the about page). A simple * static site generator. It uses `partials` and `layouts`. */ const fs = require('fs') const path = require('path') const Handlebars = require('handlebars') -const { getLocaleMenu } = require('./i18n-helpers.js') +const { getLocaleMenu } = require('./build-helpers.js') -const basepath = path.normalize(path.join(__dirname, '..')) +const basepath = path.normalize(path.join(__dirname, '../..')) const inputFolder = path.join(basepath, 'resources', 'content', 'pages') const outputFolder = path.join(basepath, 'built', 'pages') const partialsFolder = path.join(basepath, 'resources', 'content', 'partials') diff --git a/lib/challenge-completed.js b/lib/challenge-completed.js deleted file mode 100644 index 588263c8..00000000 --- a/lib/challenge-completed.js +++ /dev/null @@ -1,113 +0,0 @@ -// -// This file touches the DOM and provides an API for setting the page after -// challenge has been completed by toggling the buttons that are disabled -// or enabled. It as updates user-data. -// -// It is required by each challenge's verify file. -// - -var fs = require('fs') -var path = require('path') - -var userData = require(path.normalize(path.join(__dirname, 'user-data.js'))) -var data -var spinner - -// on click, disable the verify button -var verifyButton = document.getElementById('verify-challenge') -var directoryPathContent = document.getElementById('directory-path') -var verifySpinner = document.getElementById('verify-spinner') -var clearStatusButton = document.getElementById('clear-completed-challenge') - -verifyButton.addEventListener('click', function () { - // unless they didn't select a directory - if (directoryPathContent && directoryPathContent.innerText && !directoryPathContent.innerText.match(/Please select/)) { - document.getElementById('verify-list').style.display = 'block' - disableVerifyButtons(true) - startSpinner() - } - // if there is no directory button - if (!directoryPathContent) { - document.getElementById('verify-list').style.display = 'block' - disableVerifyButtons(true) - startSpinner() - } -}) - -var startSpinner = function () { - spinner = setTimeout(spinnerDelay, 100) -} - -var spinnerDelay = function () { - // If clear button exists then challenge is completed - if (clearStatusButton.style.display === 'none') { - verifySpinner.style.display = 'inline-block' - } -} - -var disableVerifyButtons = function (boolean) { - document.getElementById('verify-challenge').disabled = boolean - var directoryButton = document.getElementById('select-directory') - if (directoryButton) { document.getElementById('select-directory').disabled = boolean } -} - -var enableClearStatus = function (challenge) { - // hide spinner - // TODO cancel the timeout here - verifySpinner.style.display = 'none' - disableVerifyButtons(true) - clearStatusButton.style.display = 'inline-block' - clearStatusButton.addEventListener('click', function clicked (event) { - // set challenge to uncompleted and update the user's data file - data.contents[challenge].completed = false - fs.writeFileSync(data.path, JSON.stringify(data.contents, null, 2)) - // remove the completed status from the page and renable verify button - document.getElementById(challenge).classList.remove('completed') - disableVerifyButtons(false) - removeClearStatus() - - // if there is a list of passed parts of challenge, remove it - var verifyList = document.getElementById('verify-list') - if (verifyList) verifyList.style.display = 'none' - }) -} - -var removeClearStatus = function () { - clearStatusButton.style.display = 'none' -} - -var completed = function (challenge) { - document.addEventListener('DOMContentLoaded', function (event) { - checkCompletedness(challenge) - - Object.keys(data.contents).forEach(function (key) { - if (data.contents[key].completed) { - document.getElementById(key).classList.add('completed') - } - }) - }) - - function checkCompletedness (challenge) { - data = userData.getData() - if (data.contents[challenge].completed) { - document.getElementById(challenge).classList.add('completed') - var header = document.getElementsByTagName('header')[0] - header.className += ' challenge-is-completed' - // If completed, show clear button and disable verify button - enableClearStatus(challenge) - disableVerifyButtons(true) - } else removeClearStatus() - } -} - -var challengeIncomplete = function () { - clearTimeout(spinner) - // re-enable the verify button - disableVerifyButtons(false) - verifySpinner.style.display = 'none' -} - -module.exports.enableClearStatus = enableClearStatus -module.exports.completed = completed -module.exports.disableVerifyButtons = disableVerifyButtons -module.exports.challengeIncomplete = challengeIncomplete diff --git a/lib/challenge-sidebar-handler.js b/lib/challenge-sidebar-handler.js new file mode 100644 index 00000000..cf2d3454 --- /dev/null +++ b/lib/challenge-sidebar-handler.js @@ -0,0 +1,30 @@ +/* + * Runs in: Renderer-Process + * Cares for the Challenge-Sidebar to be up-to-date with completed-status + */ +const path = require('path') +const userData = require(path.normalize(path.join(__dirname, 'user-data.js'))) + +// Execute after DOM loaded. +document.addEventListener('DOMContentLoaded', () => { + const challengeUserData = userData.getChallengeData() + + Object.keys(challengeUserData).forEach((challengeKey) => { + if (challengeUserData[challengeKey].completed) { + showChallengeCompleted(challengeKey, true) + } + }) +}) + +/* + * Show a challenge as completed/incomplete in sidebar + */ +function showChallengeCompleted (challengeKey, completed) { + if (completed) { + document.getElementById('sidebar-' + challengeKey).classList.add('completed') + } else { + document.getElementById('sidebar-' + challengeKey).classList.remove('completed') + } +} + +exports.showChallengeCompleted = showChallengeCompleted diff --git a/lib/challenge-verify-handler.js b/lib/challenge-verify-handler.js new file mode 100644 index 00000000..48b66d8b --- /dev/null +++ b/lib/challenge-verify-handler.js @@ -0,0 +1,208 @@ +/* + * Runs in: Renderer-Process + * To handle everything connected to the verification on challenges + */ +const path = require('path') +const ipc = require('electron').ipcRenderer +const i18n = require('electron').remote.getGlobal('i18n') +const userData = require(path.normalize(path.join(__dirname, 'user-data.js'))) +const sidebarHandler = require(path.normalize(path.join(__dirname, 'challenge-sidebar-handler.js'))) + +const currentChallenge = window.currentChallenge +const verifyButton = document.getElementById('btn-verify-challenge') +const clearStatusButton = document.getElementById('btn-clear-challenge') +const verifyList = document.getElementById('verify-list') +const verifySpinner = document.getElementById('verify-spinner') +const verifyChallengeScript = require('../lib/verify/' + currentChallenge + '.js') + +// Only on challenges with directory +const selectDirButton = document.getElementById('btn-select-directory') +const pathRequiredWarning = document.getElementById('path-required-warning') +const selectedDirPath = document.getElementById('path-selected-dir') +const selectedDirDiv = document.getElementById('div-selected-dir') + +/* + * Execute after DOM loaded. + * Register Button-Handlers + */ +document.addEventListener('DOMContentLoaded', () => { + verifyButton.addEventListener('click', () => { + handleVerifyClick() + }) + clearStatusButton.addEventListener('click', () => { + clearChallengeStatus() + }) + // Only if Button exists + if (selectDirButton) { + selectDirButton.addEventListener('click', () => { + ipc.send('dialog-selectDir') + }) + } + + const challengeUserData = userData.getChallengeData(currentChallenge) + if (challengeUserData.completed) { + enableVerifyButtons(false) + enableClearStatusButton(true) + } else { + enableVerifyButtons(true) + enableClearStatusButton(false) + } + + // Only if button exists + if (selectDirButton) { + let savedDir = userData.getSavedDir().savedDir + // On 'forks_and_clones', clear savedDir as it should change. + if (currentChallenge === 'forks_and_clones') { + savedDir = null + } + if (!savedDir) { + showSelectedDirDiv(false) + } else { + selectedDirPath.innerText = savedDir + showSelectedDirDiv(true) + } + } +}) + +/* + * Listen for confirmed selectDir dialog + */ +ipc.on('confirm-selectDir', (event, path) => { + selectedDirPath.innerText = path + showSelectedDirDiv(true) + userData.updateSavedDir(path) +}) + +/* + * Handling Click-event on verify-button + */ +async function handleVerifyClick () { + // If challenge with directory, but no path is selected + if (selectDirButton && !selectedDirPath?.innerText.length) { + console.log('No path selected!') + showSelectedDirDiv(false) + return + } + + showSpinner(true) + enableVerifyButtons(false) + + // In js it is possible to call with more parameters, than defined + const result = await verifyChallengeScript(selectedDirPath?.innerText) + + clearVerifyList() + printOutVerifyList(result) + + if (result.challengeComplete) { + enableClearStatusButton(true) + userData.setChallengeCompleted(currentChallenge, true) + sidebarHandler.showChallengeCompleted(currentChallenge, true) + } else { + enableVerifyButtons(true) + } + + showSpinner(false) + // TODO write verifyList to userData to be able to reload it (also when reloading translations)? +} + +/* + * Clear the Challenge-Status + * Called on Button-Click + */ +async function clearChallengeStatus () { + enableVerifyButtons(true) + enableClearStatusButton(false) + showSpinner(false) + + clearVerifyList() + showVerifyList(false) + sidebarHandler.showChallengeCompleted(currentChallenge, false) + userData.setChallengeCompleted(currentChallenge, false) +} + +/* + * Write out the result.verifyList to the DOM + */ +function printOutVerifyList (result) { + showVerifyList(true) + result.verifyList.forEach(listItem => { + const li = document.createElement('li') + + li.appendChild(document.createTextNode(listItem.message)) + if (listItem.pass) { + li.classList.add('verify-pass') + } else { + li.classList.add('verify-fail') + } + + verifyList.appendChild(li) + }) +} + +/* + * Small helper for readable code + */ +function clearVerifyList () { + verifyList.innerHTML = '' +} + +/* + * clearStatusButton gets shown or not on enabled + */ +function enableClearStatusButton (enabled) { + if (enabled) { + clearStatusButton.style.display = 'block' + } else { + clearStatusButton.style.display = 'none' + } +} + +/* + * VerifyButton gets disabled or not. + */ +function enableVerifyButtons (enabled) { + verifyButton.disabled = !enabled + if (selectDirButton) { + selectDirButton.disabled = !enabled + } +} + +/* + * Either show selectedDirDiv or show PathRequiredWarning + * If selectedDir is shown, the button should say 'Change Directory', else 'Select Directory' + */ +function showSelectedDirDiv (show) { + if (show) { + selectedDirDiv.style.display = 'inline' + pathRequiredWarning.style.display = 'none' + selectDirButton.innerText = i18n.t('verify~Change Directory') + selectDirButton.setAttribute('i18n-data', 'verify~Change Directory') + } else { + selectedDirDiv.style.display = 'none' + pathRequiredWarning.style.display = 'inline' + selectDirButton.innerText = i18n.t('verify~Select directory') + selectDirButton.setAttribute('i18n-data', 'verify~Select directory') + } +} + +/* + * Show the verifySpinner or not + */ +function showSpinner (show) { + if (show) { + verifySpinner.style.display = 'inline-block' + } else { + verifySpinner.style.display = 'none' + } +} + +/* + * Show verifyList or not + */ +function showVerifyList (show) { + if (show) { + verifyList.style.display = 'block' + } else { + verifyList.style.display = 'none' + } +} diff --git a/lib/challenge.js b/lib/challenge.js deleted file mode 100644 index cf39b4ac..00000000 --- a/lib/challenge.js +++ /dev/null @@ -1,58 +0,0 @@ -// -// This file is loaded by every challenge's HTML, it listens to events on the -// verify button and provides the file-chooser dialog when the challenge needs. -// - -var path = require('path') -const i18n = require('electron').remote.getGlobal('i18n') - -var userData = require(path.normalize(path.join(__dirname, 'user-data.js'))) -var selectDirBtn = document.getElementById('select-directory') -var currentChallenge = window.currentChallenge - -var selectDirectory = function (path) { - document.getElementById('path-required-warning').classList.remove('show') - document.getElementById('directory-path').innerText = path -} -if (selectDirBtn) { - var ipc = require('electron').ipcRenderer - - selectDirBtn.addEventListener('click', function clickedDir (event) { - ipc.send('open-file-dialog') - }) - - ipc.on('selected-directory', function (event, path) { - selectDirectory(path[0]) - userData.updateCurrentDirectory(path) - }) -} - -var currentDirectory = userData.getSavedDir().contents.savedDir -var challengeCompleted = userData.getData().contents -if (currentChallenge === 'forks_and_clones') { - // on this challenge clear out the saved dir because it should change - userData.updateCurrentDirectory(null) -} else if (selectDirBtn && currentDirectory && !challengeCompleted[currentChallenge].completed) { - selectDirectory(currentDirectory) - selectDirBtn.innerHTML = i18n.t('Change Directory') -} - -// Handle verify challenge click -document.getElementById('verify-challenge').addEventListener('click', function clicked (event) { - var verifyChallenge = require('../lib/verify/' + currentChallenge + '.js') - - // If a directory is needed - if (selectDirBtn) { - var path = document.getElementById('directory-path').innerText - - if (path === '') { - document.getElementById('path-required-warning').classList.add('show') - } else { - document.getElementById('verify-list').innerHTML = '' - verifyChallenge(path) - } - } else { - document.getElementById('verify-list').innerHTML = '' - verifyChallenge() - } -}) diff --git a/lib/challenges-completed.js b/lib/challenges-completed.js deleted file mode 100644 index 1e74aaab..00000000 --- a/lib/challenges-completed.js +++ /dev/null @@ -1,94 +0,0 @@ -// -// Renderer Process—This file is required by the index page. -// It touches the DOM by showing progress in challenge completion. -// It also handles the clear buttons and writing to user-data. -// - -var fs = require('electron').remote.require('fs') -var path = require('electron').remote.require('path') -var ipc = require('electron').ipcRenderer - -var userData = require(path.normalize(path.join(__dirname, '../../lib/user-data.js'))) - -document.addEventListener('DOMContentLoaded', function (event) { - var data = userData.getData() - - // Buttons - var clearAllButtons = document.querySelectorAll('.js-clear-all-challenges') - var leftOffButton = document.getElementById('left-off-from') - // Sections - var showFirstRun = document.getElementById('show-first-run') - var showWipRun = document.getElementById('show-wip-run') - var showFinishedRun = document.getElementById('show-finished-run') - - updateIndex(data.contents) - - // Listen for Clear All Button Events, trigger confirmation dialog - for (var i = 0; i < clearAllButtons.length; i++) { - clearAllButtons[i].addEventListener('click', function () { - ipc.send('confirm-clear') - }, false) - } - - ipc.on('confirm-clear-response', function (event, response) { - if (response === 0) { - clearAllChallenges(data) - } - }) - - // Go through each challenge in user data to see which are completed - function updateIndex (data) { - var circles = document.querySelectorAll('.progress-circle') - var counter = 0 - var completed = 0 - - for (var chal in data) { - if (data[chal].completed) { - // A challenge is completed so show the WIP run HTML - showWipRun.style.display = 'block' - showFirstRun.style.display = 'none' - showFinishedRun.style.display = 'none' - // Mark the corresponding circle as completed - circles[counter].classList.add('completed') - // Show the button to go to next challenge - leftOffButton.href = path.join(__dirname, '..', 'challenges', data[chal].next_challenge + '.html') - completed++ - counter++ - } else { - counter++ - } - } - - if (completed === 0) { - // No challenges are complete, show the first run HTML - showFirstRun.style.display = 'block' - showWipRun.style.display = 'none' - showFinishedRun.style.display = 'none' - } - - if (completed === Object.keys(data).length) { - // All of the challenges are complete! Show the finished run HTML - showFirstRun.style.display = 'none' - showWipRun.style.display = 'none' - showFinishedRun.style.display = 'block' - } - } - - function clearAllChallenges (data) { - for (var chal in data.contents) { - if (data.contents[chal].completed) { - data.contents[chal].completed = false - } - } - fs.writeFileSync(data.path, JSON.stringify(data.contents, null, 2)) - // If they clear all challenges, go back to first run HTML - var circles = document.querySelectorAll('.progress-circle') - Array.prototype.forEach.call(circles, function (el) { - el.classList.remove('completed') - }) - - showFirstRun.style.display = 'block' - showWipRun.style.display = 'none' - showFinishedRun.style.display = 'none' - } -}) diff --git a/lib/exec-git.js b/lib/exec-git.js index fd561b81..621c023b 100644 --- a/lib/exec-git.js +++ b/lib/exec-git.js @@ -1,14 +1,13 @@ -// -// This file is a wrapper to the exec call used in each of the verify scripts. -// It first checks what operating system is being used and if Windows it uses -// the Portable Git rather than the system Git. -// +/* + * This file is a wrapper to the exec call used in each of the verify scripts. + * It first checks what operating system is being used and if Windows it uses + * the Portable Git rather than the system Git. + */ +const os = require('os') +const path = require('path') +const execSync = require('child_process').execSync -var execSync = require('child_process').execSync -var path = require('path') -var os = require('os') - -var winGit = path.join(__dirname, '../assets/PortableGit/bin/git.exe') +const winGit = path.join(__dirname, '../assets/PortableGit/bin/git.exe') module.exports = function execGit (command, options = {}) { // Encoding utf8 for usable output diff --git a/lib/handle-external-links.js b/lib/handle-external-links.js index 7445b4e1..5178ecd7 100644 --- a/lib/handle-external-links.js +++ b/lib/handle-external-links.js @@ -1,18 +1,20 @@ -// -// This file is used by ever web page to make sure all links that are not local -// are opened in the users default browser. -// +/* + * Runs in: Renderer-Process + * This file is used by every web page to make sure all links that are not local + * are opened in the users default browser. + */ +const shell = require('electron').shell -var shell = require('electron').shell +// Execute after DOM loaded +document.addEventListener('DOMContentLoaded', () => { + const links = document.querySelectorAll('a[href]') -document.addEventListener('DOMContentLoaded', function (event) { - var links = document.querySelectorAll('a[href]') - var array = [] - array.forEach.call(links, function (link) { - var url = link.getAttribute('href') - if (url.indexOf('http') > -1) { - link.addEventListener('click', function (e) { - e.preventDefault() + // On each external link add an event-listener, prevent default and open manually. + links.forEach(link => { + const url = link.getAttribute('href') + if (url.startsWith('http')) { + link.addEventListener('click', event => { + event.preventDefault() shell.openExternal(url) }) } diff --git a/lib/helpers.js b/lib/helpers.js deleted file mode 100644 index e89b703c..00000000 --- a/lib/helpers.js +++ /dev/null @@ -1,46 +0,0 @@ -// -// This file is used by every challenge's verify file and is an API for writing -// partial challenge completion messages to the DOM and setting a challenge -// as complete when all parts of a challenge have passed. -// -// It also sets the lanaguage for each challenge's verify's process's Git to -// English. -// - -var process = require('process') -var path = require('path') -var completed = require(path.join(__dirname, 'challenge-completed.js')) - -// Set each challenge verifying process to use -// English language pack -// Potentially move this to user-data.js -process.env.LANG = 'C' - -var ul = document.getElementById('verify-list') - -var addToList = function (message, status) { - var li = document.createElement('li') - var newContent = document.createTextNode(message) - li.appendChild(newContent) - if (status) { - li.classList.add('verify-pass') - } else { - li.classList.add('verify-fail') - } - ul.appendChild(li) - // potentially do this with domify and push - // into an array and add to dom once -} - -var markChallengeCompleted = function (challenge) { - document.getElementById(challenge).classList.add('completed') - completed.enableClearStatus(challenge) -} - -var challengeIncomplete = function () { - completed.challengeIncomplete() -} - -module.exports.markChallengeCompleted = markChallengeCompleted -module.exports.addToList = addToList -module.exports.challengeIncomplete = challengeIncomplete diff --git a/lib/i18n-translate.js b/lib/i18n-translate.js index 8ac04f3d..73329e86 100644 --- a/lib/i18n-translate.js +++ b/lib/i18n-translate.js @@ -1,4 +1,6 @@ /* + * Runs in: Renderer-Process + * * Script to include in HTML-Pages * - Insert translated Content * - Set Language Dropdown @@ -24,29 +26,35 @@ const defaultOptions = { smc: ';' // semicolon } -// Insert translated Content on Loading page. -document.addEventListener('DOMContentLoaded', function (event) { +/* + * Insert translated Content after loading page. + */ +document.addEventListener('DOMContentLoaded', () => { setDropdownLanguage() insertDataTranslations() insertBoxTranslationStyles() // Box-Titles are done in CSS, so need specific handling }) /* - * Language change listener + * Set Language change listener * When changeLanguage is done, the main process will reload the window therefore executing translation-insert from scratch. */ const languageSelector = document.getElementById('lang-select') -languageSelector.addEventListener('change', function (event) { +languageSelector.addEventListener('change', () => { i18n.changeLanguage(languageSelector.value) }) -// set Language on Dropdown-Element +/* + * Select current Language in Dropdown-Element + */ function setDropdownLanguage () { const languageSelector = document.getElementById('lang-select') languageSelector.value = i18n.language } -// Insert Translations into HTML +/* + * Insert Translations into HTML + */ function insertDataTranslations () { const i18nElements = document.querySelectorAll('[i18n-data]') diff --git a/lib/i18nInit.js b/lib/i18nInit.js index 682a5a1f..5d352fdb 100644 --- a/lib/i18nInit.js +++ b/lib/i18nInit.js @@ -1,18 +1,23 @@ /* + * Runs in: Main-Process * Init i18next and store instance on global i18n variable */ const i18next = require('i18next') const i18nextBackend = require('i18next-fs-backend') - const { i18nextConfig } = require('../config/i18next.config.js') // Store and Init i18next -function i18nInit () { +function i18nInit (buildMenuFunction, mainWindow) { global.i18n = i18next global.i18n .use(i18nextBackend) .init(i18nextConfig) + + global.i18n.on('languageChanged', () => { + buildMenuFunction() + mainWindow.reload() + }) } exports.i18nInit = i18nInit diff --git a/lib/index-challenge-handler.js b/lib/index-challenge-handler.js new file mode 100644 index 00000000..e5bb7dc5 --- /dev/null +++ b/lib/index-challenge-handler.js @@ -0,0 +1,106 @@ +/* + * Runs in: Renderer-Process + * + * This file is only required by the index page. + * It touches the DOM by showing progress in challenge completion. + * It also handles the clear buttons and writing to user-data. + */ +const path = require('path') +const ipc = require('electron').ipcRenderer +const userData = require(path.normalize(path.join(__dirname, '../../lib/user-data.js'))) + +const clearAllButtons = document.querySelectorAll('.js-clear-all-challenges') +const pickupButton = document.getElementById('pickup-button') +const progressCircles = document.querySelectorAll('.progress-circle') + +const IndexSectionStart = document.getElementById('index-section-start') +const IndexSectionWip = document.getElementById('index-section-wip') +const IndexSectionFinished = document.getElementById('index-section-finished') + +// Execute after DOM loaded. +document.addEventListener('DOMContentLoaded', () => { + // Get stored challengeUserData & store Key-Array as often used + const challengeUserData = userData.getChallengeData() + const challengeUserDataKeys = Object.keys(challengeUserData) + + // Go through the challenges in challengeUserData to see which are completed + let countCompleted = 0 + challengeUserDataKeys.forEach((challengeKey, index) => { + if (challengeUserData[challengeKey].completed) { + countCompleted++ + progressCircles[index].classList.add('completed') // Mark corresponding circle as completed + if (index < challengeUserDataKeys.length - 1) { + pickupButton.href = path.join(__dirname, '..', 'challenges', challengeUserDataKeys[index + 1] + '.html') // Set Link on pickup-button to next challenge + } + } + }) + + // If no challenges are complete, show the start-section + if (countCompleted === 0) { + showSection('start') + } + // If only some challenges are complete, show the wip-section + if (countCompleted !== 0 && countCompleted !== challengeUserDataKeys.length) { + showSection('wip') + } + // If all challenges are complete, show the finished-section + if (countCompleted === challengeUserDataKeys.length) { + showSection('finished') + } + + // Add Listeners to ClearAll Buttons + clearAllButtons.forEach(btn => { + btn.addEventListener('click', () => { + ipc.send('dialog-clearAll') + }) + }) +}) + +/* + * Catch confirmed Clear-All Dialog + * Clear all challenges on userData, progressCircles and show Start-Section + */ +ipc.on('confirm-clearAll', () => { + // Clear stored userData + userData.clearAllChallenges() + + // Clear all ProgressCircles in TopBar + progressCircles.forEach(progressCircle => { + progressCircle.classList.remove('completed') + }) + + // Show Start-Index Page + showSection('start') +}) + +/* + * Show exclusively the specified section. + * section = 'start'|'wip'|'finished' + */ +function showSection (section) { + // Only accept specific section-keywords + const accept = ['start', 'wip', 'finished'] + + // Log if wrong keyword + if (accept.indexOf(section) < 0) { + console.error('Wrong Section Keyword: ', section) + } + + switch (section) { + case 'finished': + IndexSectionStart.style.display = 'none' + IndexSectionWip.style.display = 'none' + IndexSectionFinished.style.display = 'block' + break + case 'wip': + IndexSectionStart.style.display = 'none' + IndexSectionWip.style.display = 'block' + IndexSectionFinished.style.display = 'none' + break + case 'start': + default: + IndexSectionStart.style.display = 'block' + IndexSectionWip.style.display = 'none' + IndexSectionFinished.style.display = 'none' + } +} diff --git a/lib/user-data.js b/lib/user-data.js index 3f323455..8d59aee1 100644 --- a/lib/user-data.js +++ b/lib/user-data.js @@ -1,48 +1,68 @@ -// -// This file talks to the main process by fetching the path to the user data. -// It also writes updates to the user-data file. -// +/* + * Runs in: Renderer-Process + * This file handles the user-data.json and saved-dir.json files + * Uses electron-remote fs-module to still write out async, even when function gets terminated. + */ +const fs = require('electron').remote.require('fs') +const ipc = require('electron').ipcRenderer +const path = require('path') -var fs = require('electron').remote.require('fs') +const userDataPath = ipc.sendSync('getUserDataPath') +const userDataFile = path.join(userDataPath, 'user-data.json') +const savedDirFile = path.join(userDataPath, 'saved-dir.json') -var ipc = require('electron').ipcRenderer - -var getData = function () { - var data = {} - data.path = ipc.sendSync('getUserDataPath', null) - data.contents = JSON.parse(fs.readFileSync(data.path)) - return data +/**** + * user-data.json + */ +// Returns either an object out of all challengesData-objects or if called with parameter the object to the given challenge. +function getChallengeData (challenge = null) { + if (challenge) { + return JSON.parse(fs.readFileSync(userDataFile))[challenge] + } + return JSON.parse(fs.readFileSync(userDataFile)) } - -var getSavedDir = function () { - var savedDir = {} - savedDir.path = ipc.sendSync('getUserSavedDir', null) - savedDir.contents = JSON.parse(fs.readFileSync(savedDir.path)) - return savedDir +// Set given challenge completed-status +function setChallengeCompleted (challenge, completed) { + const data = getChallengeData() + data[challenge].completed = completed + writeData(userDataFile, data) } - -var writeData = function (data) { - fs.writeFile(data.path, JSON.stringify(data.contents, null, ' '), function updatedUserData (err) { - if (err) return console.log(err) +// Clear all challenges - Set all challenges as incomplete +function clearAllChallenges () { + const data = getChallengeData() + Object.keys(data).forEach(challenge => { + data[challenge].completed = false }) + writeData(userDataFile, data) } -// this could take in a boolean on compelte status -// and be named better in re: to updating ONE challenge, not all -var updateData = function (challenge) { - var data = getData() - data.contents[challenge].completed = true - - writeData(data) +/**** + * saved-dir.json + */ +function getSavedDir () { + return JSON.parse(fs.readFileSync(savedDirFile)) +} +function updateSavedDir (path) { + const data = getSavedDir() + data.savedDir = path + writeData(savedDirFile, data) } -var updateCurrentDirectory = function (path) { - var data = getSavedDir() - data.contents.savedDir = path - writeData(data) +/**** + * Common function + * Write out data to file + * Writes out asynchronously. + */ +function writeData (file, data) { + fs.writeFile(file, JSON.stringify(data, null, 2), (err) => { + if (err) { + console.error(err) + } + }) } -module.exports.getData = getData -module.exports.getSavedDir = getSavedDir -module.exports.updateData = updateData -module.exports.updateCurrentDirectory = updateCurrentDirectory +exports.getChallengeData = getChallengeData +exports.setChallengeCompleted = setChallengeCompleted +exports.clearAllChallenges = clearAllChallenges +exports.getSavedDir = getSavedDir +exports.updateSavedDir = updateSavedDir diff --git a/lib/verify/branches_arent_just_for_birds.js b/lib/verify/branches_arent_just_for_birds.js index 63e3f859..41686702 100644 --- a/lib/verify/branches_arent_just_for_birds.js +++ b/lib/verify/branches_arent_just_for_birds.js @@ -2,28 +2,21 @@ const fs = require('fs') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) - -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'branches_arent_just_for_birds' -const total = 3 +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') // get their username // verify branch matches username, case too. // verify they've pushed // check the file is in contributors directory -module.exports = function verifyBranchesChallenge (repopath) { - let counter = 0 +module.exports = function (repopath) { + const result = getEmptyVerifyResult() let username = '' // path should be a directory if (!fs.lstatSync(repopath).isDirectory()) { - addToList(i18n.t('verify~Path is not a directory.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Path is not a directory.'), false) + return result } // Get stored username @@ -31,8 +24,8 @@ module.exports = function verifyBranchesChallenge (repopath) { const out = execGit('config user.username', { cwd: repopath }) username = out.trim() } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } let currentBranch = '' @@ -42,15 +35,13 @@ module.exports = function verifyBranchesChallenge (repopath) { const expectedBranch = 'add-' + username if (currentBranch.match(expectedBranch)) { - counter++ - addToList(i18n.t('verify~Found branch as expected!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Found branch as expected!'), true) } else { - addToList(i18n.t('verify~Branch name expected: {/expectedBranch/}', { expectedBranch }), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Branch name expected: {/expectedBranch/}', { expectedBranch }), false) } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } // see if repopath is already contributors folder & search file add-username @@ -65,16 +56,15 @@ module.exports = function verifyBranchesChallenge (repopath) { const allFiles = out.join() if (allFiles.match('add-' + username)) { - counter++ - addToList(i18n.t('verify~File in contributors folder!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~File in contributors folder!'), true) } else { - addToList(i18n.t('verify~File not found in contributors folder.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~File not found in contributors folder.'), false) + return result } } catch (err) { // TODO ENOENT: no such file or directory, scandir '/Users/jlord/jCode/.../contributors/' - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } // Check push @@ -85,20 +75,15 @@ module.exports = function verifyBranchesChallenge (repopath) { const log = out.trim() if (log.match('update by push')) { - counter++ - addToList(i18n.t('verify~Changes have been pushed!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Changes have been pushed!'), true) } else { - addToList(i18n.t('verify~Changes not pushed.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Changes not pushed.'), false) } } catch (err) { console.log(err.message) - addToList(i18n.t('verify~Changes not pushed.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Changes not pushed.'), false) } - if (counter === total) { - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) - } + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/commit_to_it.js b/lib/verify/commit_to_it.js index bb5f7acc..bd6adf83 100644 --- a/lib/verify/commit_to_it.js +++ b/lib/verify/commit_to_it.js @@ -2,21 +2,16 @@ const fs = require('fs') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) - -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'commit_to_it' +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') // check that they've commited changes +module.exports = function (path) { + const result = getEmptyVerifyResult() -module.exports = function commitVerify (path) { // path should be a directory if (!fs.lstatSync(path).isDirectory()) { - addToList(i18n.t('verify~Path is not a directory.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Path is not a directory.'), false) + return result } try { @@ -25,18 +20,16 @@ module.exports = function commitVerify (path) { // TODO Need to improve the checks? Especially Initial-commit is not used anymore in up-to-date git. if (show.match('Initial commit')) { - addToList(i18n.t("verify~Can't find committed changes."), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t("verify~Can't find committed changes."), false) } else if (show.match('nothing to commit')) { - addToList(i18n.t('verify~Changes have been committed!'), true) - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) + addToVerifyList(result.verifyList, i18n.t('verify~Changes have been committed!'), true) } else { - addToList(i18n.t('verify~Seems there are still changes to commit.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Seems there are still changes to commit.'), false) } } catch (err) { - addToList('Error: ' + err.message, false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) } + + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/forks_and_clones.js b/lib/verify/forks_and_clones.js index c39a9501..0f3223c8 100644 --- a/lib/verify/forks_and_clones.js +++ b/lib/verify/forks_and_clones.js @@ -2,24 +2,20 @@ const fs = require('fs') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) - -const addToList = helper.addToList - -const currentChallenge = 'forks_and_clones' +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') // check that they've added the remote, that shows // that they've also then forked and cloned. // TODO Separate check on fork for nice feedback?! -module.exports = function verifyForksAndClonesChallenge (path) { +module.exports = function (path) { + const result = getEmptyVerifyResult() let username = '' // path should be a directory if (!fs.lstatSync(path).isDirectory()) { - addToList(i18n.t('verify~Path is not a directory.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Path is not a directory.'), false) + return result } // Get stored username @@ -27,8 +23,8 @@ module.exports = function verifyForksAndClonesChallenge (path) { const out = execGit('config user.username') username = out.trim() } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } let remotes = [] @@ -37,40 +33,31 @@ module.exports = function verifyForksAndClonesChallenge (path) { remotes = out.trim().split('\n') if (remotes.length !== 4) { - addToList(i18n.t('verify~Did not find 2 remotes set up.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Did not find 2 remotes set up.'), false) } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) } remotes.splice(1, 2) - let incomplete = false remotes.forEach(remote => { if (remote.match('origin')) { - if (remote.match('github.com[:/]' + username + '/')) { - addToList(i18n.t('verify~Origin points to your fork!'), true) + if (remote.match('github.com[:/]' + username + '/patchwork')) { + addToVerifyList(result.verifyList, i18n.t('verify~Origin points to your fork!'), true) } else { - incomplete = true - addToList(i18n.t('verify~No Origin remote found pointing to {/userrepo/}.', { userrepo: username + '/patchwork' }), false) + addToVerifyList(result.verifyList, i18n.t('verify~No Origin remote found pointing to {/userrepo/}.', { userrepo: username + '/patchwork' }), false) } } if (remote.match('upstream')) { - if (remote.match('github.com[:/]jlord/')) { - addToList(i18n.t('verify~Upstream remote set up!'), true) + if (remote.match('github.com[:/]jlord/patchwork')) { + addToVerifyList(result.verifyList, i18n.t('verify~Upstream remote set up!'), true) } else { - incomplete = true - addToList(i18n.t('verify~No Upstream remote found pointing to {/patchworkrepo/}.', { patchworkrepo: 'jlord/patchwork' }), false) + addToVerifyList(result.verifyList, i18n.t('verify~No Upstream remote found pointing to {/patchworkrepo/}.', { patchworkrepo: 'jlord/patchwork' }), false) } } }) - if (incomplete) { - return helper.challengeIncomplete() - } - - userData.updateData(currentChallenge) - helper.markChallengeCompleted(currentChallenge) + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/get_git.js b/lib/verify/get_git.js index 3a1e9d93..cb004c21 100644 --- a/lib/verify/get_git.js +++ b/lib/verify/get_git.js @@ -1,22 +1,13 @@ const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) - -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'get_git' -const total = 3 +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') // TODO // Think about how best to show errors to user -// All that nesting -// Potentially put all responses in array, use length for total -module.exports = function verifyGetGitChallenge () { - let counter = 0 +module.exports = function () { + const result = getEmptyVerifyResult() // Check version, return if no git installed, as further checks are useless. try { @@ -24,16 +15,15 @@ module.exports = function verifyGetGitChallenge () { const gitOutput = out.trim() if (gitOutput.match('git version')) { - counter++ - addToList(i18n.t('verify~Found Git installed!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Found Git installed!'), true) } else { - addToList(i18n.t('verify~Did not find Git installed.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Did not find Git installed.'), false) + return result } } catch (err) { console.log(err) - addToList(i18n.t('verify~Did not find Git installed.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Did not find Git installed.'), false) + return result } // Check user.name @@ -42,17 +32,13 @@ module.exports = function verifyGetGitChallenge () { const name = out.trim() if (name !== '') { - counter++ - addToList(i18n.t('verify~Name Added!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Name Added!'), true) } else { - addToList(i18n.t('verify~No name found.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~No name found.'), false) } } catch (err) { console.log(err) - addToList(i18n.t('verify~No name found.'), false) - helper.challengeIncomplete() - return false + addToVerifyList(result.verifyList, i18n.t('verify~No name found.'), false) } // Check user.email @@ -61,20 +47,15 @@ module.exports = function verifyGetGitChallenge () { const email = out.trim() if (email !== '') { - counter++ - addToList(i18n.t('verify~Email Added!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Email Added!'), true) } else { - addToList(i18n.t('verify~No email found.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~No email found.'), false) } } catch (err) { console.log(err) - addToList(i18n.t('verify~No email found.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~No email found.'), false) } - if (counter === total) { - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) - } + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/githubbin.js b/lib/verify/githubbin.js index 954730f8..ee8f96c6 100644 --- a/lib/verify/githubbin.js +++ b/lib/verify/githubbin.js @@ -2,14 +2,7 @@ const axios = require('axios') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) - -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'githubbin' -const total = 3 +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') // Specifically request current REST-API v3 from Github const axiosOptions = { @@ -22,8 +15,8 @@ const axiosOptions = { // verify that user exists on GitHub (not case sensitve) // compare the two to make sure cases match -module.exports = async function verifyGitHubbinChallenge () { - let counter = 0 +module.exports = async function () { + const result = getEmptyVerifyResult() let username = '' let githubUsername = '' @@ -33,16 +26,15 @@ module.exports = async function verifyGitHubbinChallenge () { username = out.trim() if (username === '') { - addToList(i18n.t('verify~No username found.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~No username found.'), false) + return result } else { - counter++ - addToList(i18n.t('verify~Username added to Git config!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Username added to Git config!'), true) } } catch (err) { // TODO Catch 'Command failed: /bin/sh -c git config user.username' - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } // Check if respecitve user exists on github @@ -50,35 +42,29 @@ module.exports = async function verifyGitHubbinChallenge () { const response = await axios.get('https://api.github.com/users/' + username, axiosOptions) if (response.status === 200) { - counter++ - addToList(i18n.t("verify~You're on GitHub!"), true) + addToVerifyList(result.verifyList, i18n.t("verify~You're on GitHub!"), true) githubUsername = response.data.login } else { - addToList(i18n.t('verify~GitHub account matching stored Git-username was not found.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~GitHub account matching stored Git-username was not found.'), false) + return result } } catch (err) { if (err.response.status === 404) { - addToList(i18n.t('verify~GitHub account matching stored Git-username was not found.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~GitHub account matching stored Git-username was not found.'), false) + return result } else { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } } // Check if stored username matches Github-Username - CaseSensitive! if (username === githubUsername) { - counter++ - addToList(i18n.t('verify~Username identical on GitHub and Git config!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Username capitalisation identical on GitHub and Git config!'), true) } else { - addToList(i18n.t('verify~GitHub & Git config usernames do not match.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~GitHub & Git config usernames do not match (Take care on capitalisation!).'), false) } - // Check if all challenges succeeded. - if (counter === total) { - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) - } + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/its_a_small_world.js b/lib/verify/its_a_small_world.js index 390d210e..24f5cc45 100644 --- a/lib/verify/its_a_small_world.js +++ b/lib/verify/its_a_small_world.js @@ -2,17 +2,12 @@ const axios = require('axios') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') const url = 'http://reporobot.jlord.us/collab?username=' -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'its_a_small_world' - -module.exports = async function verifySmallWorldChallenge () { +module.exports = async function () { + const result = getEmptyVerifyResult() let username = '' // Get stored username @@ -20,8 +15,8 @@ module.exports = async function verifySmallWorldChallenge () { const out = execGit('config user.username') username = out.trim() } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } // check that they've added RR as a collaborator @@ -30,21 +25,20 @@ module.exports = async function verifySmallWorldChallenge () { if (response.status === 200) { if (response.data.collab === true) { - addToList(i18n.t('verify~Reporobot has been added!'), true) - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) + addToVerifyList(result.verifyList, i18n.t('verify~Reporobot has been added!'), true) } else { // If they have a non 200 error, log it so that we can use // devtools to help user debug what went wrong if (response.data.error) { console.log('StatusCode:', response.status, 'Body:', response.data) } - addToList(i18n.t('verify~Reporobot seems not to have access to your fork.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Reporobot seems not to have access to your fork.'), false) } } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) } + + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/merge_tada.js b/lib/verify/merge_tada.js index 3369f333..4dd644f2 100644 --- a/lib/verify/merge_tada.js +++ b/lib/verify/merge_tada.js @@ -2,26 +2,19 @@ const fs = require('fs') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) - -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'merge_tada' -const total = 2 +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') // check that they performed a merge // check there is not username named branch -module.exports = function verifyMergeTadaChallenge (path) { - let counter = 0 +module.exports = function (path) { + const result = getEmptyVerifyResult() let username = '' // path should be a directory if (!fs.lstatSync(path).isDirectory()) { - addToList(i18n.t('verify~Path is not a directory.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Path is not a directory.'), false) + return result } // Get stored username @@ -29,8 +22,8 @@ module.exports = function verifyMergeTadaChallenge (path) { const out = execGit('config user.username') username = out.trim() } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } // Search log for merge activity @@ -38,14 +31,12 @@ module.exports = function verifyMergeTadaChallenge (path) { const out = execGit('reflog -10', { cwd: path }) const ref = out.trim() if (ref.match('merge add-' + username)) { - counter++ - addToList(i18n.t('verify~Your branch has been merged!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Your branch has been merged!'), true) } else { - addToList(i18n.t('verify~No merge of your branch in history.'), false) + addToVerifyList(result.verifyList, i18n.t('verify~No merge of your branch in history.'), false) } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) } // Check if branch still exists @@ -53,20 +44,14 @@ module.exports = function verifyMergeTadaChallenge (path) { const out = execGit('branch', { cwd: path }) const branches = out.trim() if (branches.match('add-' + username)) { - addToList(i18n.t('verify~Uh oh, your branch is still there.'), false) + addToVerifyList(result.verifyList, i18n.t('verify~Uh oh, your branch is still there.'), false) } else { - counter++ - addToList(i18n.t('verify~Branch deleted!'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Branch deleted!'), true) } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) } - if (counter === total) { - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) - } else { - helper.challengeIncomplete() - } + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/pull_never_out_of_date.js b/lib/verify/pull_never_out_of_date.js index 3c590180..fc7b2140 100644 --- a/lib/verify/pull_never_out_of_date.js +++ b/lib/verify/pull_never_out_of_date.js @@ -1,32 +1,27 @@ const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) - -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'pull_never_out_of_date' +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') // do a fetch dry run to see if there is anything // to pull; if there is they haven't pulled yet -module.exports = function verifyPullChallenge (path) { +module.exports = function (path) { + const result = getEmptyVerifyResult() + try { const out = execGit('fetch --dry-run', { cwd: path }) const status = out.trim() if (status === '') { - addToList(i18n.t('verify~Up to date!'), true) - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) + addToVerifyList(result.verifyList, i18n.t('verify~Up to date!'), true) } else { - addToList(i18n.t('verify~There are changes to pull in.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~There are changes to pull in.'), false) } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) } + + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/remote_control.js b/lib/verify/remote_control.js index 99f3748f..b46cc3be 100644 --- a/lib/verify/remote_control.js +++ b/lib/verify/remote_control.js @@ -2,19 +2,15 @@ const fs = require('fs') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted +module.exports = function (path) { + const result = getEmptyVerifyResult() -const currentChallenge = 'remote_control' - -module.exports = function verifyRemoteControlChallenge (path) { // path should be a directory if (!fs.lstatSync(path).isDirectory()) { - addToList(i18n.t('verify~Path is not a directory.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Path is not a directory.'), false) + return result } // Checks for added remote @@ -23,14 +19,14 @@ module.exports = function verifyRemoteControlChallenge (path) { const remotes = out.trim() if (remotes.match('origin')) { - addToList(i18n.t('verify~Found remote set up.'), true) + addToVerifyList(result.verifyList, i18n.t('verify~Found remote set up.'), true) } else { - addToList(i18n.t('verify~Did not find remote origin.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Did not find remote origin.'), false) + return result } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } // check that they've made a push @@ -39,17 +35,16 @@ module.exports = function verifyRemoteControlChallenge (path) { const ref = out.trim() if (ref.match('update by push')) { - addToList(i18n.t('verify~You pushed changes!'), true) - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) + addToVerifyList(result.verifyList, i18n.t('verify~You pushed changes!'), true) } else { - addToList(i18n.t('verify~No evidence of push.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~No evidence of push.'), false) } } catch (err) { // TODO provide nicer error-message and only log the error? console.log(err.message) - addToList(i18n.t('verify~No pushed changes found.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~No pushed changes found.'), false) } + + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/repository.js b/lib/verify/repository.js index feb58230..b47921ac 100644 --- a/lib/verify/repository.js +++ b/lib/verify/repository.js @@ -2,21 +2,15 @@ const fs = require('fs') const path = require('path') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted +module.exports = function (path) { + const result = getEmptyVerifyResult() -// TODO do I want to do this as a var? un-needed, also can't browser view -// pass in the challenge string? -const currentChallenge = 'repository' - -module.exports = function repositoryVerify (path) { // path should be a directory if (!fs.lstatSync(path).isDirectory()) { - addToList(i18n.t('verify~Path is not a directory.'), false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~Path is not a directory.'), false) + return result } try { @@ -24,15 +18,14 @@ module.exports = function repositoryVerify (path) { const status = out.trim() if (status.match('On branch')) { - addToList(i18n.t('verify~This is a Git repository!'), true) - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) + addToVerifyList(result.verifyList, i18n.t('verify~This is a Git repository!'), true) } else { - addToList(i18n.t('verify~This folder is not being tracked by Git.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~This folder is not being tracked by Git.'), false) } } catch (err) { - addToList(i18n.t('verify~This folder is not being tracked by Git.'), false) - helper.challengeIncomplete() + addToVerifyList(result.verifyList, i18n.t('verify~This folder is not being tracked by Git.'), false) } + + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/requesting_you_pull_please.js b/lib/verify/requesting_you_pull_please.js index 2a546e6b..b2be4f1e 100644 --- a/lib/verify/requesting_you_pull_please.js +++ b/lib/verify/requesting_you_pull_please.js @@ -2,19 +2,15 @@ const path = require('path') const axios = require('axios') const i18n = require('electron').remote.getGlobal('i18n') const execGit = require(path.join(__dirname, '../../lib/exec-git.js')) -const helper = require(path.join(__dirname, '../../lib/helpers.js')) -const userData = require(path.join(__dirname, '../../lib/user-data.js')) +const { addToVerifyList, checkChallengeComplete, getEmptyVerifyResult } = require('./verify-helpers') -const addToList = helper.addToList -const markChallengeCompleted = helper.markChallengeCompleted - -const currentChallenge = 'requesting_you_pull_please' const url = 'http://reporobot.jlord.us/pr?username=' // check that they've submitted a pull request // to the original repository jlord/patchwork -module.exports = async function verifyPRChallenge () { +module.exports = async function () { + const result = getEmptyVerifyResult() let username = '' // Get stored username @@ -22,8 +18,8 @@ module.exports = async function verifyPRChallenge () { const out = execGit('config user.username') username = out.trim() } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) + return result } try { @@ -32,18 +28,17 @@ module.exports = async function verifyPRChallenge () { if (response.status === 200) { const pr = response.data.pr if (pr) { - addToList(i18n.t('verify~Found your pull request!'), true) - markChallengeCompleted(currentChallenge) - userData.updateData(currentChallenge) + addToVerifyList(result.verifyList, i18n.t('verify~Found your pull request!'), true) } else { // TODO give user url to their repo also - addToList(i18n.t('verify~No merged pull request found for username {/username/}. If you created a pull request on github, return there to see if reporobot left some comments.', + addToVerifyList(result.verifyList, i18n.t('verify~No merged pull request found for username {/username/}. If you created a pull request on github, return there to see if reporobot left some comments.', { username }), false) - helper.challengeIncomplete() } } } catch (err) { - addToList('Error: ' + err.message, false) - return helper.challengeIncomplete() + addToVerifyList(result.verifyList, 'Error: ' + err.message, false) } + + result.challengeComplete = checkChallengeComplete(result.verifyList) + return result } diff --git a/lib/verify/verify-helpers.js b/lib/verify/verify-helpers.js new file mode 100644 index 00000000..48c37d70 --- /dev/null +++ b/lib/verify/verify-helpers.js @@ -0,0 +1,41 @@ +/* + * Helpers for challenge-specific verification-scripts + */ + +// Set each challenge verifying process to use +// English language pack +// Remained from old code... Don't know why to set this. REMOVE, if no complications appear. +// process.env.LANG = 'C' + +/* + * Add a formatted object to given verifyList. + */ +function addToVerifyList (list, message, status) { + list.push({ + pass: status, + message + }) +} + +/* + * Checks if a challenge is complete, by reducing the pass-states of verifyList + */ +function checkChallengeComplete (verifyList) { + return verifyList.reduce((complete, listItem) => { + return complete && listItem.pass + }, true) +} + +/* + * Returns an empty verify-Result template. + */ +function getEmptyVerifyResult () { + return { + challengeComplete: false, + verifyList: [] + } +} + +exports.addToVerifyList = addToVerifyList +exports.checkChallengeComplete = checkChallengeComplete +exports.getEmptyVerifyResult = getEmptyVerifyResult diff --git a/main.js b/main.js index c0677f92..7e5fb8b3 100644 --- a/main.js +++ b/main.js @@ -1,170 +1,173 @@ -var fs = require('fs') -var path = require('path') - -var electron = require('electron') -var app = electron.app -var BrowserWindow = electron.BrowserWindow -var Menu = electron.Menu -var ipcMain = electron.ipcMain -var dialog = electron.dialog -const { i18nInit } = require('./lib/i18nInit.js') +const fs = require('fs') +const path = require('path') -var darwinTemplate = require('./menus/darwin-menu.js') -var otherTemplate = require('./menus/other-menu.js') +const electron = require('electron') +const app = electron.app +const BrowserWindow = electron.BrowserWindow +const Menu = electron.Menu +const ipcMain = electron.ipcMain +const dialog = electron.dialog -var emptyData = require('./empty-data.json') -var emptySavedDir = require('./empty-saved-dir.json') +const { i18nInit } = require('./lib/i18nInit.js') +const darwinTemplate = require('./menus/darwin-menu.js') +const otherTemplate = require('./menus/other-menu.js') -var mainWindow = null -var menu = null +const emptyUserData = require('./empty-user-data.json') +const emptySavedDir = require('./empty-saved-dir.json') +const GititIcon = path.join(__dirname, '/assets/git-it.png') -var iconPath = path.join(__dirname, '/assets/git-it.png') +const userDataPath = app.getPath('userData') +const userDataFile = path.join(userDataPath, 'user-data.json') +const savedDirFile = path.join(userDataPath, 'saved-dir.json') -app.on('window-all-closed', function appQuit () { - if (process.platform !== 'darwin') { - app.quit() - } -}) +let mainWindow = null -app.on('ready', function appReady () { +app.on('ready', () => { + // Create main-window and set listener to remove reference after it got closed mainWindow = new BrowserWindow({ minWidth: 800, minHeight: 600, width: 980, height: 760, title: 'Git-it', - icon: iconPath, + icon: GititIcon, webPreferences: { nodeIntegration: true, enableRemoteModule: true } }) + mainWindow.on('closed', function winClosed () { + mainWindow = null + }) - // Debug setup - if (process.env.NODE_ENV === 'debug') { - mainWindow.maximize() - mainWindow.webContents.openDevTools() + // Init App-Components + initUserData() + i18nInit(buildMenus, mainWindow) + buildMenus() + + // Debugging-Settings if required + checkDebugSettings() + + // Open Index-page + mainWindow.loadFile(path.normalize(path.join(__dirname, 'built', 'pages', 'index.html'))) +}) + +/* + * Quit app + */ +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() } +}) + +/* + * Provide userDataPath to Renderer (resp. to lib/user-data.js) + */ +ipcMain.on('getUserDataPath', event => { + event.returnValue = userDataPath +}) - // Init i18next Module - i18nInit() - - var appPath = app.getPath('userData') - var userDataPath = path.join(appPath, 'user-data.json') - var userSavedDir = path.join(appPath, 'saved-dir.json') - // tools for development to prefill challenge completion - // usage: electron . --none - // electron . --some - // electron . --all - if (process.argv[2] === '--none') { - setAllChallengesUncomplete(userDataPath) +/* + * Select-Directory Window if called + */ +ipcMain.on('dialog-selectDir', event => { + const path = dialog.showOpenDialogSync(mainWindow, { properties: ['openDirectory'] }) + if (path) { + event.sender.send('confirm-selectDir', path[0]) } - if (process.argv[2] === '--some') { - setSomeChallengesComplete(userDataPath) +}) + +/* + * ClearAll Dialog + */ +ipcMain.on('dialog-clearAll', event => { + const dialogOptions = { + type: 'info', + title: global.i18n.t('Confirm Clearing Statuses'), + message: global.i18n.t('Are you sure you want to clear the status for every challenge?'), + buttons: [global.i18n.t('Yes'), global.i18n.t('No')], + defaultId: 0, + cancelId: 1 } - if (process.argv[2] === '--all') { - setAllChallengesComplete(userDataPath) + + const resp = dialog.showMessageBoxSync(dialogOptions) + if (resp === 0) { + event.sender.send('confirm-clearAll') } +}) - // Create 'user-data.json', if not existing. - fs.access(userDataPath, (err) => { +/* + * Create 'user-data.json' and 'saved-dir.json', if not existing. + */ +function initUserData () { + fs.access(userDataFile, (err) => { if (err) { - fs.writeFile(userDataPath, JSON.stringify(emptyData, null, ' '), (err) => { + fs.writeFile(userDataFile, JSON.stringify(emptyUserData, null, 2), (err) => { if (err) return console.log(err) }) } }) - - // Create 'saved-dir.json', if not existing. - fs.access(userSavedDir, (err) => { + fs.access(savedDirFile, (err) => { if (err) { - fs.writeFile(userSavedDir, JSON.stringify(emptySavedDir, null, ' '), (err) => { + fs.writeFile(savedDirFile, JSON.stringify(emptySavedDir, null, 2), (err) => { if (err) return console.log(err) }) } }) +} - buildMenus() - mainWindow.loadFile(path.normalize(path.join(__dirname, 'built', 'pages', 'index.html'))) - - /* - * Create ipc listeners - */ - ipcMain.on('getUserDataPath', function (event) { - event.returnValue = userDataPath - }) - - ipcMain.on('getUserSavedDir', function (event) { - event.returnValue = userSavedDir - }) - - ipcMain.on('open-file-dialog', function (event) { - var files = dialog.showOpenDialogSync(mainWindow, { properties: ['openFile', 'openDirectory'] }) - if (files) { - event.sender.send('selected-directory', files) - } - }) - - ipcMain.on('confirm-clear', function (event) { - var options = { - type: 'info', - buttons: [global.i18n.t('Yes'), global.i18n.t('No')], - title: global.i18n.t('Confirm Clearing Statuses'), - message: global.i18n.t('Are you sure you want to clear the status for every challenge?') - } - // TODO Change to promise-based showMessageBox (w/o sync) - const resp = dialog.showMessageBoxSync(options) - event.sender.send('confirm-clear-response', resp) - }) - - /* - * Create i18n Listener - */ - global.i18n.on('languageChanged', () => { - buildMenus() - mainWindow.reload() - }) - - /* - * MainWindow Listener - */ - mainWindow.on('closed', function winClosed () { - mainWindow = null - }) -}) - +/* + * Build the menu from template and set it on application. + */ function buildMenus () { if (process.platform === 'darwin') { - menu = Menu.buildFromTemplate(darwinTemplate(app, mainWindow, global.i18n)) + const menu = Menu.buildFromTemplate(darwinTemplate(mainWindow, global.i18n)) Menu.setApplicationMenu(menu) } else { - menu = Menu.buildFromTemplate(otherTemplate(app, mainWindow, global.i18n)) + const menu = Menu.buildFromTemplate(otherTemplate(mainWindow, global.i18n)) mainWindow.setMenu(menu) } } -function setAllChallengesComplete (path) { - var challenges = JSON.parse(fs.readFileSync(path)) - for (var key in challenges) { - challenges[key].completed = true - } - fs.writeFileSync(path, JSON.stringify(challenges), '', null) -} +/* + * Tools for development + * - Preset challenge completion + * usage: electron . --none + * electron . --some + * electron . --all + * - Show devtools in maximized Window + * usage: NODE_ENV=debug + */ +function checkDebugSettings () { + const debugArguments = ['--none', '--some', '--all'] + + // Preset challenges + if (process.argv[2] && debugArguments.includes(process.argv[2])) { + const userData = JSON.parse(fs.readFileSync(userDataFile)) + + if (process.argv[2] === '--none') { + Object.keys(userData).forEach(challenge => { + userData[challenge].completed = false + }) + } + if (process.argv[2] === '--some') { + Object.keys(userData).forEach((challenge, index) => { + userData[challenge].completed = index < 6 + }) + } + if (process.argv[2] === '--all') { + Object.keys(userData).forEach(challenge => { + userData[challenge].completed = true + }) + } -function setAllChallengesUncomplete (path) { - var challenges = JSON.parse(fs.readFileSync(path)) - for (var key in challenges) { - challenges[key].completed = false + fs.writeFileSync(userDataFile, JSON.stringify(userData, null, 2)) } - fs.writeFileSync(path, JSON.stringify(challenges), '', null) -} -function setSomeChallengesComplete (path) { - var counter = 0 - var challenges = JSON.parse(fs.readFileSync(path)) - for (var key in challenges) { - counter++ - challenges[key].completed = counter < 6 + // Show devtools + if (process.env.NODE_ENV === 'debug') { + mainWindow.maximize() + mainWindow.webContents.openDevTools() } - fs.writeFileSync(path, JSON.stringify(challenges), '', null) } diff --git a/menus/darwin-menu.js b/menus/darwin-menu.js index 4c1cc5db..c4bfdfae 100644 --- a/menus/darwin-menu.js +++ b/menus/darwin-menu.js @@ -1,6 +1,6 @@ const path = require('path') -module.exports = function menu (app, mainWindow, i18n) { +module.exports = function (mainWindow, i18n) { const darwinMenu = [ { label: 'Git-it', diff --git a/menus/other-menu.js b/menus/other-menu.js index eae4260f..5a616b33 100644 --- a/menus/other-menu.js +++ b/menus/other-menu.js @@ -1,6 +1,6 @@ const path = require('path') -module.exports = function menu (app, mainWindow, i18n) { +module.exports = function (mainWindow, i18n) { const otherMenu = [ { label: i18n.t('menu~&File'), diff --git a/package.json b/package.json index 8e6f332c..5abe9f8c 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,12 @@ "start": "electron .", "debug": "npm run build && env NODE_ENV=debug electron .", "debug:none": "npm run build && env NODE_ENV=debug electron . --none", + "debug:some": "npm run build && env NODE_ENV=debug electron . --some", + "debug:all": "npm run build && env NODE_ENV=debug electron . --all", "lint": "standard lib/*.js lib/verify/*.js menus/*.js main.js", "lint:fix": "standard --fix lib/*.js lib/verify/*.js menus/*.js main.js", "i18n:extract": "i18next -c ./config/i18next-parser.config.js", - "build": "rimraf built/* && node lib/build-challenges.js && node lib/build-pages.js", + "build": "rimraf built/* && node lib/build/build-challenges.js && node lib/build/build-pages.js", "pack": "npm run build && npm run pack-mac && npm run pack-lin && npm run pack-win", "pack-mac": "electron-packager . Git-it --platform=darwin --arch=x64 --icon=assets/git-it.icns --overwrite --out=out --extraResource=resources/i18n/ --ignore=.github/ --ignore=resources/ --ignore=assets/PortableGit", "pack-lin": "electron-packager . Git-it --platform=linux --arch=x64 --icon=assets/git-it.png --overwrite --out=out --extraResource=resources/i18n/ --ignore=.github/ --ignore=resources/ --ignore=assets/PortableGit", diff --git a/resources/content/pages/index.html b/resources/content/pages/index.html index e9695e8c..c824e8cd 100644 --- a/resources/content/pages/index.html +++ b/resources/content/pages/index.html @@ -7,7 +7,7 @@ - +