diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 11:18:50 +0000 |
commit | 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch) | |
tree | a77e7fe7a93de11213032ed4ab1f33a3db51b738 /app/assets/javascripts/ide/stores | |
parent | 00b35af3db1abfe813a778f643dad221aad51fca (diff) | |
download | gitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz |
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'app/assets/javascripts/ide/stores')
46 files changed, 781 insertions, 138 deletions
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index e32b5ac7bdc..c881f1221e5 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -7,7 +7,6 @@ import * as types from './mutation_types'; import { decorateFiles } from '../lib/files'; import { stageKeys } from '../constants'; import service from '../services'; -import router from '../ide_router'; import eventHub from '../eventhub'; export const redirectToUrl = (self, url) => visitUrl(url); @@ -20,21 +19,25 @@ export const discardAllChanges = ({ state, commit, dispatch }) => { commit(types.REMOVE_ALL_CHANGES_FILES); }; -export const closeAllFiles = ({ state, dispatch }) => { - state.openFiles.forEach(file => dispatch('closeFile', file)); -}; - export const setResizingStatus = ({ commit }, resizing) => { commit(types.SET_RESIZING_STATUS, resizing); }; export const createTempEntry = ( { state, commit, dispatch, getters }, - { name, type, content = '', base64 = false, binary = false, rawPath = '' }, + { + name, + type, + content = '', + binary = false, + rawPath = '', + openFile = true, + makeFileActive = true, + }, ) => { const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name; - if (state.entries[name] && !state.entries[name].deleted) { + if (getters.entryExists(name)) { flash( sprintf(__('The name "%{name}" is already taken in this directory.'), { name: name.split('/').pop(), @@ -46,7 +49,7 @@ export const createTempEntry = ( true, ); - return; + return undefined; } const data = decorateFiles({ @@ -56,7 +59,6 @@ export const createTempEntry = ( type, tempFile: true, content, - base64, binary, rawPath, }); @@ -69,18 +71,31 @@ export const createTempEntry = ( }); if (type === 'blob') { - commit(types.TOGGLE_FILE_OPEN, file.path); + if (openFile) commit(types.TOGGLE_FILE_OPEN, file.path); commit(types.STAGE_CHANGE, { path: file.path, diffInfo: getters.getDiffInfo(file.path) }); - dispatch('setFileActive', file.path); + if (openFile && makeFileActive) dispatch('setFileActive', file.path); dispatch('triggerFilesChange'); } if (parentPath && !state.entries[parentPath].opened) { commit(types.TOGGLE_TREE_OPEN, parentPath); } + + return file; }; +export const addTempImage = ({ dispatch, getters }, { name, rawPath = '' }) => + dispatch('createTempEntry', { + name: getters.getAvailableFileName(name), + type: 'blob', + content: rawPath.split('base64,')[1], + binary: true, + rawPath, + openFile: false, + makeFileActive: false, + }); + export const scrollToTab = () => { Vue.nextTick(() => { const tabs = document.getElementById('tabs'); @@ -239,7 +254,7 @@ export const renameEntry = ({ dispatch, commit, state, getters }, { path, name, } if (newEntry.opened) { - router.push(`/project${newEntry.url}`); + dispatch('router/push', `/project${newEntry.url}`, { root: true }); } } @@ -297,6 +312,3 @@ export * from './actions/tree'; export * from './actions/file'; export * from './actions/project'; export * from './actions/merge_request'; - -// prevent babel-plugin-rewire from generating an invalid default during karma tests -export default () => {}; diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index da7d4a44bde..47f9337a288 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -3,8 +3,7 @@ import { __ } from '~/locale'; import eventHub from '../../eventhub'; import service from '../../services'; import * as types from '../mutation_types'; -import router from '../../ide_router'; -import { addFinalNewlineIfNeeded, setPageTitleForFile } from '../utils'; +import { setPageTitleForFile } from '../utils'; import { viewerTypes, stageKeys } from '../../constants'; export const closeFile = ({ commit, state, dispatch }, file) => { @@ -30,10 +29,10 @@ export const closeFile = ({ commit, state, dispatch }, file) => { keyPrefix: nextFileToOpen.staged ? 'staged' : 'unstaged', }); } else { - router.push(`/project${nextFileToOpen.url}`); + dispatch('router/push', `/project${nextFileToOpen.url}`, { root: true }); } } else if (!state.openFiles.length) { - router.push(`/project/${file.projectId}/tree/${file.branchId}/`); + dispatch('router/push', `/project/${file.projectId}/tree/${file.branchId}/`, { root: true }); } eventHub.$emit(`editor.update.model.dispose.${file.key}`); @@ -152,7 +151,7 @@ export const changeFileContent = ({ commit, state, getters }, { path, content }) const file = state.entries[path]; commit(types.UPDATE_FILE_CONTENT, { path, - content: addFinalNewlineIfNeeded(content), + content, }); const indexOfChangedFile = state.changedFiles.findIndex(f => f.path === path); @@ -170,12 +169,6 @@ export const setFileLanguage = ({ getters, commit }, { fileLanguage }) => { } }; -export const setFileEOL = ({ getters, commit }, { eol }) => { - if (getters.activeFile) { - commit(types.SET_FILE_EOL, { file: getters.activeFile, eol }); - } -}; - export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn }) => { if (getters.activeFile) { commit(types.SET_FILE_POSITION, { @@ -226,7 +219,7 @@ export const discardFileChanges = ({ dispatch, state, commit, getters }, path) = if (!isDestructiveDiscard && file.path === getters.activeFile?.path) { dispatch('updateDelayViewerUpdated', true) .then(() => { - router.push(`/project${file.url}`); + dispatch('router/push', `/project${file.url}`, { root: true }); }) .catch(e => { throw e; @@ -275,14 +268,16 @@ export const unstageChange = ({ commit, dispatch, getters }, path) => { } }; -export const openPendingTab = ({ commit, getters, state }, { file, keyPrefix }) => { +export const openPendingTab = ({ commit, dispatch, getters, state }, { file, keyPrefix }) => { if (getters.activeFile && getters.activeFile.key === `${keyPrefix}-${file.key}`) return false; state.openFiles.forEach(f => eventHub.$emit(`editor.update.model.dispose.${f.key}`)); commit(types.ADD_PENDING_TAB, { file, keyPrefix }); - router.push(`/project/${file.projectId}/tree/${state.currentBranchId}/`); + dispatch('router/push', `/project/${file.projectId}/tree/${state.currentBranchId}/`, { + root: true, + }); return true; }; diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js index 6c8fb9f90aa..d172bb31ae5 100644 --- a/app/assets/javascripts/ide/stores/actions/project.js +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -4,7 +4,6 @@ import { __, sprintf } from '~/locale'; import service from '../../services'; import api from '../../../api'; import * as types from '../mutation_types'; -import router from '../../ide_router'; export const getProjectData = ({ commit, state }, { namespace, projectId, force = false } = {}) => new Promise((resolve, reject) => { @@ -57,7 +56,7 @@ export const createNewBranchFromDefault = ({ state, dispatch, getters }, branch) }) .then(() => { dispatch('setErrorMessage', null); - router.push(`${router.currentRoute.path}?${Date.now()}`); + window.location.reload(); }) .catch(() => { dispatch('setErrorMessage', { diff --git a/app/assets/javascripts/ide/stores/extend.js b/app/assets/javascripts/ide/stores/extend.js new file mode 100644 index 00000000000..1c1636cf6ca --- /dev/null +++ b/app/assets/javascripts/ide/stores/extend.js @@ -0,0 +1,14 @@ +import terminal from './plugins/terminal'; +import terminalSync from './plugins/terminal_sync'; + +const plugins = () => [ + terminal, + ...(gon.features && gon.features.buildServiceProxy ? [terminalSync] : []), +]; + +export default (store, el) => { + // plugins is actually an array of plugin factories, so we have to create first then call + plugins().forEach(plugin => plugin(el)(store)); + + return store; +}; diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js index 5d0a8570906..53734fa626b 100644 --- a/app/assets/javascripts/ide/stores/getters.js +++ b/app/assets/javascripts/ide/stores/getters.js @@ -50,9 +50,6 @@ export const emptyRepo = state => export const currentTree = state => state.trees[`${state.currentProjectId}/${state.currentBranchId}`]; -export const hasChanges = state => - Boolean(state.changedFiles.length) || Boolean(state.stagedFiles.length); - export const hasMergeRequest = state => Boolean(state.currentMergeRequestId); export const allBlobs = state => @@ -162,5 +159,18 @@ export const canCreateMergeRequests = (state, getters) => export const canPushCode = (state, getters) => Boolean(getters.findProjectPermissions(state.currentProjectId)[PERMISSION_PUSH_CODE]); -// prevent babel-plugin-rewire from generating an invalid default during karma tests -export default () => {}; +export const entryExists = state => path => + Boolean(state.entries[path] && !state.entries[path].deleted); + +export const getAvailableFileName = (state, getters) => path => { + let newPath = path; + + while (getters.entryExists(newPath)) { + newPath = newPath.replace( + /([ _-]?)(\d*)(\..+?$|$)/, + (_, before, number, after) => `${before || '_'}${Number(number) + 1}${after}`, + ); + } + + return newPath; +}; diff --git a/app/assets/javascripts/ide/stores/index.js b/app/assets/javascripts/ide/stores/index.js index 85550578e94..18c466cc93d 100644 --- a/app/assets/javascripts/ide/stores/index.js +++ b/app/assets/javascripts/ide/stores/index.js @@ -11,24 +11,27 @@ import branches from './modules/branches'; import fileTemplates from './modules/file_templates'; import paneModule from './modules/pane'; import clientsideModule from './modules/clientside'; +import routerModule from './modules/router'; Vue.use(Vuex); -export const createStore = () => - new Vuex.Store({ - state: state(), - actions, - mutations, - getters, - modules: { - commit: commitModule, - pipelines, - mergeRequests, - branches, - fileTemplates: fileTemplates(), - rightPane: paneModule(), - clientside: clientsideModule(), - }, - }); +export const createStoreOptions = () => ({ + state: state(), + actions, + mutations, + getters, + modules: { + commit: commitModule, + pipelines, + mergeRequests, + branches, + fileTemplates: fileTemplates(), + rightPane: paneModule(), + clientside: clientsideModule(), + router: routerModule, + }, +}); + +export const createStore = () => new Vuex.Store(createStoreOptions()); export default createStore(); diff --git a/app/assets/javascripts/ide/stores/modules/branches/index.js b/app/assets/javascripts/ide/stores/modules/branches/index.js index 04e7e0f08f1..deda95cd0c9 100644 --- a/app/assets/javascripts/ide/stores/modules/branches/index.js +++ b/app/assets/javascripts/ide/stores/modules/branches/index.js @@ -4,7 +4,7 @@ import mutations from './mutations'; export default { namespaced: true, - state: state(), + state, actions, mutations, }; diff --git a/app/assets/javascripts/ide/stores/modules/clientside/actions.js b/app/assets/javascripts/ide/stores/modules/clientside/actions.js index eb3bcdff2ae..2bebf8b90ce 100644 --- a/app/assets/javascripts/ide/stores/modules/clientside/actions.js +++ b/app/assets/javascripts/ide/stores/modules/clientside/actions.js @@ -8,5 +8,4 @@ export const pingUsage = ({ rootGetters }) => { return axios.post(url); }; -// prevent babel-plugin-rewire from generating an invalid default during karma tests -export default () => {}; +export default pingUsage; diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index 592c7e15918..005bd0240e2 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -3,7 +3,6 @@ import flash from '~/flash'; import httpStatusCodes from '~/lib/utils/http_status'; import * as rootTypes from '../../mutation_types'; import { createCommitPayload, createNewMergeRequestUrl } from '../../utils'; -import router from '../../../ide_router'; import service from '../../../services'; import * as types from './mutation_types'; import consts from './constants'; @@ -196,8 +195,10 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo dispatch('updateViewer', 'editor', { root: true }); if (rootGetters.activeFile) { - router.push( + dispatch( + 'router/push', `/project/${rootState.currentProjectId}/blob/${branchName}/-/${rootGetters.activeFile.path}`, + { root: true }, ); } } @@ -234,6 +235,3 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo window.dispatchEvent(new Event('resize')); }); }; - -// prevent babel-plugin-rewire from generating an invalid default during karma tests -export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js index 413c4b0110d..37f887bcf0a 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/getters.js +++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js @@ -59,6 +59,3 @@ export const shouldDisableNewMrOption = (state, getters, rootState, rootGetters) export const shouldCreateMR = (state, getters) => state.shouldCreateMR && !getters.shouldDisableNewMrOption; - -// prevent babel-plugin-rewire from generating an invalid default during karma tests -export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/index.js b/app/assets/javascripts/ide/stores/modules/commit/index.js index 3bf65b02847..5cec73bde2e 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/index.js +++ b/app/assets/javascripts/ide/stores/modules/commit/index.js @@ -5,7 +5,7 @@ import * as getters from './getters'; export default { namespaced: true, - state: state(), + state, mutations, actions, getters, diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js index 59ead8a3dcf..6b2c929cd44 100644 --- a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js +++ b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js @@ -117,6 +117,3 @@ export const undoFileTemplate = ({ dispatch, commit, rootGetters }) => { dispatch('discardFileChanges', file.path, { root: true }); } }; - -// prevent babel-plugin-rewire from generating an invalid default during karma tests -export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/pane/actions.js b/app/assets/javascripts/ide/stores/modules/pane/actions.js index a8fcdf539ec..b7cff368fe4 100644 --- a/app/assets/javascripts/ide/stores/modules/pane/actions.js +++ b/app/assets/javascripts/ide/stores/modules/pane/actions.js @@ -25,6 +25,3 @@ export const open = ({ state, commit }, view) => { export const close = ({ commit }) => { commit(types.SET_OPEN, false); }; - -// prevent babel-plugin-rewire from generating an invalid default during karma tests -export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/pane/getters.js b/app/assets/javascripts/ide/stores/modules/pane/getters.js index c346cf13689..7816172bb6f 100644 --- a/app/assets/javascripts/ide/stores/modules/pane/getters.js +++ b/app/assets/javascripts/ide/stores/modules/pane/getters.js @@ -1,4 +1,3 @@ -export const isActiveView = state => view => state.currentView === view; - -export const isAliveView = (state, getters) => view => - state.keepAliveViews[view] || (state.isOpen && getters.isActiveView(view)); +// eslint-disable-next-line import/prefer-default-export +export const isAliveView = state => view => + state.keepAliveViews[view] || (state.isOpen && state.currentView === view); diff --git a/app/assets/javascripts/ide/stores/modules/router/actions.js b/app/assets/javascripts/ide/stores/modules/router/actions.js new file mode 100644 index 00000000000..849067599f2 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/router/actions.js @@ -0,0 +1,6 @@ +import * as types from './mutation_types'; + +// eslint-disable-next-line import/prefer-default-export +export const push = ({ commit }, fullPath) => { + commit(types.PUSH, fullPath); +}; diff --git a/app/assets/javascripts/ide/stores/modules/router/index.js b/app/assets/javascripts/ide/stores/modules/router/index.js new file mode 100644 index 00000000000..68c81bb4509 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/router/index.js @@ -0,0 +1,10 @@ +import state from './state'; +import mutations from './mutations'; +import * as actions from './actions'; + +export default { + namespaced: true, + state, + mutations, + actions, +}; diff --git a/app/assets/javascripts/ide/stores/modules/router/mutation_types.js b/app/assets/javascripts/ide/stores/modules/router/mutation_types.js new file mode 100644 index 00000000000..ae99073cc4c --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/router/mutation_types.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export const PUSH = 'PUSH'; diff --git a/app/assets/javascripts/ide/stores/modules/router/mutations.js b/app/assets/javascripts/ide/stores/modules/router/mutations.js new file mode 100644 index 00000000000..471cace314c --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/router/mutations.js @@ -0,0 +1,7 @@ +import * as types from './mutation_types'; + +export default { + [types.PUSH](state, fullPath) { + state.fullPath = fullPath; + }, +}; diff --git a/app/assets/javascripts/ide/stores/modules/router/state.js b/app/assets/javascripts/ide/stores/modules/router/state.js new file mode 100644 index 00000000000..abb6c5239e4 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/router/state.js @@ -0,0 +1,3 @@ +export default () => ({ + fullPath: '', +}); diff --git a/app/assets/javascripts/ide/stores/modules/terminal/actions/checks.js b/app/assets/javascripts/ide/stores/modules/terminal/actions/checks.js new file mode 100644 index 00000000000..43b6650b241 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/actions/checks.js @@ -0,0 +1,98 @@ +import Api from '~/api'; +import httpStatus from '~/lib/utils/http_status'; +import * as types from '../mutation_types'; +import * as messages from '../messages'; +import { CHECK_CONFIG, CHECK_RUNNERS, RETRY_RUNNERS_INTERVAL } from '../constants'; +import * as terminalService from '../../../../services/terminals'; + +export const requestConfigCheck = ({ commit }) => { + commit(types.REQUEST_CHECK, CHECK_CONFIG); +}; + +export const receiveConfigCheckSuccess = ({ commit }) => { + commit(types.SET_VISIBLE, true); + commit(types.RECEIVE_CHECK_SUCCESS, CHECK_CONFIG); +}; + +export const receiveConfigCheckError = ({ commit, state }, e) => { + const { status } = e.response; + const { paths } = state; + + const isVisible = status !== httpStatus.FORBIDDEN && status !== httpStatus.NOT_FOUND; + commit(types.SET_VISIBLE, isVisible); + + const message = messages.configCheckError(status, paths.webTerminalConfigHelpPath); + commit(types.RECEIVE_CHECK_ERROR, { type: CHECK_CONFIG, message }); +}; + +export const fetchConfigCheck = ({ dispatch, rootState, rootGetters }) => { + dispatch('requestConfigCheck'); + + const { currentBranchId } = rootState; + const { currentProject } = rootGetters; + + terminalService + .checkConfig(currentProject.path_with_namespace, currentBranchId) + .then(() => { + dispatch('receiveConfigCheckSuccess'); + }) + .catch(e => { + dispatch('receiveConfigCheckError', e); + }); +}; + +export const requestRunnersCheck = ({ commit }) => { + commit(types.REQUEST_CHECK, CHECK_RUNNERS); +}; + +export const receiveRunnersCheckSuccess = ({ commit, dispatch, state }, data) => { + if (data.length) { + commit(types.RECEIVE_CHECK_SUCCESS, CHECK_RUNNERS); + } else { + const { paths } = state; + + commit(types.RECEIVE_CHECK_ERROR, { + type: CHECK_RUNNERS, + message: messages.runnersCheckEmpty(paths.webTerminalRunnersHelpPath), + }); + + dispatch('retryRunnersCheck'); + } +}; + +export const receiveRunnersCheckError = ({ commit }) => { + commit(types.RECEIVE_CHECK_ERROR, { + type: CHECK_RUNNERS, + message: messages.UNEXPECTED_ERROR_RUNNERS, + }); +}; + +export const retryRunnersCheck = ({ dispatch, state }) => { + // if the overall check has failed, don't worry about retrying + const check = state.checks[CHECK_CONFIG]; + if (!check.isLoading && !check.isValid) { + return; + } + + setTimeout(() => { + dispatch('fetchRunnersCheck', { background: true }); + }, RETRY_RUNNERS_INTERVAL); +}; + +export const fetchRunnersCheck = ({ dispatch, rootGetters }, options = {}) => { + const { background = false } = options; + + if (!background) { + dispatch('requestRunnersCheck'); + } + + const { currentProject } = rootGetters; + + Api.projectRunners(currentProject.id, { params: { scope: 'active' } }) + .then(({ data }) => { + dispatch('receiveRunnersCheckSuccess', data); + }) + .catch(e => { + dispatch('receiveRunnersCheckError', e); + }); +}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/actions/index.js b/app/assets/javascripts/ide/stores/modules/terminal/actions/index.js new file mode 100644 index 00000000000..112b3794114 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/actions/index.js @@ -0,0 +1,5 @@ +export * from './setup'; +export * from './checks'; +export * from './session_controls'; +export * from './session_status'; +export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js b/app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js new file mode 100644 index 00000000000..d3dcb9dd125 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js @@ -0,0 +1,118 @@ +import axios from '~/lib/utils/axios_utils'; +import httpStatus from '~/lib/utils/http_status'; +import flash from '~/flash'; +import * as types from '../mutation_types'; +import * as messages from '../messages'; +import * as terminalService from '../../../../services/terminals'; +import { STARTING, STOPPING, STOPPED } from '../constants'; + +export const requestStartSession = ({ commit }) => { + commit(types.SET_SESSION_STATUS, STARTING); +}; + +export const receiveStartSessionSuccess = ({ commit, dispatch }, data) => { + commit(types.SET_SESSION, { + id: data.id, + status: data.status, + showPath: data.show_path, + cancelPath: data.cancel_path, + retryPath: data.retry_path, + terminalPath: data.terminal_path, + proxyWebsocketPath: data.proxy_websocket_path, + services: data.services, + }); + + dispatch('pollSessionStatus'); +}; + +export const receiveStartSessionError = ({ dispatch }) => { + flash(messages.UNEXPECTED_ERROR_STARTING); + dispatch('killSession'); +}; + +export const startSession = ({ state, dispatch, rootGetters, rootState }) => { + if (state.session && state.session.status === STARTING) { + return; + } + + const { currentProject } = rootGetters; + const { currentBranchId } = rootState; + + dispatch('requestStartSession'); + + terminalService + .create(currentProject.path_with_namespace, currentBranchId) + .then(({ data }) => { + dispatch('receiveStartSessionSuccess', data); + }) + .catch(error => { + dispatch('receiveStartSessionError', error); + }); +}; + +export const requestStopSession = ({ commit }) => { + commit(types.SET_SESSION_STATUS, STOPPING); +}; + +export const receiveStopSessionSuccess = ({ dispatch }) => { + dispatch('killSession'); +}; + +export const receiveStopSessionError = ({ dispatch }) => { + flash(messages.UNEXPECTED_ERROR_STOPPING); + dispatch('killSession'); +}; + +export const stopSession = ({ state, dispatch }) => { + const { cancelPath } = state.session; + + dispatch('requestStopSession'); + + axios + .post(cancelPath) + .then(() => { + dispatch('receiveStopSessionSuccess'); + }) + .catch(err => { + dispatch('receiveStopSessionError', err); + }); +}; + +export const killSession = ({ commit, dispatch }) => { + dispatch('stopPollingSessionStatus'); + commit(types.SET_SESSION_STATUS, STOPPED); +}; + +export const restartSession = ({ state, dispatch, rootState }) => { + const { status, retryPath } = state.session; + const { currentBranchId } = rootState; + + if (status !== STOPPED) { + return; + } + + if (!retryPath) { + dispatch('startSession'); + return; + } + + dispatch('requestStartSession'); + + axios + .post(retryPath, { branch: currentBranchId, format: 'json' }) + .then(({ data }) => { + dispatch('receiveStartSessionSuccess', data); + }) + .catch(error => { + const responseStatus = error.response && error.response.status; + // We may have removed the build, in this case we'll just create a new session + if ( + responseStatus === httpStatus.NOT_FOUND || + responseStatus === httpStatus.UNPROCESSABLE_ENTITY + ) { + dispatch('startSession'); + } else { + dispatch('receiveStartSessionError', error); + } + }); +}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/actions/session_status.js b/app/assets/javascripts/ide/stores/modules/terminal/actions/session_status.js new file mode 100644 index 00000000000..59ba1605c47 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/actions/session_status.js @@ -0,0 +1,64 @@ +import axios from '~/lib/utils/axios_utils'; +import flash from '~/flash'; +import * as types from '../mutation_types'; +import * as messages from '../messages'; +import { isEndingStatus } from '../utils'; + +export const pollSessionStatus = ({ state, dispatch, commit }) => { + dispatch('stopPollingSessionStatus'); + dispatch('fetchSessionStatus'); + + const interval = setInterval(() => { + if (!state.session) { + dispatch('stopPollingSessionStatus'); + } else { + dispatch('fetchSessionStatus'); + } + }, 5000); + + commit(types.SET_SESSION_STATUS_INTERVAL, interval); +}; + +export const stopPollingSessionStatus = ({ state, commit }) => { + const { sessionStatusInterval } = state; + + if (!sessionStatusInterval) { + return; + } + + clearInterval(sessionStatusInterval); + + commit(types.SET_SESSION_STATUS_INTERVAL, 0); +}; + +export const receiveSessionStatusSuccess = ({ commit, dispatch }, data) => { + const status = data && data.status; + + commit(types.SET_SESSION_STATUS, status); + + if (isEndingStatus(status)) { + dispatch('killSession'); + } +}; + +export const receiveSessionStatusError = ({ dispatch }) => { + flash(messages.UNEXPECTED_ERROR_STATUS); + dispatch('killSession'); +}; + +export const fetchSessionStatus = ({ dispatch, state }) => { + if (!state.session) { + return; + } + + const { showPath } = state.session; + + axios + .get(showPath) + .then(({ data }) => { + dispatch('receiveSessionStatusSuccess', data); + }) + .catch(error => { + dispatch('receiveSessionStatusError', error); + }); +}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/actions/setup.js b/app/assets/javascripts/ide/stores/modules/terminal/actions/setup.js new file mode 100644 index 00000000000..78ad94f8a91 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/actions/setup.js @@ -0,0 +1,14 @@ +import * as types from '../mutation_types'; + +export const init = ({ dispatch }) => { + dispatch('fetchConfigCheck'); + dispatch('fetchRunnersCheck'); +}; + +export const hideSplash = ({ commit }) => { + commit(types.HIDE_SPLASH); +}; + +export const setPaths = ({ commit }, paths) => { + commit(types.SET_PATHS, paths); +}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/constants.js b/app/assets/javascripts/ide/stores/modules/terminal/constants.js new file mode 100644 index 00000000000..f7ae9d8f4ea --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/constants.js @@ -0,0 +1,9 @@ +export const CHECK_CONFIG = 'config'; +export const CHECK_RUNNERS = 'runners'; +export const RETRY_RUNNERS_INTERVAL = 10000; + +export const STARTING = 'starting'; +export const PENDING = 'pending'; +export const RUNNING = 'running'; +export const STOPPING = 'stopping'; +export const STOPPED = 'stopped'; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/getters.js b/app/assets/javascripts/ide/stores/modules/terminal/getters.js new file mode 100644 index 00000000000..6d64ee4ab6e --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/getters.js @@ -0,0 +1,19 @@ +export const allCheck = state => { + const checks = Object.values(state.checks); + + if (checks.some(check => check.isLoading)) { + return { isLoading: true }; + } + + const invalidCheck = checks.find(check => !check.isValid); + const isValid = !invalidCheck; + const message = !invalidCheck ? '' : invalidCheck.message; + + return { + isLoading: false, + isValid, + message, + }; +}; + +export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/index.js b/app/assets/javascripts/ide/stores/modules/terminal/index.js new file mode 100644 index 00000000000..ef1289e1722 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/index.js @@ -0,0 +1,12 @@ +import * as actions from './actions'; +import * as getters from './getters'; +import mutations from './mutations'; +import state from './state'; + +export default () => ({ + namespaced: true, + actions, + getters, + mutations, + state: state(), +}); diff --git a/app/assets/javascripts/ide/stores/modules/terminal/messages.js b/app/assets/javascripts/ide/stores/modules/terminal/messages.js new file mode 100644 index 00000000000..38c5a8a28d8 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/messages.js @@ -0,0 +1,55 @@ +import { escape } from 'lodash'; +import { __, sprintf } from '~/locale'; +import httpStatus from '~/lib/utils/http_status'; + +export const UNEXPECTED_ERROR_CONFIG = __( + 'An unexpected error occurred while checking the project environment.', +); +export const UNEXPECTED_ERROR_RUNNERS = __( + 'An unexpected error occurred while checking the project runners.', +); +export const UNEXPECTED_ERROR_STATUS = __( + 'An unexpected error occurred while communicating with the Web Terminal.', +); +export const UNEXPECTED_ERROR_STARTING = __( + 'An unexpected error occurred while starting the Web Terminal.', +); +export const UNEXPECTED_ERROR_STOPPING = __( + 'An unexpected error occurred while stopping the Web Terminal.', +); +export const EMPTY_RUNNERS = __( + 'Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}', +); +export const ERROR_CONFIG = __( + 'Configure a <code>.gitlab-webide.yml</code> file in the <code>.gitlab</code> directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}', +); +export const ERROR_PERMISSION = __( + 'You do not have permission to run the Web Terminal. Please contact a project administrator.', +); + +export const configCheckError = (status, helpUrl) => { + if (status === httpStatus.UNPROCESSABLE_ENTITY) { + return sprintf( + ERROR_CONFIG, + { + helpStart: `<a href="${escape(helpUrl)}" target="_blank">`, + helpEnd: '</a>', + }, + false, + ); + } else if (status === httpStatus.FORBIDDEN) { + return ERROR_PERMISSION; + } + + return UNEXPECTED_ERROR_CONFIG; +}; + +export const runnersCheckEmpty = helpUrl => + sprintf( + EMPTY_RUNNERS, + { + helpStart: `<a href="${escape(helpUrl)}" target="_blank">`, + helpEnd: '</a>', + }, + false, + ); diff --git a/app/assets/javascripts/ide/stores/modules/terminal/mutation_types.js b/app/assets/javascripts/ide/stores/modules/terminal/mutation_types.js new file mode 100644 index 00000000000..b6a6f28abfa --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/mutation_types.js @@ -0,0 +1,11 @@ +export const SET_VISIBLE = 'SET_VISIBLE'; +export const HIDE_SPLASH = 'HIDE_SPLASH'; +export const SET_PATHS = 'SET_PATHS'; + +export const REQUEST_CHECK = 'REQUEST_CHECK'; +export const RECEIVE_CHECK_SUCCESS = 'RECEIVE_CHECK_SUCCESS'; +export const RECEIVE_CHECK_ERROR = 'RECEIVE_CHECK_ERROR'; + +export const SET_SESSION = 'SET_SESSION'; +export const SET_SESSION_STATUS = 'SET_SESSION_STATUS'; +export const SET_SESSION_STATUS_INTERVAL = 'SET_SESSION_STATUS_INTERVAL'; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/mutations.js b/app/assets/javascripts/ide/stores/modules/terminal/mutations.js new file mode 100644 index 00000000000..37f40af9c2e --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/mutations.js @@ -0,0 +1,64 @@ +import * as types from './mutation_types'; + +export default { + [types.SET_VISIBLE](state, isVisible) { + Object.assign(state, { + isVisible, + }); + }, + [types.HIDE_SPLASH](state) { + Object.assign(state, { + isShowSplash: false, + }); + }, + [types.SET_PATHS](state, paths) { + Object.assign(state, { + paths, + }); + }, + [types.REQUEST_CHECK](state, type) { + Object.assign(state.checks, { + [type]: { + isLoading: true, + }, + }); + }, + [types.RECEIVE_CHECK_ERROR](state, { type, message }) { + Object.assign(state.checks, { + [type]: { + isLoading: false, + isValid: false, + message, + }, + }); + }, + [types.RECEIVE_CHECK_SUCCESS](state, type) { + Object.assign(state.checks, { + [type]: { + isLoading: false, + isValid: true, + message: null, + }, + }); + }, + [types.SET_SESSION](state, session) { + Object.assign(state, { + session, + }); + }, + [types.SET_SESSION_STATUS](state, status) { + const session = { + ...(state.session || {}), + status, + }; + + Object.assign(state, { + session, + }); + }, + [types.SET_SESSION_STATUS_INTERVAL](state, sessionStatusInterval) { + Object.assign(state, { + sessionStatusInterval, + }); + }, +}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal/state.js b/app/assets/javascripts/ide/stores/modules/terminal/state.js new file mode 100644 index 00000000000..f35a10ed2fe --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/state.js @@ -0,0 +1,13 @@ +import { CHECK_CONFIG, CHECK_RUNNERS } from './constants'; + +export default () => ({ + checks: { + [CHECK_CONFIG]: { isLoading: true }, + [CHECK_RUNNERS]: { isLoading: true }, + }, + isVisible: false, + isShowSplash: true, + paths: {}, + session: null, + sessionStatusInterval: 0, +}); diff --git a/app/assets/javascripts/ide/stores/modules/terminal/utils.js b/app/assets/javascripts/ide/stores/modules/terminal/utils.js new file mode 100644 index 00000000000..c30136b5277 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal/utils.js @@ -0,0 +1,5 @@ +import { STARTING, PENDING, RUNNING } from './constants'; + +export const isStartingStatus = status => status === STARTING || status === PENDING; +export const isRunningStatus = status => status === RUNNING; +export const isEndingStatus = status => !isStartingStatus(status) && !isRunningStatus(status); diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/actions.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/actions.js new file mode 100644 index 00000000000..2fee6b4e974 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal_sync/actions.js @@ -0,0 +1,41 @@ +import * as types from './mutation_types'; +import mirror, { canConnect } from '../../../lib/mirror'; + +export const upload = ({ rootState, commit }) => { + commit(types.START_LOADING); + + return mirror + .upload(rootState) + .then(() => { + commit(types.SET_SUCCESS); + }) + .catch(err => { + commit(types.SET_ERROR, err); + }); +}; + +export const stop = ({ commit }) => { + mirror.disconnect(); + + commit(types.STOP); +}; + +export const start = ({ rootState, commit }) => { + const { session } = rootState.terminal; + const path = session && session.proxyWebsocketPath; + if (!path || !canConnect(session)) { + return Promise.reject(); + } + + commit(types.START_LOADING); + + return mirror + .connect(path) + .then(() => { + commit(types.SET_SUCCESS); + }) + .catch(err => { + commit(types.SET_ERROR, err); + throw err; + }); +}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/index.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/index.js new file mode 100644 index 00000000000..795c2fad724 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal_sync/index.js @@ -0,0 +1,10 @@ +import state from './state'; +import * as actions from './actions'; +import mutations from './mutations'; + +export default () => ({ + namespaced: true, + actions, + mutations, + state: state(), +}); diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/messages.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/messages.js new file mode 100644 index 00000000000..e50e1a1406b --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal_sync/messages.js @@ -0,0 +1,5 @@ +import { __ } from '~/locale'; + +export const MSG_TERMINAL_SYNC_CONNECTING = __('Connecting to terminal sync service'); +export const MSG_TERMINAL_SYNC_UPLOADING = __('Uploading changes to terminal'); +export const MSG_TERMINAL_SYNC_RUNNING = __('Terminal sync service is running'); diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/mutation_types.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/mutation_types.js new file mode 100644 index 00000000000..ec809540c18 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal_sync/mutation_types.js @@ -0,0 +1,4 @@ +export const START_LOADING = 'START_LOADING'; +export const SET_ERROR = 'SET_ERROR'; +export const SET_SUCCESS = 'SET_SUCCESS'; +export const STOP = 'STOP'; diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/mutations.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/mutations.js new file mode 100644 index 00000000000..70ed137776a --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal_sync/mutations.js @@ -0,0 +1,22 @@ +import * as types from './mutation_types'; + +export default { + [types.START_LOADING](state) { + state.isLoading = true; + state.isError = false; + }, + [types.SET_ERROR](state, { message }) { + state.isLoading = false; + state.isError = true; + state.message = message; + }, + [types.SET_SUCCESS](state) { + state.isLoading = false; + state.isError = false; + state.isStarted = true; + }, + [types.STOP](state) { + state.isLoading = false; + state.isStarted = false; + }, +}; diff --git a/app/assets/javascripts/ide/stores/modules/terminal_sync/state.js b/app/assets/javascripts/ide/stores/modules/terminal_sync/state.js new file mode 100644 index 00000000000..7ec3e38f675 --- /dev/null +++ b/app/assets/javascripts/ide/stores/modules/terminal_sync/state.js @@ -0,0 +1,6 @@ +export default () => ({ + isLoading: false, + isStarted: false, + isError: false, + message: '', +}); diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index 5c78bfefa04..d94adc3760f 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -27,7 +27,6 @@ export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN'; export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA'; export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN'; export const SET_TREE_OPEN = 'SET_TREE_OPEN'; -export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL'; export const CREATE_TREE = 'CREATE_TREE'; export const REMOVE_ALL_CHANGES_FILES = 'REMOVE_ALL_CHANGES_FILES'; @@ -41,7 +40,6 @@ export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE'; export const SET_FILE_POSITION = 'SET_FILE_POSITION'; export const SET_FILE_VIEWMODE = 'SET_FILE_VIEWMODE'; -export const SET_FILE_EOL = 'SET_FILE_EOL'; export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES'; export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED'; export const REMOVE_FILE_FROM_CHANGED = 'REMOVE_FILE_FROM_CHANGED'; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index 12ac10df206..e827aacac13 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -65,14 +65,10 @@ export default { // NOTE: We can't clone `entry` in any of the below assignments because // we need `state.entries` and the `entry.tree` to reference the same object. - if (!foundEntry) { + if (!foundEntry || foundEntry.deleted) { Object.assign(state.entries, { [key]: entry, }); - } else if (foundEntry.deleted) { - Object.assign(state.entries, { - [key]: Object.assign(entry, { replaces: true }), - }); } else { const tree = entry.tree.filter( f => foundEntry.tree.find(e => e.path === f.path) === undefined, @@ -147,7 +143,6 @@ export default { raw: file.content, changed: Boolean(changedFile), staged: false, - replaces: false, lastCommitSha: lastCommit.commit.id, prevId: undefined, @@ -164,9 +159,6 @@ export default { Object.assign(state.entries[file.path], { rawPath: file.rawPath.replace(regex, file.path), - permalink: file.permalink.replace(regex, file.path), - commitsPath: file.commitsPath.replace(regex, file.path), - blamePath: file.blamePath.replace(regex, file.path), }); } }, @@ -207,8 +199,6 @@ export default { state.changedFiles = state.changedFiles.concat(entry); } } - - state.unusedSeal = false; }, [types.RENAME_ENTRY](state, { path, name, parentPath }) { const oldEntry = state.entries[path]; diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index 5c5920a3027..c90bc2a3320 100644 --- a/app/assets/javascripts/ide/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -99,11 +99,6 @@ export default { fileLanguage, }); }, - [types.SET_FILE_EOL](state, { file, eol }) { - Object.assign(state.entries[file.path], { - eol, - }); - }, [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) { Object.assign(state.entries[file.path], { editorRow, @@ -153,13 +148,11 @@ export default { [types.ADD_FILE_TO_CHANGED](state, path) { Object.assign(state, { changedFiles: state.changedFiles.concat(state.entries[path]), - unusedSeal: false, }); }, [types.REMOVE_FILE_FROM_CHANGED](state, path) { Object.assign(state, { changedFiles: state.changedFiles.filter(f => f.path !== path), - unusedSeal: false, }); }, [types.STAGE_CHANGE](state, { path, diffInfo }) { @@ -175,7 +168,6 @@ export default { deleted: diffInfo.deleted, }), }), - unusedSeal: false, }); if (stagedFile) { diff --git a/app/assets/javascripts/ide/stores/mutations/tree.js b/app/assets/javascripts/ide/stores/mutations/tree.js index c8f14a680c2..cce43a99bd9 100644 --- a/app/assets/javascripts/ide/stores/mutations/tree.js +++ b/app/assets/javascripts/ide/stores/mutations/tree.js @@ -34,11 +34,6 @@ export default { Object.assign(selectedTree, { tree }); }, - [types.SET_LAST_COMMIT_URL](state, { tree = state, url }) { - Object.assign(tree, { - lastCommitPath: url, - }); - }, [types.REMOVE_ALL_CHANGES_FILES](state) { Object.assign(state, { changedFiles: [], diff --git a/app/assets/javascripts/ide/stores/plugins/terminal.js b/app/assets/javascripts/ide/stores/plugins/terminal.js new file mode 100644 index 00000000000..66539c7bd4f --- /dev/null +++ b/app/assets/javascripts/ide/stores/plugins/terminal.js @@ -0,0 +1,25 @@ +import * as mutationTypes from '~/ide/stores/mutation_types'; +import terminalModule from '../modules/terminal'; + +function getPathsFromData(el) { + return { + webTerminalSvgPath: el.dataset.eeWebTerminalSvgPath, + webTerminalHelpPath: el.dataset.eeWebTerminalHelpPath, + webTerminalConfigHelpPath: el.dataset.eeWebTerminalConfigHelpPath, + webTerminalRunnersHelpPath: el.dataset.eeWebTerminalRunnersHelpPath, + }; +} + +export default function createTerminalPlugin(el) { + return store => { + store.registerModule('terminal', terminalModule()); + + store.dispatch('terminal/setPaths', getPathsFromData(el)); + + store.subscribe(({ type }) => { + if (type === mutationTypes.SET_BRANCH_WORKING_REFERENCE) { + store.dispatch('terminal/init'); + } + }); + }; +} diff --git a/app/assets/javascripts/ide/stores/plugins/terminal_sync.js b/app/assets/javascripts/ide/stores/plugins/terminal_sync.js new file mode 100644 index 00000000000..c60bba4293a --- /dev/null +++ b/app/assets/javascripts/ide/stores/plugins/terminal_sync.js @@ -0,0 +1,49 @@ +import { debounce } from 'lodash'; +import eventHub from '~/ide/eventhub'; +import terminalSyncModule from '../modules/terminal_sync'; +import { isEndingStatus, isRunningStatus } from '../modules/terminal/utils'; + +const UPLOAD_DEBOUNCE = 200; + +/** + * Registers and controls the terminalSync vuex module based on IDE events. + * + * - Watches the terminal session status state to control start/stop. + * - Listens for file change event to control upload. + */ +export default function createMirrorPlugin() { + return store => { + store.registerModule('terminalSync', terminalSyncModule()); + + const upload = debounce(() => { + store.dispatch(`terminalSync/upload`); + }, UPLOAD_DEBOUNCE); + + const stop = () => { + store.dispatch(`terminalSync/stop`); + eventHub.$off('ide.files.change', upload); + }; + + const start = () => { + store + .dispatch(`terminalSync/start`) + .then(() => { + eventHub.$on('ide.files.change', upload); + }) + .catch(() => { + // error is handled in store + }); + }; + + store.watch( + x => x.terminal && x.terminal.session && x.terminal.session.status, + val => { + if (isRunningStatus(val)) { + start(); + } else if (isEndingStatus(val)) { + stop(); + } + }, + ); + }; +} diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js index 0c95c22e8f8..c1a83bf0726 100644 --- a/app/assets/javascripts/ide/stores/state.js +++ b/app/assets/javascripts/ide/stores/state.js @@ -9,10 +9,8 @@ export default () => ({ stagedFiles: [], endpoints: {}, lastCommitMsg: '', - lastCommitPath: '', loading: false, openFiles: [], - parentTreeUrl: '', trees: {}, projects: {}, panelResizing: false, @@ -20,7 +18,6 @@ export default () => ({ viewer: viewerTypes.edit, delayViewerUpdated: false, currentActivityView: leftSidebarViews.edit.name, - unusedSeal: true, fileFindVisible: false, links: {}, errorMessage: null, diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 56671142bd4..1c5fe9fe9a5 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -1,5 +1,10 @@ import { commitActionTypes, FILE_VIEW_MODE_EDITOR } from '../constants'; -import { relativePathToAbsolute, isAbsolute, isRootRelative } from '~/lib/utils/url_utility'; +import { + relativePathToAbsolute, + isAbsolute, + isRootRelative, + isBase64DataUrl, +} from '~/lib/utils/url_utility'; export const dataStructure = () => ({ id: '', @@ -19,8 +24,6 @@ export const dataStructure = () => ({ active: false, changed: false, staged: false, - replaces: false, - lastCommitPath: '', lastCommitSha: '', lastCommit: { id: '', @@ -29,23 +32,14 @@ export const dataStructure = () => ({ updatedAt: '', author: '', }, - blamePath: '', - commitsPath: '', - permalink: '', rawPath: '', binary: false, - html: '', raw: '', content: '', - parentTreeUrl: '', - renderError: false, - base64: false, editorRow: 1, editorColumn: 1, fileLanguage: '', - eol: '', viewMode: FILE_VIEW_MODE_EDITOR, - previewMode: null, size: 0, parentPath: null, lastOpenedAt: 0, @@ -63,19 +57,14 @@ export const decorateData = entity => { url, name, path, - renderError, content = '', tempFile = false, active = false, opened = false, changed = false, - parentTreeUrl = '', - base64 = false, binary = false, rawPath = '', - previewMode, file_lock, - html, parentPath = '', } = entity; @@ -91,25 +80,15 @@ export const decorateData = entity => { tempFile, opened, active, - parentTreeUrl, changed, - renderError, content, - base64, binary, rawPath, - previewMode, file_lock, - html, parentPath, }); }; -export const findEntry = (tree, type, name, prop = 'name') => - tree.find(f => f.type === type && f[prop] === name); - -export const findIndexOfFile = (state, file) => state.findIndex(f => f.path === file.path); - export const setPageTitle = title => { document.title = title; }; @@ -124,7 +103,7 @@ export const commitActionForFile = file => { return commitActionTypes.move; } else if (file.deleted) { return commitActionTypes.delete; - } else if (file.tempFile && !file.replaces) { + } else if (file.tempFile) { return commitActionTypes.create; } @@ -155,9 +134,8 @@ export const createCommitPayload = ({ file_path: f.path, previous_path: f.prevPath || undefined, content: f.prevPath && !f.changed ? null : f.content || undefined, - encoding: f.base64 ? 'base64' : 'text', - last_commit_id: - newBranch || f.deleted || f.prevPath || f.replaces ? undefined : f.lastCommitSha, + encoding: isBase64DataUrl(f.rawPath) ? 'base64' : 'text', + last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha, })), start_sha: newBranch ? rootGetters.lastCommit.id : undefined, }); @@ -272,10 +250,6 @@ export const pathsAreEqual = (a, b) => { return cleanA === cleanB; }; -// if the contents of a file dont end with a newline, this function adds a newline -export const addFinalNewlineIfNeeded = content => - content.charAt(content.length - 1) !== '\n' ? `${content}\n` : content; - export function extractMarkdownImagesFromEntries(mdFile, entries) { /** * Regex to identify an image tag in markdown, like: |