summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/ide/stores/modules
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ide/stores/modules')
-rw-r--r--app/assets/javascripts/ide/stores/modules/branches/index.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/clientside/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js8
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/getters.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/index.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/file_templates/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/pane/getters.js7
-rw-r--r--app/assets/javascripts/ide/stores/modules/router/actions.js6
-rw-r--r--app/assets/javascripts/ide/stores/modules/router/index.js10
-rw-r--r--app/assets/javascripts/ide/stores/modules/router/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/router/mutations.js7
-rw-r--r--app/assets/javascripts/ide/stores/modules/router/state.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/actions/checks.js98
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/actions/index.js5
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/actions/session_controls.js118
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/actions/session_status.js64
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/actions/setup.js14
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/constants.js9
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/getters.js19
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/index.js12
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/messages.js55
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/mutation_types.js11
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/mutations.js64
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/state.js13
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal/utils.js5
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal_sync/actions.js41
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal_sync/index.js10
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal_sync/messages.js5
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal_sync/mutation_types.js4
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal_sync/mutations.js22
-rw-r--r--app/assets/javascripts/ide/stores/modules/terminal_sync/state.js6
32 files changed, 612 insertions, 22 deletions
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: '',
+});