Merge branch 'sketch-preview-files' into 'master'
Load a preview of Sketch 43 files See merge request !10470
This commit is contained in:
commit
c713292992
|
@ -0,0 +1,73 @@
|
|||
import JSZip from 'jszip';
|
||||
import JSZipUtils from 'jszip-utils';
|
||||
|
||||
export default class SketchLoader {
|
||||
constructor(container) {
|
||||
this.container = container;
|
||||
this.loadingIcon = this.container.querySelector('.js-loading-icon');
|
||||
|
||||
this.load();
|
||||
}
|
||||
|
||||
load() {
|
||||
return this.getZipFile()
|
||||
.then(data => JSZip.loadAsync(data))
|
||||
.then(asyncResult => asyncResult.files['previews/preview.png'].async('uint8array'))
|
||||
.then((content) => {
|
||||
const url = window.URL || window.webkitURL;
|
||||
const blob = new Blob([new Uint8Array(content)], {
|
||||
type: 'image/png',
|
||||
});
|
||||
const previewUrl = url.createObjectURL(blob);
|
||||
|
||||
this.render(previewUrl);
|
||||
})
|
||||
.catch(this.error.bind(this));
|
||||
}
|
||||
|
||||
getZipFile() {
|
||||
return new JSZip.external.Promise((resolve, reject) => {
|
||||
JSZipUtils.getBinaryContent(this.container.dataset.endpoint, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render(previewUrl) {
|
||||
const previewLink = document.createElement('a');
|
||||
const previewImage = document.createElement('img');
|
||||
|
||||
previewLink.href = previewUrl;
|
||||
previewLink.target = '_blank';
|
||||
previewImage.src = previewUrl;
|
||||
previewImage.className = 'img-responsive';
|
||||
|
||||
previewLink.appendChild(previewImage);
|
||||
this.container.appendChild(previewLink);
|
||||
|
||||
this.removeLoadingIcon();
|
||||
}
|
||||
|
||||
error() {
|
||||
const errorMsg = document.createElement('p');
|
||||
|
||||
errorMsg.className = 'prepend-top-default append-bottom-default text-center';
|
||||
errorMsg.textContent = `
|
||||
Cannot show preview. For previews on sketch files, they must have the file format
|
||||
introduced by Sketch version 43 and above.
|
||||
`;
|
||||
this.container.appendChild(errorMsg);
|
||||
|
||||
this.removeLoadingIcon();
|
||||
}
|
||||
|
||||
removeLoadingIcon() {
|
||||
if (this.loadingIcon) {
|
||||
this.loadingIcon.remove();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/* eslint-disable no-new */
|
||||
import SketchLoader from './sketch';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const el = document.getElementById('js-sketch-viewer');
|
||||
|
||||
new SketchLoader(el);
|
||||
});
|
|
@ -50,6 +50,10 @@ class Blob < SimpleDelegator
|
|||
text? && language&.name == 'Jupyter Notebook'
|
||||
end
|
||||
|
||||
def sketch?
|
||||
binary? && extname.downcase.delete('.') == 'sketch'
|
||||
end
|
||||
|
||||
def size_within_svg_limits?
|
||||
size <= MAXIMUM_SVG_SIZE
|
||||
end
|
||||
|
@ -69,6 +73,8 @@ class Blob < SimpleDelegator
|
|||
'image'
|
||||
elsif ipython_notebook?
|
||||
'notebook'
|
||||
elsif sketch?
|
||||
'sketch'
|
||||
elsif text?
|
||||
'text'
|
||||
else
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
- content_for :page_specific_javascripts do
|
||||
= page_specific_javascript_bundle_tag('common_vue')
|
||||
= page_specific_javascript_bundle_tag('sketch_viewer')
|
||||
|
||||
.file-content#js-sketch-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
|
||||
.js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
|
||||
= icon('spinner spin 2x', 'aria-hidden' => 'true');
|
|
@ -326,3 +326,21 @@
|
|||
:why: https://github.com/domenic/opener/blob/1.4.3/LICENSE.txt
|
||||
:versions: []
|
||||
:when: 2017-02-21 22:33:41.729629000 Z
|
||||
- - :approve
|
||||
- jszip
|
||||
- :who: Phil Hughes
|
||||
:why: https://github.com/Stuk/jszip/blob/master/LICENSE.markdown
|
||||
:versions: []
|
||||
:when: 2017-04-05 10:38:46.275721000 Z
|
||||
- - :approve
|
||||
- jszip-utils
|
||||
- :who: Phil Hughes
|
||||
:why: https://github.com/Stuk/jszip-utils/blob/master/LICENSE.markdown
|
||||
:versions: []
|
||||
:when: 2017-04-05 10:39:32.676232000 Z
|
||||
- - :approve
|
||||
- pako
|
||||
- :who: Phil Hughes
|
||||
:why: https://github.com/nodeca/pako/blob/master/LICENSE
|
||||
:versions: []
|
||||
:when: 2017-04-05 10:43:45.897720000 Z
|
||||
|
|
|
@ -37,6 +37,7 @@ var config = {
|
|||
monitoring: './monitoring/monitoring_bundle.js',
|
||||
network: './network/network_bundle.js',
|
||||
notebook_viewer: './blob/notebook_viewer.js',
|
||||
sketch_viewer: './blob/sketch_viewer.js',
|
||||
profile: './profile/profile_bundle.js',
|
||||
protected_branches: './protected_branches/protected_branches_bundle.js',
|
||||
snippet: './snippet/snippet_bundle.js',
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
"jquery": "^2.2.1",
|
||||
"jquery-ujs": "^1.2.1",
|
||||
"js-cookie": "^2.1.3",
|
||||
"jszip": "^3.1.3",
|
||||
"jszip-utils": "^0.0.2",
|
||||
"mousetrap": "^1.4.6",
|
||||
"pikaday": "^1.5.1",
|
||||
"raphael": "^2.2.7",
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/* eslint-disable no-new */
|
||||
import JSZip from 'jszip';
|
||||
import SketchLoader from '~/blob/sketch';
|
||||
|
||||
describe('Sketch viewer', () => {
|
||||
const generateZipFileArrayBuffer = (zipFile, resolve, done) => {
|
||||
zipFile
|
||||
.generateAsync({ type: 'arrayBuffer' })
|
||||
.then((content) => {
|
||||
resolve(content);
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
|
||||
preloadFixtures('static/sketch_viewer.html.raw');
|
||||
|
||||
beforeEach(() => {
|
||||
loadFixtures('static/sketch_viewer.html.raw');
|
||||
});
|
||||
|
||||
describe('with error message', () => {
|
||||
beforeEach((done) => {
|
||||
spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve, reject) => {
|
||||
reject();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
}));
|
||||
|
||||
new SketchLoader(document.getElementById('js-sketch-viewer'));
|
||||
});
|
||||
|
||||
it('renders error message', () => {
|
||||
expect(
|
||||
document.querySelector('#js-sketch-viewer p'),
|
||||
).not.toBeNull();
|
||||
|
||||
expect(
|
||||
document.querySelector('#js-sketch-viewer p').textContent.trim(),
|
||||
).toContain('Cannot show preview.');
|
||||
});
|
||||
|
||||
it('removes render the loading icon', () => {
|
||||
expect(
|
||||
document.querySelector('.js-loading-icon'),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('success', () => {
|
||||
beforeEach((done) => {
|
||||
spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => {
|
||||
const zipFile = new JSZip();
|
||||
zipFile.folder('previews')
|
||||
.file('preview.png', 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=', {
|
||||
base64: true,
|
||||
});
|
||||
|
||||
generateZipFileArrayBuffer(zipFile, resolve, done);
|
||||
}));
|
||||
|
||||
new SketchLoader(document.getElementById('js-sketch-viewer'));
|
||||
});
|
||||
|
||||
it('does not render error message', () => {
|
||||
expect(
|
||||
document.querySelector('#js-sketch-viewer p'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('removes render the loading icon', () => {
|
||||
expect(
|
||||
document.querySelector('.js-loading-icon'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('renders preview img', () => {
|
||||
const img = document.querySelector('#js-sketch-viewer img');
|
||||
|
||||
expect(img).not.toBeNull();
|
||||
expect(img.classList.contains('img-responsive')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders link to image', () => {
|
||||
const img = document.querySelector('#js-sketch-viewer img');
|
||||
const link = document.querySelector('#js-sketch-viewer a');
|
||||
|
||||
expect(link.href).toBe(img.src);
|
||||
expect(link.target).toBe('_blank');
|
||||
});
|
||||
});
|
||||
|
||||
describe('incorrect file', () => {
|
||||
beforeEach((done) => {
|
||||
spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => {
|
||||
const zipFile = new JSZip();
|
||||
|
||||
generateZipFileArrayBuffer(zipFile, resolve, done);
|
||||
}));
|
||||
|
||||
new SketchLoader(document.getElementById('js-sketch-viewer'));
|
||||
});
|
||||
|
||||
it('renders error message', () => {
|
||||
expect(
|
||||
document.querySelector('#js-sketch-viewer p'),
|
||||
).not.toBeNull();
|
||||
|
||||
expect(
|
||||
document.querySelector('#js-sketch-viewer p').textContent.trim(),
|
||||
).toContain('Cannot show preview.');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
.file-content#js-sketch-viewer{ data: { endpoint: '/test_sketch_file.sketch' } }
|
||||
.js-loading-icon
|
|
@ -67,6 +67,20 @@ describe Blob do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#sketch?' do
|
||||
it 'is falsey with image extension' do
|
||||
git_blob = Gitlab::Git::Blob.new(name: "design.png")
|
||||
|
||||
expect(described_class.decorate(git_blob)).not_to be_sketch
|
||||
end
|
||||
|
||||
it 'is truthy with sketch extension' do
|
||||
git_blob = Gitlab::Git::Blob.new(name: "design.sketch")
|
||||
|
||||
expect(described_class.decorate(git_blob)).to be_sketch
|
||||
end
|
||||
end
|
||||
|
||||
describe '#video?' do
|
||||
it 'is falsey with image extension' do
|
||||
git_blob = Gitlab::Git::Blob.new(name: 'image.png')
|
||||
|
@ -92,7 +106,8 @@ describe Blob do
|
|||
language: nil,
|
||||
lfs_pointer?: false,
|
||||
svg?: false,
|
||||
text?: false
|
||||
text?: false,
|
||||
binary?: false
|
||||
)
|
||||
|
||||
described_class.decorate(double).tap do |blob|
|
||||
|
@ -135,6 +150,11 @@ describe Blob do
|
|||
blob = stubbed_blob(text?: true, ipython_notebook?: true)
|
||||
expect(blob.to_partial_path(project)).to eq 'notebook'
|
||||
end
|
||||
|
||||
it 'handles Sketch files' do
|
||||
blob = stubbed_blob(text?: true, sketch?: true, binary?: true)
|
||||
expect(blob.to_partial_path(project)).to eq 'sketch'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#size_within_svg_limits?' do
|
||||
|
|
38
yarn.lock
38
yarn.lock
|
@ -1245,6 +1245,10 @@ core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1:
|
|||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
|
||||
|
||||
core-js@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65"
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
|
@ -1581,6 +1585,10 @@ es6-map@^0.1.3:
|
|||
es6-symbol "~3.1.0"
|
||||
event-emitter "~0.3.4"
|
||||
|
||||
es6-promise@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
|
||||
|
||||
es6-promise@~4.0.3:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42"
|
||||
|
@ -2335,6 +2343,10 @@ ignore@^3.2.0:
|
|||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.2.tgz#1c51e1ef53bab6ddc15db4d9ac4ec139eceb3410"
|
||||
|
||||
immediate@~3.0.5:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||
|
||||
imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
|
@ -2764,6 +2776,20 @@ jsprim@^1.2.2:
|
|||
json-schema "0.2.3"
|
||||
verror "1.3.6"
|
||||
|
||||
jszip-utils@^0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jszip-utils/-/jszip-utils-0.0.2.tgz#457d5cbca60a1c2e0706e9da2b544e8e7bc50bf8"
|
||||
|
||||
jszip@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.3.tgz#8a920403b2b1651c0fc126be90192d9080957c37"
|
||||
dependencies:
|
||||
core-js "~2.3.0"
|
||||
es6-promise "~3.0.2"
|
||||
lie "~3.1.0"
|
||||
pako "~1.0.2"
|
||||
readable-stream "~2.0.6"
|
||||
|
||||
karma-coverage-istanbul-reporter@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-0.2.0.tgz#5766263338adeb0026f7e4ac7a89a5f056c5642c"
|
||||
|
@ -2868,6 +2894,12 @@ levn@^0.3.0, levn@~0.3.0:
|
|||
prelude-ls "~1.1.2"
|
||||
type-check "~0.3.2"
|
||||
|
||||
lie@~3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
load-json-file@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||
|
@ -3349,6 +3381,10 @@ pako@~0.2.0:
|
|||
version "0.2.9"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
|
||||
|
||||
pako@~1.0.2:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.5.tgz#d2205dfe5b9da8af797e7c163db4d1f84e4600bc"
|
||||
|
||||
parse-asn1@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23"
|
||||
|
@ -3661,7 +3697,7 @@ readable-stream@~1.0.2:
|
|||
isarray "0.0.1"
|
||||
string_decoder "~0.10.x"
|
||||
|
||||
readable-stream@~2.0.0:
|
||||
readable-stream@~2.0.0, readable-stream@~2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in New Issue