summaryrefslogtreecommitdiff
path: root/addons/web_unsplash/static/src/js/unsplash_image_widget.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/web_unsplash/static/src/js/unsplash_image_widget.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/web_unsplash/static/src/js/unsplash_image_widget.js')
-rw-r--r--addons/web_unsplash/static/src/js/unsplash_image_widget.js259
1 files changed, 259 insertions, 0 deletions
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');
+ }
+ },
+});
+});