diff options
Diffstat (limited to 'app/javascript/flavours/glitch/util')
-rw-r--r-- | app/javascript/flavours/glitch/util/compare_id.js | 10 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/util/html.js | 6 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/util/resize_image.js | 116 | ||||
-rw-r--r-- | app/javascript/flavours/glitch/util/stream.js | 29 |
4 files changed, 151 insertions, 10 deletions
diff --git a/app/javascript/flavours/glitch/util/compare_id.js b/app/javascript/flavours/glitch/util/compare_id.js new file mode 100644 index 000000000..aaff66481 --- /dev/null +++ b/app/javascript/flavours/glitch/util/compare_id.js @@ -0,0 +1,10 @@ +export default function compareId(id1, id2) { + if (id1 === id2) { + return 0; + } + if (id1.length === id2.length) { + return id1 > id2 ? 1 : -1; + } else { + return id1.length > id2.length ? 1 : -1; + } +} diff --git a/app/javascript/flavours/glitch/util/html.js b/app/javascript/flavours/glitch/util/html.js new file mode 100644 index 000000000..0b646ce58 --- /dev/null +++ b/app/javascript/flavours/glitch/util/html.js @@ -0,0 +1,6 @@ +export const unescapeHTML = (html) => { + const wrapper = document.createElement('div'); + html = html.replace(/<br \/>|<br>|\n/g, ' '); + wrapper.innerHTML = html; + return wrapper.textContent; +}; diff --git a/app/javascript/flavours/glitch/util/resize_image.js b/app/javascript/flavours/glitch/util/resize_image.js new file mode 100644 index 000000000..279a858ca --- /dev/null +++ b/app/javascript/flavours/glitch/util/resize_image.js @@ -0,0 +1,116 @@ +import EXIF from 'exif-js'; + +const MAX_IMAGE_DIMENSION = 1280; + +const getImageUrl = inputFile => new Promise((resolve, reject) => { + if (window.URL && URL.createObjectURL) { + try { + resolve(URL.createObjectURL(inputFile)); + } catch (error) { + reject(error); + } + return; + } + + const reader = new FileReader(); + reader.onerror = (...args) => reject(...args); + reader.onload = ({ target }) => resolve(target.result); + + reader.readAsDataURL(inputFile); +}); + +const loadImage = inputFile => new Promise((resolve, reject) => { + getImageUrl(inputFile).then(url => { + const img = new Image(); + + img.onerror = (...args) => reject(...args); + img.onload = () => resolve(img); + + img.src = url; + }).catch(reject); +}); + +const getOrientation = (img, type = 'image/png') => new Promise(resolve => { + if (type !== 'image/jpeg') { + resolve(1); + return; + } + + EXIF.getData(img, () => { + const orientation = EXIF.getTag(img, 'Orientation'); + resolve(orientation); + }); +}); + +const processImage = (img, { width, height, orientation, type = 'image/png' }) => new Promise(resolve => { + const canvas = document.createElement('canvas'); + + if (4 < orientation && orientation < 9) { + canvas.width = height; + canvas.height = width; + } else { + canvas.width = width; + canvas.height = height; + } + + const context = canvas.getContext('2d'); + + switch (orientation) { + case 2: context.transform(-1, 0, 0, 1, width, 0); break; + case 3: context.transform(-1, 0, 0, -1, width, height); break; + case 4: context.transform(1, 0, 0, -1, 0, height); break; + case 5: context.transform(0, 1, 1, 0, 0, 0); break; + case 6: context.transform(0, 1, -1, 0, height, 0); break; + case 7: context.transform(0, -1, -1, 0, height, width); break; + case 8: context.transform(0, -1, 1, 0, 0, width); break; + } + + context.drawImage(img, 0, 0, width, height); + + canvas.toBlob(resolve, type); +}); + +const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) => { + const { width, height } = img; + + let newWidth, newHeight; + + if (width > height) { + newHeight = height * MAX_IMAGE_DIMENSION / width; + newWidth = MAX_IMAGE_DIMENSION; + } else if (height > width) { + newWidth = width * MAX_IMAGE_DIMENSION / height; + newHeight = MAX_IMAGE_DIMENSION; + } else { + newWidth = MAX_IMAGE_DIMENSION; + newHeight = MAX_IMAGE_DIMENSION; + } + + getOrientation(img, type) + .then(orientation => processImage(img, { + width: newWidth, + height: newHeight, + orientation, + type, + })) + .then(resolve) + .catch(reject); +}); + +export default inputFile => new Promise((resolve, reject) => { + if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') { + resolve(inputFile); + return; + } + + loadImage(inputFile).then(img => { + if (img.width < MAX_IMAGE_DIMENSION && img.height < MAX_IMAGE_DIMENSION) { + resolve(inputFile); + return; + } + + resizeImage(img, inputFile.type) + .then(resolve) + .catch(() => resolve(inputFile)); + }).catch(reject); +}); diff --git a/app/javascript/flavours/glitch/util/stream.js b/app/javascript/flavours/glitch/util/stream.js index 36c68ffc5..9928d0dd7 100644 --- a/app/javascript/flavours/glitch/util/stream.js +++ b/app/javascript/flavours/glitch/util/stream.js @@ -1,21 +1,24 @@ import WebSocketClient from 'websocket.js'; -export function connectStream(path, pollingRefresh = null, callbacks = () => ({ onConnect() {}, onDisconnect() {}, onReceive() {} })) { +const randomIntUpTo = max => Math.floor(Math.random() * Math.floor(max)); + +export function connectStream(path, pollingRefresh = null, callbacks = () => ({ onDisconnect() {}, onReceive() {} })) { return (dispatch, getState) => { const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']); const accessToken = getState().getIn(['meta', 'access_token']); - const { onConnect, onDisconnect, onReceive } = callbacks(dispatch, getState); + const { onDisconnect, onReceive } = callbacks(dispatch, getState); + let polling = null; const setupPolling = () => { - polling = setInterval(() => { - pollingRefresh(dispatch); - }, 20000); + pollingRefresh(dispatch, () => { + polling = setTimeout(() => setupPolling(), 20000 + randomIntUpTo(20000)); + }); }; const clearPolling = () => { if (polling) { - clearInterval(polling); + clearTimeout(polling); polling = null; } }; @@ -25,13 +28,13 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({ if (pollingRefresh) { clearPolling(); } - onConnect(); }, disconnected () { if (pollingRefresh) { - setupPolling(); + polling = setTimeout(() => setupPolling(), randomIntUpTo(40000)); } + onDisconnect(); }, @@ -44,7 +47,6 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({ clearPolling(); pollingRefresh(dispatch); } - onConnect(); }, }); @@ -53,6 +55,7 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({ if (subscription) { subscription.close(); } + clearPolling(); }; @@ -62,7 +65,13 @@ export function connectStream(path, pollingRefresh = null, callbacks = () => ({ export default function getStream(streamingAPIBaseURL, accessToken, stream, { connected, received, disconnected, reconnected }) { - const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?access_token=${accessToken}&stream=${stream}`); + const params = [ `stream=${stream}` ]; + + if (accessToken !== null) { + params.push(`access_token=${accessToken}`); + } + + const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`); ws.onopen = connected; ws.onmessage = e => received(JSON.parse(e.data)); |