about summary refs log tree commit diff
path: root/src/view/com/lightbox/ImageViewing/utils.ts
diff options
context:
space:
mode:
authorAryan Goharzad <arrygoo@gmail.com>2023-01-25 18:25:34 -0500
committerGitHub <noreply@github.com>2023-01-25 17:25:34 -0600
commiteb33c3fa812cc087db14a6b6ba743e982b26c462 (patch)
treed098f7a804c67755f39e95bbbfd56887bacf476c /src/view/com/lightbox/ImageViewing/utils.ts
parentadf328b50ce98c5ebd3282fe897ddfdcd0de8011 (diff)
downloadvoidsky-eb33c3fa812cc087db14a6b6ba743e982b26c462.tar.zst
Saves image on long press (#83)
* Saves image on long press

* Adds save on long press

* Forking lightbox

* move to wrapper only to the bottom sheet to reduce impact of this change

* lint

* lint

* lint

* Use official `share` API

* Clean up cache after download

* comment

* comment

* Reduce swipe close velocity

* Updates per feedback

* lint

* bugfix

* Adds delayed press-in for TouchableOpacity
Diffstat (limited to 'src/view/com/lightbox/ImageViewing/utils.ts')
-rw-r--r--src/view/com/lightbox/ImageViewing/utils.ts179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/view/com/lightbox/ImageViewing/utils.ts b/src/view/com/lightbox/ImageViewing/utils.ts
new file mode 100644
index 000000000..7fcdc84cf
--- /dev/null
+++ b/src/view/com/lightbox/ImageViewing/utils.ts
@@ -0,0 +1,179 @@
+/**
+ * Copyright (c) JOB TODAY S.A. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+import {
+  Animated,
+  GestureResponderEvent,
+  PanResponder,
+  PanResponderGestureState,
+  PanResponderInstance,
+  NativeTouchEvent,
+} from 'react-native'
+import {Dimensions, Position} from './@types'
+
+type CacheStorageItem = {key: string; value: any}
+
+export const createCache = (cacheSize: number) => ({
+  _storage: [] as CacheStorageItem[],
+  get(key: string): any {
+    const {value} =
+      this._storage.find(({key: storageKey}) => storageKey === key) || {}
+
+    return value
+  },
+  set(key: string, value: any) {
+    if (this._storage.length >= cacheSize) {
+      this._storage.shift()
+    }
+
+    this._storage.push({key, value})
+  },
+})
+
+export const splitArrayIntoBatches = (arr: any[], batchSize: number): any[] =>
+  arr.reduce((result, item) => {
+    const batch = result.pop() || []
+
+    if (batch.length < batchSize) {
+      batch.push(item)
+      result.push(batch)
+    } else {
+      result.push(batch, [item])
+    }
+
+    return result
+  }, [])
+
+export const getImageTransform = (
+  image: Dimensions | null,
+  screen: Dimensions,
+) => {
+  if (!image?.width || !image?.height) {
+    return [] as const
+  }
+
+  const wScale = screen.width / image.width
+  const hScale = screen.height / image.height
+  const scale = Math.min(wScale, hScale)
+  const {x, y} = getImageTranslate(image, screen)
+
+  return [{x, y}, scale] as const
+}
+
+export const getImageStyles = (
+  image: Dimensions | null,
+  translate: Animated.ValueXY,
+  scale?: Animated.Value,
+) => {
+  if (!image?.width || !image?.height) {
+    return {width: 0, height: 0}
+  }
+
+  const transform = translate.getTranslateTransform()
+
+  if (scale) {
+    transform.push({scale}, {perspective: new Animated.Value(1000)})
+  }
+
+  return {
+    width: image.width,
+    height: image.height,
+    transform,
+  }
+}
+
+export const getImageTranslate = (
+  image: Dimensions,
+  screen: Dimensions,
+): Position => {
+  const getTranslateForAxis = (axis: 'x' | 'y'): number => {
+    const imageSize = axis === 'x' ? image.width : image.height
+    const screenSize = axis === 'x' ? screen.width : screen.height
+
+    return (screenSize - imageSize) / 2
+  }
+
+  return {
+    x: getTranslateForAxis('x'),
+    y: getTranslateForAxis('y'),
+  }
+}
+
+export const getImageDimensionsByTranslate = (
+  translate: Position,
+  screen: Dimensions,
+): Dimensions => ({
+  width: screen.width - translate.x * 2,
+  height: screen.height - translate.y * 2,
+})
+
+export const getImageTranslateForScale = (
+  currentTranslate: Position,
+  targetScale: number,
+  screen: Dimensions,
+): Position => {
+  const {width, height} = getImageDimensionsByTranslate(
+    currentTranslate,
+    screen,
+  )
+
+  const targetImageDimensions = {
+    width: width * targetScale,
+    height: height * targetScale,
+  }
+
+  return getImageTranslate(targetImageDimensions, screen)
+}
+
+type HandlerType = (
+  event: GestureResponderEvent,
+  state: PanResponderGestureState,
+) => void
+
+type PanResponderProps = {
+  onGrant: HandlerType
+  onStart?: HandlerType
+  onMove: HandlerType
+  onRelease?: HandlerType
+  onTerminate?: HandlerType
+}
+
+export const createPanResponder = ({
+  onGrant,
+  onStart,
+  onMove,
+  onRelease,
+  onTerminate,
+}: PanResponderProps): PanResponderInstance =>
+  PanResponder.create({
+    onStartShouldSetPanResponder: () => true,
+    onStartShouldSetPanResponderCapture: () => true,
+    onMoveShouldSetPanResponder: () => true,
+    onMoveShouldSetPanResponderCapture: () => true,
+    onPanResponderGrant: onGrant,
+    onPanResponderStart: onStart,
+    onPanResponderMove: onMove,
+    onPanResponderRelease: onRelease,
+    onPanResponderTerminate: onTerminate,
+    onPanResponderTerminationRequest: () => false,
+    onShouldBlockNativeResponder: () => false,
+  })
+
+export const getDistanceBetweenTouches = (
+  touches: NativeTouchEvent[],
+): number => {
+  const [a, b] = touches
+
+  if (a == null || b == null) {
+    return 0
+  }
+
+  return Math.sqrt(
+    Math.pow(a.pageX - b.pageX, 2) + Math.pow(a.pageY - b.pageY, 2),
+  )
+}