function avatarUpload(formAction, redirectTo) { return { tab: 'emoji', showModal: false, previewUrl: null, croppedBlob: null, cropper: null, openFile(e) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (ev) => { this.previewUrl = ev.target.result; this.showModal = true; this.$nextTick(() => this._initCropper()); }; reader.readAsDataURL(file); // Clear the input so re-selecting the same file triggers change again e.target.value = ''; }, _initCropper() { if (this.cropper) { this.cropper.destroy(); this.cropper = null; } const img = this.$refs.cropImg; this.cropper = new Cropper(img, { aspectRatio: 1, viewMode: 1, dragMode: 'move', autoCropArea: 1, cropBoxMovable: false, cropBoxResizable: false, toggleDragModeOnDblclick: false, background: false, }); }, confirmCrop() { const canvas = this.cropper.getCroppedCanvas({ width: 256, height: 256 }); canvas.toBlob((blob) => { this.croppedBlob = blob; this.previewUrl = canvas.toDataURL('image/jpeg'); this.showModal = false; this.cropper.destroy(); this.cropper = null; }, 'image/jpeg', 0.88); }, cancelCrop() { this.showModal = false; if (this.cropper) { this.cropper.destroy(); this.cropper = null; } if (!this.croppedBlob) this.previewUrl = null; }, async submitForm(e) { e.preventDefault(); const form = e.target; const fd = new FormData(form); if (this.tab === 'upload' && this.croppedBlob) { fd.delete('avatar_file'); fd.set('avatar_file', this.croppedBlob, 'avatar.jpg'); fd.set('avatar_emoji', ''); } else if (this.tab === 'emoji') { fd.delete('avatar_file'); } const resp = await fetch(formAction, { method: 'POST', body: fd, redirect: 'follow' }); window.location.href = resp.url || redirectTo; }, }; }