Compare commits
23 commits
04c5699c5a
...
f246611aa2
Author | SHA1 | Date | |
---|---|---|---|
|
f246611aa2 | ||
|
19277f6cd5 | ||
|
2b4052a2c8 | ||
|
60897ebbda | ||
|
84b2a55424 | ||
|
97aaa71bd6 | ||
|
97a944c690 | ||
|
fc10e7cc1a | ||
|
df100989ed | ||
|
626066944e | ||
|
bd45a0d17b | ||
|
e4e6dc30b2 | ||
|
868187479b | ||
|
7586b75e1c | ||
|
24699536c6 | ||
|
a1ddd01291 | ||
|
518ca4a26c | ||
|
5cd7d3cd03 | ||
|
8739bc7002 | ||
|
a2dca331e2 | ||
|
8a40afa174 | ||
|
bc79c1c901 | ||
|
1b14b0cca7 |
8 changed files with 544 additions and 349 deletions
1
changelog.d/upload-resizing.add
Normal file
1
changelog.d/upload-resizing.add
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Resize most kinds of images on upload.
|
1
changelog.d/vue.change
Normal file
1
changelog.d/vue.change
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Upgraded Vue to version 3.5
|
19
package.json
19
package.json
|
@ -30,7 +30,7 @@
|
||||||
"body-scroll-lock": "3.1.5",
|
"body-scroll-lock": "3.1.5",
|
||||||
"chromatism": "3.0.0",
|
"chromatism": "3.0.0",
|
||||||
"click-outside-vue3": "4.0.1",
|
"click-outside-vue3": "4.0.1",
|
||||||
"cropperjs": "1.5.13",
|
"cropperjs": "1.6.2",
|
||||||
"escape-html": "1.0.3",
|
"escape-html": "1.0.3",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"js-cookie": "3.0.5",
|
"js-cookie": "3.0.5",
|
||||||
|
@ -43,10 +43,9 @@
|
||||||
"querystring-es3": "0.2.1",
|
"querystring-es3": "0.2.1",
|
||||||
"url": "0.11.4",
|
"url": "0.11.4",
|
||||||
"utf8": "3.0.0",
|
"utf8": "3.0.0",
|
||||||
"vue": "3.2.45",
|
"vue": "3.5.13",
|
||||||
"vue-i18n": "10",
|
"vue-i18n": "10",
|
||||||
"vue-router": "4.1.6",
|
"vue-router": "4.5.0",
|
||||||
"vue-template-compiler": "2.7.14",
|
|
||||||
"vue-virtual-scroller": "^2.0.0-beta.7",
|
"vue-virtual-scroller": "^2.0.0-beta.7",
|
||||||
"vuex": "4.1.0"
|
"vuex": "4.1.0"
|
||||||
},
|
},
|
||||||
|
@ -60,21 +59,21 @@
|
||||||
"@ungap/event-target": "0.2.4",
|
"@ungap/event-target": "0.2.4",
|
||||||
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
||||||
"@vue/babel-plugin-jsx": "1.2.5",
|
"@vue/babel-plugin-jsx": "1.2.5",
|
||||||
"@vue/compiler-sfc": "3.2.45",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
"@vue/test-utils": "2.2.8",
|
"@vue/test-utils": "2.4.6",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.20",
|
||||||
"babel-loader": "9.1.3",
|
"babel-loader": "9.2.1",
|
||||||
"babel-plugin-lodash": "3.3.4",
|
"babel-plugin-lodash": "3.3.4",
|
||||||
"chai": "4.3.7",
|
"chai": "4.5.0",
|
||||||
"chalk": "1.1.3",
|
"chalk": "1.1.3",
|
||||||
"chromedriver": "108.0.0",
|
"chromedriver": "108.0.0",
|
||||||
"connect-history-api-fallback": "2.0.0",
|
"connect-history-api-fallback": "2.0.0",
|
||||||
"copy-webpack-plugin": "11.0.0",
|
"copy-webpack-plugin": "11.0.0",
|
||||||
"cross-spawn": "7.0.6",
|
"cross-spawn": "7.0.6",
|
||||||
"css-loader": "6.10.0",
|
"css-loader": "6.11.0",
|
||||||
"css-minimizer-webpack-plugin": "4.2.2",
|
"css-minimizer-webpack-plugin": "4.2.2",
|
||||||
"custom-event-polyfill": "1.0.7",
|
"custom-event-polyfill": "1.0.7",
|
||||||
"eslint": "8.33.0",
|
"eslint": "8.57.1",
|
||||||
"eslint-config-standard": "17.0.0",
|
"eslint-config-standard": "17.0.0",
|
||||||
"eslint-formatter-friendly": "7.0.0",
|
"eslint-formatter-friendly": "7.0.0",
|
||||||
"eslint-plugin-import": "2.27.5",
|
"eslint-plugin-import": "2.27.5",
|
||||||
|
|
|
@ -28,7 +28,81 @@ const mediaUpload = {
|
||||||
this.$refs.input.click()
|
this.$refs.input.click()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
uploadFile (file) {
|
async resizeImage (file) {
|
||||||
|
// Skip if not an image or if it's a GIF
|
||||||
|
if (!file.type.startsWith('image/') || file.type === 'image/gif') {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// For PNGs, check if animated
|
||||||
|
if (file.type === 'image/png') {
|
||||||
|
const isAnimated = await this.isAnimatedPng(file)
|
||||||
|
if (isAnimated) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const img = new Image()
|
||||||
|
img.onload = () => {
|
||||||
|
// Calculate new dimensions
|
||||||
|
let width = img.width
|
||||||
|
let height = img.height
|
||||||
|
const maxSize = 2048
|
||||||
|
|
||||||
|
if (width > maxSize || height > maxSize) {
|
||||||
|
if (width > height) {
|
||||||
|
height = Math.round((height * maxSize) / width)
|
||||||
|
width = maxSize
|
||||||
|
} else {
|
||||||
|
width = Math.round((width * maxSize) / height)
|
||||||
|
height = maxSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create canvas and resize
|
||||||
|
const canvas = document.createElement('canvas')
|
||||||
|
canvas.width = width
|
||||||
|
canvas.height = height
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
ctx.drawImage(img, 0, 0, width, height)
|
||||||
|
|
||||||
|
// Check WebP support by trying to create a WebP canvas
|
||||||
|
const testCanvas = document.createElement('canvas')
|
||||||
|
const supportsWebP = testCanvas.toDataURL('image/webp').startsWith('data:image/webp')
|
||||||
|
|
||||||
|
// Convert to WebP if supported, otherwise JPEG
|
||||||
|
const type = supportsWebP ? 'image/webp' : 'image/jpeg'
|
||||||
|
const extension = supportsWebP ? '.webp' : '.jpg'
|
||||||
|
|
||||||
|
// Remove the original extension and add new one
|
||||||
|
const newFileName = file.name.replace(/\.[^/.]+$/, '') + extension
|
||||||
|
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
resolve(new File([blob], newFileName, {
|
||||||
|
type: type,
|
||||||
|
lastModified: Date.now()
|
||||||
|
}))
|
||||||
|
}, type, 0.85)
|
||||||
|
}
|
||||||
|
img.src = URL.createObjectURL(file)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async isAnimatedPng (file) {
|
||||||
|
const buffer = await file.arrayBuffer()
|
||||||
|
const view = new Uint8Array(buffer)
|
||||||
|
// Look for animated PNG chunks (acTL)
|
||||||
|
for (let i = 0; i < view.length - 8; i++) {
|
||||||
|
if (view[i] === 0x61 && // a
|
||||||
|
view[i + 1] === 0x63 && // c
|
||||||
|
view[i + 2] === 0x54 && // T
|
||||||
|
view[i + 3] === 0x4C) { // L
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
async uploadFile (file) {
|
||||||
const self = this
|
const self = this
|
||||||
const store = this.$store
|
const store = this.$store
|
||||||
if (file.size > store.state.instance.uploadlimit) {
|
if (file.size > store.state.instance.uploadlimit) {
|
||||||
|
@ -37,8 +111,11 @@ const mediaUpload = {
|
||||||
self.$emit('upload-failed', 'file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })
|
self.$emit('upload-failed', 'file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resize image if needed
|
||||||
|
const processedFile = await this.resizeImage(file)
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', processedFile)
|
||||||
|
|
||||||
self.$emit('uploading')
|
self.$emit('uploading')
|
||||||
self.uploadCount++
|
self.uploadCount++
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
<pinch-zoom
|
<pinch-zoom
|
||||||
class="pinch-zoom-parent"
|
class="pinch-zoom-parent"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
v-on="$listeners"
|
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</pinch-zoom>
|
</pinch-zoom>
|
||||||
|
|
|
@ -89,7 +89,7 @@ const _verifyPrefs = (state) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const _getRecentData = (cache, live) => {
|
export const _getRecentData = (cache, live, isTest) => {
|
||||||
const result = { recent: null, stale: null, needUpload: false }
|
const result = { recent: null, stale: null, needUpload: false }
|
||||||
const cacheValid = _checkValidity(cache || {})
|
const cacheValid = _checkValidity(cache || {})
|
||||||
const liveValid = _checkValidity(live || {})
|
const liveValid = _checkValidity(live || {})
|
||||||
|
@ -124,6 +124,8 @@ export const _getRecentData = (cache, live) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const merge = (a, b) => ({
|
const merge = (a, b) => ({
|
||||||
|
_version: a._version ?? b._version,
|
||||||
|
_timestamp: a._timestamp ?? b._timestamp,
|
||||||
needUpload: b.needUpload ?? a.needUpload,
|
needUpload: b.needUpload ?? a.needUpload,
|
||||||
prefsStorage: {
|
prefsStorage: {
|
||||||
...a.prefsStorage,
|
...a.prefsStorage,
|
||||||
|
@ -134,8 +136,8 @@ export const _getRecentData = (cache, live) => {
|
||||||
...b.flagStorage
|
...b.flagStorage
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
result.recent = result.recent && merge(defaultState, result.recent)
|
result.recent = isTest ? result.recent : (result.recent && merge(defaultState, result.recent))
|
||||||
result.stale = result.stale && merge(defaultState, result.stale)
|
result.stale = isTest ? result.stale : (result.stale && merge(defaultState, result.stale))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -308,7 +310,7 @@ export const mutations = {
|
||||||
clearServerSideStorage (state, userData) {
|
clearServerSideStorage (state, userData) {
|
||||||
state = { ...cloneDeep(defaultState) }
|
state = { ...cloneDeep(defaultState) }
|
||||||
},
|
},
|
||||||
setServerSideStorage (state, userData) {
|
setServerSideStorage (state, userData, test) {
|
||||||
const live = userData.storage
|
const live = userData.storage
|
||||||
state.raw = live
|
state.raw = live
|
||||||
let cache = state.cache
|
let cache = state.cache
|
||||||
|
|
|
@ -74,7 +74,7 @@ describe('The serverSideStorage module', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should reset local timestamp to remote if contents are the same', () => {
|
it.only('should reset local timestamp to remote if contents are the same', () => {
|
||||||
const state = {
|
const state = {
|
||||||
...cloneDeep(defaultState),
|
...cloneDeep(defaultState),
|
||||||
cache: null
|
cache: null
|
||||||
|
@ -176,33 +176,33 @@ describe('The serverSideStorage module', () => {
|
||||||
})
|
})
|
||||||
describe('_getRecentData', () => {
|
describe('_getRecentData', () => {
|
||||||
it('should handle nulls correctly', () => {
|
it('should handle nulls correctly', () => {
|
||||||
expect(_getRecentData(null, null)).to.eql({ recent: null, stale: null, needUpload: true })
|
expect(_getRecentData(null, null, true)).to.eql({ recent: null, stale: null, needUpload: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('doesn\'t choke on invalid data', () => {
|
it('doesn\'t choke on invalid data', () => {
|
||||||
expect(_getRecentData({ a: 1 }, { b: 2 })).to.eql({ recent: null, stale: null, needUpload: true })
|
expect(_getRecentData({ a: 1 }, { b: 2 }, true)).to.eql({ recent: null, stale: null, needUpload: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should prefer the valid non-null correctly, needUpload works properly', () => {
|
it('should prefer the valid non-null correctly, needUpload works properly', () => {
|
||||||
const nonNull = { _version: VERSION, _timestamp: 1 }
|
const nonNull = { _version: VERSION, _timestamp: 1 }
|
||||||
expect(_getRecentData(nonNull, null)).to.eql({ recent: nonNull, stale: null, needUpload: true })
|
expect(_getRecentData(nonNull, null, true)).to.eql({ recent: nonNull, stale: null, needUpload: true })
|
||||||
expect(_getRecentData(null, nonNull)).to.eql({ recent: nonNull, stale: null, needUpload: false })
|
expect(_getRecentData(null, nonNull, true)).to.eql({ recent: nonNull, stale: null, needUpload: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should prefer the one with higher timestamp', () => {
|
it('should prefer the one with higher timestamp', () => {
|
||||||
const a = { _version: VERSION, _timestamp: 1 }
|
const a = { _version: VERSION, _timestamp: 1 }
|
||||||
const b = { _version: VERSION, _timestamp: 2 }
|
const b = { _version: VERSION, _timestamp: 2 }
|
||||||
|
|
||||||
expect(_getRecentData(a, b)).to.eql({ recent: b, stale: a, needUpload: false })
|
expect(_getRecentData(a, b, true)).to.eql({ recent: b, stale: a, needUpload: false })
|
||||||
expect(_getRecentData(b, a)).to.eql({ recent: b, stale: a, needUpload: false })
|
expect(_getRecentData(b, a, true)).to.eql({ recent: b, stale: a, needUpload: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('case where both are same', () => {
|
it('case where both are same', () => {
|
||||||
const a = { _version: VERSION, _timestamp: 3 }
|
const a = { _version: VERSION, _timestamp: 3 }
|
||||||
const b = { _version: VERSION, _timestamp: 3 }
|
const b = { _version: VERSION, _timestamp: 3 }
|
||||||
|
|
||||||
expect(_getRecentData(a, b)).to.eql({ recent: b, stale: a, needUpload: false })
|
expect(_getRecentData(a, b, true)).to.eql({ recent: b, stale: a, needUpload: false })
|
||||||
expect(_getRecentData(b, a)).to.eql({ recent: b, stale: a, needUpload: false })
|
expect(_getRecentData(b, a, true)).to.eql({ recent: b, stale: a, needUpload: false })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue