diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/web_unsplash/static/src/js | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/web_unsplash/static/src/js')
| -rw-r--r-- | addons/web_unsplash/static/src/js/unsplash_beacon.js | 34 | ||||
| -rw-r--r-- | addons/web_unsplash/static/src/js/unsplash_image_widget.js | 259 | ||||
| -rw-r--r-- | addons/web_unsplash/static/src/js/unsplashapi.js | 89 |
3 files changed, 382 insertions, 0 deletions
diff --git a/addons/web_unsplash/static/src/js/unsplash_beacon.js b/addons/web_unsplash/static/src/js/unsplash_beacon.js new file mode 100644 index 00000000..c15ff027 --- /dev/null +++ b/addons/web_unsplash/static/src/js/unsplash_beacon.js @@ -0,0 +1,34 @@ +odoo.define('web_unsplash.beacon', function (require) { +'use strict'; + +var publicWidget = require('web.public.widget'); + +publicWidget.registry.UnsplashBeacon = publicWidget.Widget.extend({ + // /!\ To adapt the day the beacon makes sense for backend customizations + selector: '#wrapwrap', + + /** + * @override + */ + start: function () { + var unsplashImages = _.map(this.$('img[src*="/unsplash/"]'), function (img) { + // get image id from URL (`http://www.domain.com:1234/unsplash/xYdf5feoI/lion.jpg` -> `xYdf5feoI`) + return img.src.split('/unsplash/')[1].split('/')[0]; + }); + if (unsplashImages.length) { + this._rpc({ + route: '/web_unsplash/get_app_id', + }).then(function (appID) { + if (!appID) { + return; + } + $.get('https://views.unsplash.com/v', { + 'photo_id': unsplashImages.join(','), + 'app_id': appID, + }); + }); + } + return this._super.apply(this, arguments); + }, +}); +}); diff --git a/addons/web_unsplash/static/src/js/unsplash_image_widget.js b/addons/web_unsplash/static/src/js/unsplash_image_widget.js new file mode 100644 index 00000000..e638806d --- /dev/null +++ b/addons/web_unsplash/static/src/js/unsplash_image_widget.js @@ -0,0 +1,259 @@ +odoo.define('web_unsplash.image_widgets', function (require) { +'use strict'; + +var core = require('web.core'); +var UnsplashAPI = require('unsplash.api'); +var widgetsMedia = require('wysiwyg.widgets.media'); + +var unsplashAPI = null; + +// Prevent base class from treating unsplash images like regular attachments +const originalEvents = widgetsMedia.ImageWidget.prototype.events; +const clickHandler = originalEvents['click .o_existing_attachment_cell']; +if (!clickHandler) { + throw new Error(`Couldn't find a handler for o_existing_attachment_cell clicks. +The unsplash image widget needs to prevent this handler from executing on unsplash attachments.`); +} +_.extend(originalEvents, { + 'click .o_existing_attachment_cell:not(.o_unsplash_attachment_cell)': clickHandler, +}); +delete originalEvents['click .o_existing_attachment_cell']; + +widgetsMedia.ImageWidget.include({ + xmlDependencies: widgetsMedia.ImageWidget.prototype.xmlDependencies.concat( + ['/web_unsplash/static/src/xml/unsplash_image_widget.xml'] + ), + events: _.extend({}, widgetsMedia.ImageWidget.prototype.events, { + 'click .o_unsplash_attachment_cell[data-imgid]': '_onUnsplashImgClick', + 'click button.save_unsplash': '_onSaveUnsplashCredentials', + }), + + /** + * @override + */ + init: function () { + this._super.apply(this, arguments); + + this._unsplash = { + selectedImages: {}, + isMaxed: false, + query: false, + error: false, + records: [], + }; + + // TODO improve this + // + // This is a `hack` to prevent the UnsplashAPI to be destroyed every + // time the media dialog is closed. Indeed, UnsplashAPI has a cache + // system to recude unsplash call, it is then better to keep its state + // to take advantage from it from one media dialog call to another. + // + // Unsplash API will either be (it's still being discussed): + // * a service (ideally coming with an improvement to not auto load + // the service) + // * initialized in the website_root (trigger_up) + if (unsplashAPI === null) { + this.unsplashAPI = new UnsplashAPI(this); + unsplashAPI = this.unsplashAPI; + } else { + this.unsplashAPI = unsplashAPI; + this.unsplashAPI.setParent(this); + } + }, + /** + * @override + */ + destroy: function () { + // TODO See `hack` explained in `init`. This prevent the media dialog destroy + // to destroy unsplashAPI when destroying the children + this.unsplashAPI.setParent(undefined); + this._super.apply(this, arguments); + }, + + // -------------------------------------------------------------------------- + // Public + // -------------------------------------------------------------------------- + + /** + * @override + */ + _save: async function () { + const _super = this._super; + if (Object.keys(this._unsplash.selectedImages).length) { + this.saved = true; + const images = await this._rpc({ + route: '/web_unsplash/attachment/add', + params: { + unsplashurls: this._unsplash.selectedImages, + res_model: this.options.res_model, + res_id: this.options.res_id, + query: this._unsplash.query, + }, + }); + this.attachments.push(...images); + this.selectedAttachments.push(...images); + } + return _super.apply(this, arguments); + }, + /** + * @override + */ + search: async function (needle) { + var self = this; + await this._super(...arguments); + + this._unsplash.query = needle; + if (!needle) { + this._unsplash.records = []; + return; + } + + await this.unsplashAPI.getImages(needle, this.numberOfAttachmentsToDisplay).then(function (res) { + self._unsplash.isMaxed = res.isMaxed; + self._unsplash.records = res.images; + self._unsplash.error = false; + }, function (err) { + self._unsplash.error = err; + }); + }, + /** + * @override + */ + hasContent() { + if (this.searchService === 'all') { + return this._super(...arguments) || (this.unsplashRecords && this.unsplashRecords.length); + } else if (this.searchService === 'unsplash') { + return (this.unsplashRecords && this.unsplashRecords.length); + } + return this._super(...arguments); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _highlightSelected: function () { + this._super.apply(this, arguments); + + const $select = this.$('.o_unsplash_attachment_cell[data-imgid]').filter((i, el) => { + return $(el).data('imgid') in this._unsplash.selectedImages; + }).addClass('o_we_attachment_selected'); + return $select; + }, + /** + * @private + */ + _loadMoreImages: function (forceSearch) { + if (!this.$('.o_we_search').val()) { + return this._super(forceSearch); + } + this.numberOfAttachmentsToDisplay += 10; + this.search(this.$('.o_we_search').val()).then(() => this._renderThumbnails()); + }, + /** + * @override + */ + _renderThumbnails: function () { + this._super(...arguments); + this.$('.unsplash_error').empty(); + if (!['all', 'unsplash'].includes(this.searchService)) { + return; + } + if (this._unsplash.query && this._unsplash.error) { + this.$('.unsplash_error').html( + core.qweb.render('web_unsplash.dialog.error.content', { + status: this._unsplash.error, + }) + ); + return; + } + + if (['all', 'unsplash'].includes(this.searchService) && this._unsplash.query && !this._unsplash.isMaxed) { + this.$('.o_load_more').removeClass('d-none'); + this.$('.o_load_done_msg').addClass('d-none'); + } + }, + /** + * @override + */ + _renderExisting: function (attachments) { + this.unsplashRecords = this._unsplash.records.map(record => { + const url = new URL(record.urls.regular); + // In small windows, row height could get quite a bit larger than the min, so we keep some leeway. + url.searchParams.set('h', 2 * this.MIN_ROW_HEIGHT); + url.searchParams.delete('w'); + return Object.assign({}, record, { + url: url.toString(), + }); + }); + return this._super(...arguments); + }, + /** + * @override + */ + _selectAttachement: function (attachment, save) { + if (!this.options.multiImages) { + this._unsplash.selectedImages = {}; + } + this._super(...arguments); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onSaveUnsplashCredentials: function () { + var self = this; + var key = this.$('#accessKeyInput').val().trim(); + var appId = this.$('#appIdInput').val().trim(); + + this.$('#accessKeyInput').toggleClass('is-invalid', !key); + this.$('#appIdInput').toggleClass('is-invalid', !appId); + + if (key && appId) { + if (!this.$el.find('.is-invalid').length) { + this._rpc({ + route: '/web_unsplash/save_unsplash', + params: {key: key, appId: appId}, + }).then(function () { + self.unsplashAPI.clientId = key; + self._unsplash.error = false; + self.search(self._unsplash.query).then(() => self._renderThumbnails()); + }); + } + } + }, + /** + * @private + */ + _onUnsplashImgClick: function (ev) { + if (this.saved) { + // already saved, probably a double click. Ignore. + return; + } + const {imgid, url, downloadUrl, description} = ev.currentTarget.dataset; + if (!this.options.multiImages) { + this._unsplash.selectedImages = {}; + this.selectedAttachments = []; + } + if (imgid in this._unsplash.selectedImages) { + delete this._unsplash.selectedImages[imgid]; + } else { + const _1920Url = new URL(url); + _1920Url.searchParams.set('w', '1920'); + this._unsplash.selectedImages[imgid] = {url: _1920Url.href, download_url: downloadUrl, description: description}; + } + this._highlightSelected(); + if (!this.options.multiImages) { + this.trigger_up('save_request'); + } + }, +}); +}); diff --git a/addons/web_unsplash/static/src/js/unsplashapi.js b/addons/web_unsplash/static/src/js/unsplashapi.js new file mode 100644 index 00000000..8156be6c --- /dev/null +++ b/addons/web_unsplash/static/src/js/unsplashapi.js @@ -0,0 +1,89 @@ +odoo.define('unsplash.api', function (require) { +'use strict'; + +var Class = require('web.Class'); +var rpc = require('web.rpc'); +var Mixins = require('web.mixins'); +var ServicesMixin = require('web.ServicesMixin'); + +var UnsplashCore = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, { + /** + * @constructor + */ + init: function (parent) { + Mixins.EventDispatcherMixin.init.call(this, arguments); + this.setParent(parent); + + this._cache = {}; + this.clientId = false; + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Gets unsplash images from query string. + * + * @param {String} query search terms + * @param {Integer} pageSize number of image to display per page + * @returns {Promise} + */ + getImages: function (query, pageSize) { + var from = 0; + var to = pageSize; + var cachedData = this._cache[query]; + + if (cachedData && (cachedData.images.length >= to || (cachedData.totalImages !== 0 && cachedData.totalImages < to))) { + return Promise.resolve({ images: cachedData.images.slice(from, to), isMaxed: to > cachedData.totalImages }); + } + return this._fetchImages(query).then(function (cachedData) { + return { images: cachedData.images.slice(from, to), isMaxed: to > cachedData.totalImages }; + }); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Fetches images from unsplash and stores it in cache + * + * @param {String} query search terms + * @returns {Promise} + * @private + */ + _fetchImages: function (query) { + if (!this._cache[query]) { + this._cache[query] = { + images: [], + maxPages: 0, + totalImages: 0, + pageCached: 0 + }; + } + var cachedData = this._cache[query]; + var payload = { + query: query, + page: cachedData.pageCached + 1, + per_page: 30, // max size from unsplash API + }; + return this._rpc({ + route: '/web_unsplash/fetch_images', + params: payload, + }).then(function (result) { + if (result.error) { + return Promise.reject(result.error); + } + cachedData.pageCached++; + cachedData.images.push.apply(cachedData.images, result.results); + cachedData.maxPages = result.total_pages; + cachedData.totalImages = result.total; + return cachedData; + }); + }, +}); + +return UnsplashCore; + +}); |
