Commit d3c735d3 authored by Illya Klymov's avatar Illya Klymov

Merge branch 'himkp-ide-move-file-props' into 'master'

Web IDE: Move some file props away from the file object

See merge request gitlab-org/gitlab!33870
parents 1c46bc3c 718f54e7
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import TerminalSyncStatusSafe from './terminal_sync/terminal_sync_status_safe.vue'; import TerminalSyncStatusSafe from './terminal_sync/terminal_sync_status_safe.vue';
import { getFileEOL } from '../utils';
export default { export default {
components: { components: {
...@@ -8,6 +9,9 @@ export default { ...@@ -8,6 +9,9 @@ export default {
}, },
computed: { computed: {
...mapGetters(['activeFile']), ...mapGetters(['activeFile']),
activeFileEOL() {
return getFileEOL(this.activeFile.content);
},
}, },
}; };
</script> </script>
...@@ -16,7 +20,7 @@ export default { ...@@ -16,7 +20,7 @@ export default {
<div class="ide-status-list d-flex"> <div class="ide-status-list d-flex">
<template v-if="activeFile"> <template v-if="activeFile">
<div class="ide-status-file">{{ activeFile.name }}</div> <div class="ide-status-file">{{ activeFile.name }}</div>
<div class="ide-status-file">{{ activeFile.eol }}</div> <div class="ide-status-file">{{ activeFileEOL }}</div>
<div v-if="!activeFile.binary" class="ide-status-file"> <div v-if="!activeFile.binary" class="ide-status-file">
{{ activeFile.editorRow }}:{{ activeFile.editorColumn }} {{ activeFile.editorRow }}:{{ activeFile.editorColumn }}
</div> </div>
......
...@@ -35,7 +35,6 @@ export default { ...@@ -35,7 +35,6 @@ export default {
name: `${this.path ? `${this.path}/` : ''}${name}`, name: `${this.path ? `${this.path}/` : ''}${name}`,
type: 'blob', type: 'blob',
content, content,
base64: !isText,
binary: !isText, binary: !isText,
rawPath: !isText ? target.result : '', rawPath: !isText ? target.result : '',
}); });
......
...@@ -83,10 +83,6 @@ export default { ...@@ -83,10 +83,6 @@ export default {
active: this.isPreviewViewMode, active: this.isPreviewViewMode,
}; };
}, },
fileType() {
const info = viewerInformationForPath(this.file.path);
return (info && info.id) || '';
},
showEditor() { showEditor() {
return !this.shouldHideEditor && this.isEditorViewMode; return !this.shouldHideEditor && this.isEditorViewMode;
}, },
...@@ -99,6 +95,12 @@ export default { ...@@ -99,6 +95,12 @@ export default {
currentBranchCommit() { currentBranchCommit() {
return this.currentBranch?.commit.id; return this.currentBranch?.commit.id;
}, },
previewMode() {
return viewerInformationForPath(this.file.path);
},
fileType() {
return this.previewMode?.id || '';
},
}, },
watch: { watch: {
file(newVal, oldVal) { file(newVal, oldVal) {
...@@ -181,7 +183,6 @@ export default { ...@@ -181,7 +183,6 @@ export default {
'setFileLanguage', 'setFileLanguage',
'setEditorPosition', 'setEditorPosition',
'setFileViewMode', 'setFileViewMode',
'setFileEOL',
'updateViewer', 'updateViewer',
'removePendingTab', 'removePendingTab',
'triggerFilesChange', 'triggerFilesChange',
...@@ -260,7 +261,6 @@ export default { ...@@ -260,7 +261,6 @@ export default {
const monacoModel = model.getModel(); const monacoModel = model.getModel();
const content = monacoModel.getValue(); const content = monacoModel.getValue();
this.changeFileContent({ path: file.path, content }); this.changeFileContent({ path: file.path, content });
this.setFileEOL({ eol: this.model.eol });
}); });
// Handle Cursor Position // Handle Cursor Position
...@@ -280,11 +280,6 @@ export default { ...@@ -280,11 +280,6 @@ export default {
this.setFileLanguage({ this.setFileLanguage({
fileLanguage: this.model.language, fileLanguage: this.model.language,
}); });
// Get File eol
this.setFileEOL({
eol: this.model.eol,
});
}, },
refreshEditorDimensions() { refreshEditorDimensions() {
if (this.showEditor) { if (this.showEditor) {
...@@ -331,16 +326,15 @@ export default { ...@@ -331,16 +326,15 @@ export default {
role="button" role="button"
@click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_EDITOR })" @click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_EDITOR })"
> >
<template v-if="viewer === $options.viewerTypes.edit">{{ __('Edit') }}</template> {{ __('Edit') }}
<template v-else>{{ __('Review') }}</template>
</a> </a>
</li> </li>
<li v-if="file.previewMode" :class="previewTabCSS"> <li v-if="previewMode" :class="previewTabCSS">
<a <a
href="javascript:void(0);" href="javascript:void(0);"
role="button" role="button"
@click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_PREVIEW })" @click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_PREVIEW })"
>{{ file.previewMode.previewTitle }}</a >{{ previewMode.previewTitle }}</a
> >
</li> </li>
</ul> </ul>
......
...@@ -53,10 +53,6 @@ export default class Model { ...@@ -53,10 +53,6 @@ export default class Model {
return this.model.getModeId(); return this.model.getModeId();
} }
get eol() {
return this.model.getEOL() === '\n' ? 'LF' : 'CRLF';
}
get path() { get path() {
return this.file.key; return this.file.key;
} }
......
...@@ -19,7 +19,6 @@ export const decorateFiles = ({ ...@@ -19,7 +19,6 @@ export const decorateFiles = ({
branchId, branchId,
tempFile = false, tempFile = false,
content = '', content = '',
base64 = false,
binary = false, binary = false,
rawPath = '', rawPath = '',
}) => { }) => {
...@@ -88,10 +87,8 @@ export const decorateFiles = ({ ...@@ -88,10 +87,8 @@ export const decorateFiles = ({
tempFile, tempFile,
changed: tempFile, changed: tempFile,
content, content,
base64,
binary: (previewMode && previewMode.binary) || binary, binary: (previewMode && previewMode.binary) || binary,
rawPath, rawPath,
previewMode,
parentPath, parentPath,
}); });
......
...@@ -29,7 +29,6 @@ export const createTempEntry = ( ...@@ -29,7 +29,6 @@ export const createTempEntry = (
name, name,
type, type,
content = '', content = '',
base64 = false,
binary = false, binary = false,
rawPath = '', rawPath = '',
openFile = true, openFile = true,
...@@ -60,7 +59,6 @@ export const createTempEntry = ( ...@@ -60,7 +59,6 @@ export const createTempEntry = (
type, type,
tempFile: true, tempFile: true,
content, content,
base64,
binary, binary,
rawPath, rawPath,
}); });
...@@ -92,7 +90,6 @@ export const addTempImage = ({ dispatch, getters }, { name, rawPath = '' }) => ...@@ -92,7 +90,6 @@ export const addTempImage = ({ dispatch, getters }, { name, rawPath = '' }) =>
name: getters.getAvailableFileName(name), name: getters.getAvailableFileName(name),
type: 'blob', type: 'blob',
content: rawPath.split('base64,')[1], content: rawPath.split('base64,')[1],
base64: true,
binary: true, binary: true,
rawPath, rawPath,
openFile: false, openFile: false,
......
...@@ -169,12 +169,6 @@ export const setFileLanguage = ({ getters, commit }, { fileLanguage }) => { ...@@ -169,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 }) => { export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn }) => {
if (getters.activeFile) { if (getters.activeFile) {
commit(types.SET_FILE_POSITION, { commit(types.SET_FILE_POSITION, {
......
...@@ -40,7 +40,6 @@ export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT'; ...@@ -40,7 +40,6 @@ export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT';
export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE'; export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE';
export const SET_FILE_POSITION = 'SET_FILE_POSITION'; export const SET_FILE_POSITION = 'SET_FILE_POSITION';
export const SET_FILE_VIEWMODE = 'SET_FILE_VIEWMODE'; 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 DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES';
export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED'; export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED';
export const REMOVE_FILE_FROM_CHANGED = 'REMOVE_FILE_FROM_CHANGED'; export const REMOVE_FILE_FROM_CHANGED = 'REMOVE_FILE_FROM_CHANGED';
......
...@@ -99,11 +99,6 @@ export default { ...@@ -99,11 +99,6 @@ export default {
fileLanguage, fileLanguage,
}); });
}, },
[types.SET_FILE_EOL](state, { file, eol }) {
Object.assign(state.entries[file.path], {
eol,
});
},
[types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) { [types.SET_FILE_POSITION](state, { file, editorRow, editorColumn }) {
Object.assign(state.entries[file.path], { Object.assign(state.entries[file.path], {
editorRow, editorRow,
......
import { commitActionTypes, FILE_VIEW_MODE_EDITOR } from '../constants'; 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 = () => ({ export const dataStructure = () => ({
id: '', id: '',
...@@ -31,13 +36,10 @@ export const dataStructure = () => ({ ...@@ -31,13 +36,10 @@ export const dataStructure = () => ({
binary: false, binary: false,
raw: '', raw: '',
content: '', content: '',
base64: false,
editorRow: 1, editorRow: 1,
editorColumn: 1, editorColumn: 1,
fileLanguage: '', fileLanguage: '',
eol: '',
viewMode: FILE_VIEW_MODE_EDITOR, viewMode: FILE_VIEW_MODE_EDITOR,
previewMode: null,
size: 0, size: 0,
parentPath: null, parentPath: null,
lastOpenedAt: 0, lastOpenedAt: 0,
...@@ -60,10 +62,8 @@ export const decorateData = entity => { ...@@ -60,10 +62,8 @@ export const decorateData = entity => {
active = false, active = false,
opened = false, opened = false,
changed = false, changed = false,
base64 = false,
binary = false, binary = false,
rawPath = '', rawPath = '',
previewMode,
file_lock, file_lock,
parentPath = '', parentPath = '',
} = entity; } = entity;
...@@ -82,10 +82,8 @@ export const decorateData = entity => { ...@@ -82,10 +82,8 @@ export const decorateData = entity => {
active, active,
changed, changed,
content, content,
base64,
binary, binary,
rawPath, rawPath,
previewMode,
file_lock, file_lock,
parentPath, parentPath,
}); });
...@@ -136,7 +134,7 @@ export const createCommitPayload = ({ ...@@ -136,7 +134,7 @@ export const createCommitPayload = ({
file_path: f.path, file_path: f.path,
previous_path: f.prevPath || undefined, previous_path: f.prevPath || undefined,
content: f.prevPath && !f.changed ? null : f.content || undefined, content: f.prevPath && !f.changed ? null : f.content || undefined,
encoding: f.base64 ? 'base64' : 'text', encoding: isBase64DataUrl(f.rawPath) ? 'base64' : 'text',
last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha, last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
})), })),
start_sha: newBranch ? rootGetters.lastCommit.id : undefined, start_sha: newBranch ? rootGetters.lastCommit.id : undefined,
......
...@@ -119,3 +119,7 @@ export function readFileAsDataURL(file) { ...@@ -119,3 +119,7 @@ export function readFileAsDataURL(file) {
reader.readAsDataURL(file); reader.readAsDataURL(file);
}); });
} }
export function getFileEOL(content = '') {
return content.includes('\r\n') ? 'CRLF' : 'LF';
}
...@@ -243,6 +243,15 @@ export function isRootRelative(url) { ...@@ -243,6 +243,15 @@ export function isRootRelative(url) {
return /^\//.test(url); return /^\//.test(url);
} }
/**
* Returns true if url is a base64 data URL
*
* @param {String} url
*/
export function isBase64DataUrl(url) {
return /^data:[.\w+-]+\/[.\w+-]+;base64,/.test(url);
}
/** /**
* Returns true if url is an absolute or root-relative URL * Returns true if url is an absolute or root-relative URL
* *
......
...@@ -5,10 +5,10 @@ import TerminalSyncStatusSafe from '~/ide/components/terminal_sync/terminal_sync ...@@ -5,10 +5,10 @@ import TerminalSyncStatusSafe from '~/ide/components/terminal_sync/terminal_sync
const TEST_FILE = { const TEST_FILE = {
name: 'lorem.md', name: 'lorem.md',
eol: 'LF',
editorRow: 3, editorRow: 3,
editorColumn: 23, editorColumn: 23,
fileLanguage: 'markdown', fileLanguage: 'markdown',
content: 'abc\nndef',
}; };
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -56,7 +56,8 @@ describe('ide/components/ide_status_list', () => { ...@@ -56,7 +56,8 @@ describe('ide/components/ide_status_list', () => {
}); });
it('shows file eol', () => { it('shows file eol', () => {
expect(wrapper.text()).toContain(TEST_FILE.name); expect(wrapper.text()).not.toContain('CRLF');
expect(wrapper.text()).toContain('LF');
}); });
it('shows file editor position', () => { it('shows file editor position', () => {
......
...@@ -85,7 +85,6 @@ describe('new dropdown upload', () => { ...@@ -85,7 +85,6 @@ describe('new dropdown upload', () => {
name: textFile.name, name: textFile.name,
type: 'blob', type: 'blob',
content: 'plain text', content: 'plain text',
base64: false,
binary: false, binary: false,
rawPath: '', rawPath: '',
}); });
...@@ -103,7 +102,6 @@ describe('new dropdown upload', () => { ...@@ -103,7 +102,6 @@ describe('new dropdown upload', () => {
name: binaryFile.name, name: binaryFile.name,
type: 'blob', type: 'blob',
content: binaryTarget.result.split('base64,')[1], content: binaryTarget.result.split('base64,')[1],
base64: true,
binary: true, binary: true,
rawPath: binaryTarget.result, rawPath: binaryTarget.result,
}); });
......
...@@ -94,47 +94,24 @@ describe('RepoEditor', () => { ...@@ -94,47 +94,24 @@ describe('RepoEditor', () => {
}); });
describe('when file is markdown', () => { describe('when file is markdown', () => {
beforeEach(done => {
vm.file.previewMode = {
id: 'markdown',
previewTitle: 'Preview Markdown',
};
vm.$nextTick(done);
});
it('renders an Edit and a Preview Tab', done => {
Vue.nextTick(() => {
const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
expect(tabs.length).toBe(2);
expect(tabs[0].textContent.trim()).toBe('Edit');
expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
done();
});
});
});
describe('when file is markdown and viewer mode is review', () => {
let mock; let mock;
beforeEach(done => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
vm.file.projectId = 'namespace/project';
vm.file.previewMode = {
id: 'markdown',
previewTitle: 'Preview Markdown',
};
vm.file.content = 'testing 123';
vm.$store.state.viewer = 'diff';
mock.onPost(/(.*)\/preview_markdown/).reply(200, { mock.onPost(/(.*)\/preview_markdown/).reply(200, {
body: '<p>testing 123</p>', body: '<p>testing 123</p>',
}); });
vm.$nextTick(done); Vue.set(vm, 'file', {
...vm.file,
projectId: 'namespace/project',
path: 'sample.md',
content: 'testing 123',
});
vm.$store.state.entries[vm.file.path] = vm.file;
return vm.$nextTick();
}); });
afterEach(() => { afterEach(() => {
...@@ -146,7 +123,7 @@ describe('RepoEditor', () => { ...@@ -146,7 +123,7 @@ describe('RepoEditor', () => {
const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li'); const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li');
expect(tabs.length).toBe(2); expect(tabs.length).toBe(2);
expect(tabs[0].textContent.trim()).toBe('Review'); expect(tabs[0].textContent.trim()).toBe('Edit');
expect(tabs[1].textContent.trim()).toBe('Preview Markdown'); expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
done(); done();
...@@ -155,8 +132,6 @@ describe('RepoEditor', () => { ...@@ -155,8 +132,6 @@ describe('RepoEditor', () => {
it('renders markdown for tempFile', done => { it('renders markdown for tempFile', done => {
vm.file.tempFile = true; vm.file.tempFile = true;
vm.file.path = `${vm.file.path}.md`;
vm.$store.state.entries[vm.file.path] = vm.file;
vm.$nextTick() vm.$nextTick()
.then(() => { .then(() => {
...@@ -171,6 +146,20 @@ describe('RepoEditor', () => { ...@@ -171,6 +146,20 @@ describe('RepoEditor', () => {
.then(done) .then(done)
.catch(done.fail); .catch(done.fail);
}); });
describe('when not in edit mode', () => {
beforeEach(async () => {
await vm.$nextTick();
vm.$store.state.currentActivityView = leftSidebarViews.review.name;
return vm.$nextTick();
});
it('shows no tabs', () => {
expect(vm.$el.querySelectorAll('.ide-mode-tabs .nav-links a')).toHaveLength(0);
});
});
}); });
describe('when open file is binary and not raw', () => { describe('when open file is binary and not raw', () => {
...@@ -560,7 +549,6 @@ describe('RepoEditor', () => { ...@@ -560,7 +549,6 @@ describe('RepoEditor', () => {
path: 'foo/foo.png', path: 'foo/foo.png',
type: 'blob', type: 'blob',
content: 'Zm9v', content: 'Zm9v',
base64: true,
binary: true, binary: true,
rawPath: 'data:image/png;base64,Zm9v', rawPath: 'data:image/png;base64,Zm9v',
}); });
......
...@@ -13,8 +13,8 @@ import { commitActionTypes, PERMISSION_CREATE_MR } from '~/ide/constants'; ...@@ -13,8 +13,8 @@ import { commitActionTypes, PERMISSION_CREATE_MR } from '~/ide/constants';
import testAction from '../../../../helpers/vuex_action_helper'; import testAction from '../../../../helpers/vuex_action_helper';
jest.mock('~/lib/utils/url_utility', () => ({ jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
visitUrl: jest.fn(), visitUrl: jest.fn(),
joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
})); }));
const TEST_COMMIT_SHA = '123456789'; const TEST_COMMIT_SHA = '123456789';
......
...@@ -46,7 +46,7 @@ describe('Multi-file store utils', () => { ...@@ -46,7 +46,7 @@ describe('Multi-file store utils', () => {
path: 'added', path: 'added',
tempFile: true, tempFile: true,
content: 'new file content', content: 'new file content',
base64: true, rawPath: 'data:image/png;base64,abc',
lastCommitSha: '123456789', lastCommitSha: '123456789',
}, },
{ ...file('deletedFile'), path: 'deletedFile', deleted: true }, { ...file('deletedFile'), path: 'deletedFile', deleted: true },
...@@ -117,7 +117,7 @@ describe('Multi-file store utils', () => { ...@@ -117,7 +117,7 @@ describe('Multi-file store utils', () => {
path: 'added', path: 'added',
tempFile: true, tempFile: true,
content: 'new file content', content: 'new file content',
base64: true, rawPath: 'data:image/png;base64,abc',
lastCommitSha: '123456789', lastCommitSha: '123456789',
}, },
], ],
......
...@@ -371,6 +371,23 @@ describe('URL utility', () => { ...@@ -371,6 +371,23 @@ describe('URL utility', () => {
}); });
}); });
describe('isBase64DataUrl', () => {
it.each`
url | valid
${undefined} | ${false}
${'http://gitlab.com'} | ${false}
${'data:image/png;base64,abcdef'} | ${true}
${'data:application/smil+xml;base64,abcdef'} | ${true}
${'data:application/vnd.syncml+xml;base64,abcdef'} | ${true}
${'data:application/vnd.3m.post-it-notes;base64,abcdef'} | ${true}
${'notaurl'} | ${false}
${'../relative_url'} | ${false}
${'<a></a>'} | ${false}
`('returns $valid for $url', ({ url, valid }) => {
expect(urlUtils.isBase64DataUrl(url)).toBe(valid);
});
});
describe('relativePathToAbsolute', () => { describe('relativePathToAbsolute', () => {
it.each` it.each`
path | base | result path | base | result
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment