Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorAlexis Reigel <alexis.reigel.ext@siemens.com>2017-12-06 23:04:53 +0300
committerAlexis Reigel <mail@koffeinfrei.org>2018-06-05 17:20:21 +0300
commit9e14f437b6ed205744d916f5566ee2c11e52b734 (patch)
tree5c4bf1237cfc4a6cf73f30f942d25e0ab4f0973c /app
parent5202c3f0c8da618e2d3821917f6f5d48ae8ae3c2 (diff)
create favicon overlay on the client
the initial reason for this change was that graphicsmagick does not support writing to ico files. this fact lead to a chain of changes: 1. use png instead of ico (browser support is good enough) 2. render the overlays on the client using the canvas API. this way we only need to store the original favion and generate the overlay versions dynamically. this change also enables (next step) to simplify the handling of the stock favicons as well, as we don't need to generate all the versions upfront.
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/favicon_admin.js19
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js50
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue5
-rw-r--r--app/serializers/status_entity.rb2
-rw-r--r--app/uploaders/favicon_uploader.rb37
-rw-r--r--app/views/admin/appearances/_form.html.haml7
-rw-r--r--app/views/layouts/_head.html.haml2
7 files changed, 75 insertions, 47 deletions
diff --git a/app/assets/javascripts/favicon_admin.js b/app/assets/javascripts/favicon_admin.js
new file mode 100644
index 00000000000..6b2dcf4502e
--- /dev/null
+++ b/app/assets/javascripts/favicon_admin.js
@@ -0,0 +1,19 @@
+import {createOverlayIcon} from '~/lib/utils/common_utils';
+
+export default class FaviconAdmin {
+ constructor() {
+ const faviconContainer = $('.js-favicons');
+ const faviconUrl = faviconContainer.data('favicon');
+ const overlayUrls = faviconContainer.data('status-overlays');
+
+ overlayUrls.forEach((statusOverlay) => {
+ createOverlayIcon(faviconUrl, statusOverlay).then((faviconWithOverlayUrl) => {
+ const image = $('<img />');
+ image.addClass('appearance-light-logo-preview');
+ image.attr('src', faviconWithOverlayUrl);
+
+ faviconContainer.append(image);
+ });
+ });
+ }
+}
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 310b731c6d8..d55d0585031 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -384,6 +384,49 @@ export const backOff = (fn, timeout = 60000) => {
});
};
+export const createOverlayIcon = (iconPath, overlayPath) => {
+ const faviconImage = document.createElement('img');
+
+ return new Promise((resolve) => {
+ faviconImage.onload = () => {
+ const size = 32;
+
+ const canvas = document.createElement('canvas');
+ canvas.width = size;
+ canvas.height = size;
+
+ const context = canvas.getContext('2d');
+ context.clearRect(0, 0, size, size);
+ context.drawImage(
+ faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size,
+ );
+
+ const overlayImage = document.createElement('img');
+ overlayImage.onload = () => {
+ context.drawImage(
+ overlayImage, 0, 0, overlayImage.width, overlayImage.height, 0, 0, size, size,
+ );
+
+ const faviconWithOverlayUrl = canvas.toDataURL();
+
+ resolve(faviconWithOverlayUrl);
+ };
+ overlayImage.src = overlayPath;
+ };
+ faviconImage.src = iconPath;
+ });
+};
+
+export const setFaviconOverlay = (overlayPath) => {
+ const faviconEl = document.getElementById('favicon');
+
+ if (!faviconEl) { return null; }
+
+ const iconPath = faviconEl.getAttribute('data-original-href');
+
+ return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl => faviconEl.setAttribute('href', faviconWithOverlayUrl));
+};
+
export const setFavicon = (faviconPath) => {
const faviconEl = document.getElementById('favicon');
if (faviconEl && faviconPath) {
@@ -395,7 +438,7 @@ export const resetFavicon = () => {
const faviconEl = document.getElementById('favicon');
if (faviconEl) {
- const originalFavicon = faviconEl.getAttribute('data-default-href');
+ const originalFavicon = faviconEl.getAttribute('data-original-href');
faviconEl.setAttribute('href', originalFavicon);
}
};
@@ -404,10 +447,9 @@ export const setCiStatusFavicon = pageUrl =>
axios.get(pageUrl)
.then(({ data }) => {
if (data && data.favicon) {
- setFavicon(data.favicon);
- } else {
- resetFavicon();
+ return setFaviconOverlay(data.favicon);
}
+ return resetFavicon();
})
.catch(resetFavicon);
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index f69fe03fcb3..d6cba878b28 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -36,7 +36,7 @@ import {
notify,
SourceBranchRemovalStatus,
} from './dependencies';
-import { setFavicon } from '../lib/utils/common_utils';
+import { setFaviconOverlay } from '../lib/utils/common_utils';
export default {
el: '#js-vue-mr-widget',
@@ -159,8 +159,9 @@ export default {
},
setFaviconHelper() {
if (this.mr.ciStatusFaviconPath) {
- setFavicon(this.mr.ciStatusFaviconPath);
+ return setFaviconOverlay(this.mr.ciStatusFaviconPath);
}
+ return Promise.resolve();
},
fetchDeployments() {
return this.service.fetchDeployments()
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
index 2f3d4b80565..47df7f9dcf9 100644
--- a/app/serializers/status_entity.rb
+++ b/app/serializers/status_entity.rb
@@ -7,7 +7,7 @@ class StatusEntity < Grape::Entity
expose :details_path
expose :favicon do |status|
- Gitlab::Favicon.status(status.favicon)
+ Gitlab::Favicon.status_overlay(status.favicon)
end
expose :action, if: -> (status, _) { status.has_action? } do
diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb
index 7697f5fe885..d7be77477b2 100644
--- a/app/uploaders/favicon_uploader.rb
+++ b/app/uploaders/favicon_uploader.rb
@@ -1,35 +1,12 @@
class FaviconUploader < AttachmentUploader
include CarrierWave::MiniMagick
- STATUS_ICON_NAMES = [
- :favicon_status_canceled,
- :favicon_status_created,
- :favicon_status_failed,
- :favicon_status_manual,
- :favicon_status_not_found,
- :favicon_status_pending,
- :favicon_status_running,
- :favicon_status_skipped,
- :favicon_status_success,
- :favicon_status_warning
- ].freeze
-
version :favicon_main do
process resize_to_fill: [32, 32]
- process convert: 'ico'
+ process convert: 'png'
def full_filename(filename)
- filename_for_different_format(super(filename), 'ico')
- end
- end
-
- STATUS_ICON_NAMES.each do |status_name|
- version status_name, from_version: :favicon_main do
- process status_favicon: status_name
-
- def full_filename(filename)
- filename_for_different_format(super(filename), 'ico')
- end
+ filename_for_different_format(super(filename), 'png')
end
end
@@ -39,16 +16,6 @@ class FaviconUploader < AttachmentUploader
private
- def status_favicon(status_name)
- manipulate! do |img|
- overlay_path = Rails.root.join("app/assets/images/ci_favicons/overlays/#{status_name}.png")
- overlay = MiniMagick::Image.open(overlay_path)
- img.composite(overlay) do |c|
- c.compose 'over'
- end
- end
- end
-
def filename_for_different_format(filename, format)
filename.chomp(File.extname(filename)) + ".#{format}"
end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 1119c75637c..f77e22bcc45 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -62,13 +62,12 @@
= f.label :favicon, 'Favicon', class: 'control-label'
.col-sm-10
- if @appearance.favicon?
- = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview'
+ = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview js-main-favicon'
- if @appearance.favicon?
- = f.label :favicon, 'Generated status icons', class: 'control-label'
+ = f.label :favicon, 'Status icons preview', class: 'control-label'
.col-sm-10
- if @appearance.favicon?
- - FaviconUploader::STATUS_ICON_NAMES.each do |status_name|
- = image_tag @appearance.favicon.public_send(status_name).url, class: 'appearance-light-logo-preview'
+ .js-favicons{ data: { favicon: @appearance.favicon.favicon_main.url, status_overlays: Gitlab::Favicon.available_status_overlays } }
- if @appearance.persisted?
%br
= link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo"
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 024f80e9935..9253a0652da 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -25,7 +25,7 @@
%title= page_title(site_name)
%meta{ name: "description", content: page_description }
- = favicon_link_tag favicon, id: 'favicon', :'data-default-href': favicon
+ = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png'
= stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print"