فهرست منبع

Compress all avatar images above 100kb

JC Brand 2 ماه پیش
والد
کامیت
fe4cdcb96c

+ 0 - 1
src/headless/plugins/vcard/api.js

@@ -42,7 +42,6 @@ export default {
          */
         async set (jid, data) {
             if (!jid) throw Error("No jid provided for the VCard data");
-            debugger;
             api.waitUntil('VCardsInitialized');
 
             let vcard = _converse.state.vcards.get(jid);

+ 26 - 24
src/plugins/profile/modals/profile.js

@@ -1,14 +1,13 @@
 import { Model } from '@converse/skeletor';
-import { _converse, api, log } from "@converse/headless";
+import { _converse, api, log } from '@converse/headless';
 import { compressImage, isImageWithAlphaChannel } from 'utils/file.js';
-import BaseModal from "plugins/modal/modal.js";
+import BaseModal from 'plugins/modal/modal.js';
 import { __ } from 'i18n';
 import '../password-reset.js';
-import tplProfileModal from "../templates/profile_modal.js";
+import tplProfileModal from '../templates/profile_modal.js';
 
 import './styles/profile.scss';
 
-
 export default class ProfileModal extends BaseModal {
     /**
      * @typedef {import('@converse/headless/types/plugins/vcard/types').VCardData} VCardData
@@ -18,17 +17,17 @@ export default class ProfileModal extends BaseModal {
     static properties = {
         _submitting: { state: true },
         model: { type: Model },
-    }
+    };
 
     /**
      * @param {Object} options
      */
-    constructor (options) {
+    constructor(options) {
         super(options);
         this.tab = 'profile';
     }
 
-    initialize () {
+    initialize() {
         super.initialize();
         this.listenTo(this.model, 'change', this.render);
         /**
@@ -40,27 +39,29 @@ export default class ProfileModal extends BaseModal {
         api.trigger('profileModalInitialized', this.model);
     }
 
-    renderModal () {
+    renderModal() {
         return tplProfileModal(this);
     }
 
-    getModalTitle () { // eslint-disable-line class-methods-use-this
+    getModalTitle() {
         return __('Your Profile');
     }
 
     /**
      * @param {VCardData} data
      */
-    async setVCard (data) {
+    async setVCard(data) {
         const bare_jid = _converse.session.get('bare_jid');
         try {
             await api.vcard.set(bare_jid, data);
         } catch (err) {
             log.fatal(err);
-            this.alert([
-                __("Sorry, an error happened while trying to save your profile data."),
-                __("You can check your browser's developer console for any error output.")
-            ].join(" "));
+            this.alert(
+                [
+                    __('Sorry, an error happened while trying to save your profile data.'),
+                    __("You can check your browser's developer console for any error output."),
+                ].join(' ')
+            );
             return false;
         }
         return true;
@@ -69,14 +70,14 @@ export default class ProfileModal extends BaseModal {
     /**
      * @param {SubmitEvent} ev
      */
-    async onFormSubmitted (ev) {
+    async onFormSubmitted(ev) {
         ev.preventDefault();
         this._submitting = true;
 
-        const form_data = new FormData(/** @type {HTMLFormElement} */(ev.target));
-        const image_file = /** @type {File} */(form_data.get('avatar_image'));
+        const form_data = new FormData(/** @type {HTMLFormElement} */ (ev.target));
+        const image_file = /** @type {File} */ (form_data.get('avatar_image'));
 
-        const data = /** @type {VCardData} */({
+        const data = /** @type {VCardData} */ ({
             fn: form_data.get('fn'),
             nickname: form_data.get('nickname'),
             role: form_data.get('role'),
@@ -85,14 +86,15 @@ export default class ProfileModal extends BaseModal {
         });
 
         if (image_file?.size) {
-            const image_data = isImageWithAlphaChannel ? image_file : await compressImage(image_file);
+            const image_data = await compressImage(image_file);
             const reader = new FileReader();
             reader.onloadend = async () => {
                 Object.assign(data, {
-                    image: btoa(/** @type {string} */(reader.result)),
-                    image_type: image_file.type
+                    image: btoa(/** @type {string} */ (reader.result)),
+                    image_type: image_file.type,
                 });
                 if (await this.setVCard(data)) {
+                    this._submitting = false;
                     this.modal.hide();
                 }
             };
@@ -100,14 +102,14 @@ export default class ProfileModal extends BaseModal {
         } else {
             Object.assign(data, {
                 image: this.model.vcard.get('image'),
-                image_type: this.model.vcard.get('image_type')
+                image_type: this.model.vcard.get('image_type'),
             });
             if (await this.setVCard(data)) {
                 this.modal.hide();
-                api.toast.show('vcard-updated', { body: __("Profile updated successfully") });
+                api.toast.show('vcard-updated', { body: __('Profile updated successfully') });
             }
+            this._submitting = false;
         }
-        this._submitting = false;
     }
 }
 

+ 1 - 1
src/types/plugins/profile/modals/profile.d.ts

@@ -23,6 +23,6 @@ export default class ProfileModal extends BaseModal {
     onFormSubmitted(ev: SubmitEvent): Promise<void>;
     _submitting: boolean;
 }
-import BaseModal from "plugins/modal/modal.js";
+import BaseModal from 'plugins/modal/modal.js';
 import { Model } from '@converse/skeletor';
 //# sourceMappingURL=profile.d.ts.map

+ 2 - 14
src/types/utils/file.d.ts

@@ -5,17 +5,11 @@
  */
 export function isImageWithAlphaChannel(image_file: File): Promise<boolean>;
 /**
- * @typedef {Object} CompressionOptions
- * @property {number} targetSize
- * @property {number} quality
- * @property {number} maxWidth
- * @property {number} maxHeight
- *
  * @param {File} file
- * @param {CompressionOptions} options
+ * @param {import('./types').CompressionOptions} options
  * @returns {Promise<Blob>}
  */
-export function compressImage(file: File, options?: CompressionOptions): Promise<Blob>;
+export function compressImage(file: File, options?: import("./types").CompressionOptions): Promise<Blob>;
 export const MIMETYPES_MAP: {
     aac: string;
     abw: string;
@@ -95,10 +89,4 @@ export const MIMETYPES_MAP: {
     '3g2': string;
     '7z': string;
 };
-export type CompressionOptions = {
-    targetSize: number;
-    quality: number;
-    maxWidth: number;
-    maxHeight: number;
-};
 //# sourceMappingURL=file.d.ts.map

+ 1 - 2
src/types/utils/index.d.ts

@@ -201,7 +201,7 @@ declare const _default: {
         unique<T extends unknown>(arr: Array<T>): Array<T>;
     } & import("headless/types/utils/index.js").CommonUtils & import("headless/types/utils/index.js").PluginUtils;
     isImageWithAlphaChannel(image_file: File): Promise<boolean>;
-    compressImage(file: File, options?: CompressionOptions): Promise<Blob>;
+    compressImage(file: File, options?: import("./types.js").CompressionOptions): Promise<Blob>;
     MIMETYPES_MAP: {
         aac: string;
         abw: string;
@@ -285,6 +285,5 @@ declare const _default: {
 };
 export default _default;
 import * as html from "./html.js";
-import * as file from "./file.js";
 import * as color from "./color.js";
 //# sourceMappingURL=index.d.ts.map

+ 8 - 0
src/types/utils/types.d.ts

@@ -0,0 +1,8 @@
+export type CompressionOptions = {
+    targetSize: number;
+    quality: number;
+    maxWidth: number;
+    maxHeight: number;
+    maxUncompressedSize: number;
+};
+//# sourceMappingURL=types.d.ts.map

+ 10 - 11
src/utils/file.js

@@ -109,14 +109,8 @@ export async function isImageWithAlphaChannel(image_file) {
 }
 
 /**
- * @typedef {Object} CompressionOptions
- * @property {number} targetSize
- * @property {number} quality
- * @property {number} maxWidth
- * @property {number} maxHeight
- *
  * @param {File} file
- * @param {CompressionOptions} options
+ * @param {import('./types').CompressionOptions} options
  * @returns {Promise<Blob>}
  */
 export async function compressImage(
@@ -126,10 +120,15 @@ export async function compressImage(
         quality: 0.75,
         maxWidth: 256,
         maxHeight: 256,
+        maxUncompressedSize: 100, // In KB
     }
 ) {
-    const compress = new Compress(options);
-    const conversions = await compress.compress([file]);
-    const { photo } = conversions[0];
-    return photo.data;
+    if (options.maxUncompressedSize && file.size > options.maxUncompressedSize * 1024) {
+        delete options.maxUncompressedSize;
+        const compress = new Compress(options);
+        const conversions = await compress.compress([file]);
+        const { photo } = conversions[0];
+        return photo.data;
+    }
+    return file;
 }

+ 8 - 0
src/utils/types.ts

@@ -0,0 +1,8 @@
+export type CompressionOptions = {
+    targetSize: number;
+    quality: number;
+    maxWidth: number;
+    maxHeight: number;
+    maxUncompressedSize: number;
+};
+