diff --git a/go/chat/s3/multi.go b/go/chat/s3/multi.go
index 2fd68efa6730..5d4f74fd29dd 100644
--- a/go/chat/s3/multi.go
+++ b/go/chat/s3/multi.go
@@ -57,11 +57,14 @@ func (b *Bucket) ListMulti(ctx context.Context, prefix, delim string) (multis []
"prefix": {prefix},
"delimiter": {delim},
}
+ headers := map[string][]string{}
+ b.addTokenHeader(headers)
for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
req := &request{
- method: "GET",
- bucket: b.Name,
- params: params,
+ method: "GET",
+ bucket: b.Name,
+ params: params,
+ headers: headers,
}
var resp listMultiResp
err := b.S3.query(ctx, req, &resp)
@@ -121,6 +124,7 @@ func (b *Bucket) InitMulti(ctx context.Context, key string, contType string, per
"Content-Length": {"0"},
"x-amz-acl": {string(perm)},
}
+ b.addTokenHeader(headers)
params := map[string][]string{
"uploads": {""},
}
@@ -164,6 +168,7 @@ func (m *Multi) putPart(ctx context.Context, n int, r io.ReadSeeker, partSize in
"Content-Length": {strconv.FormatInt(partSize, 10)},
"Content-MD5": {md5b64},
}
+ m.Bucket.addTokenHeader(headers)
params := map[string][]string{
"uploadId": {m.UploadID},
"partNumber": {strconv.FormatInt(int64(n), 10)},
@@ -247,13 +252,17 @@ func (m *Multi) ListParts(ctx context.Context) ([]Part, error) {
"uploadId": {m.UploadID},
"max-parts": {strconv.FormatInt(int64(listPartsMax), 10)},
}
+ headers := map[string][]string{}
+ m.Bucket.addTokenHeader(headers)
+
var parts partSlice
for attempt := m.Bucket.S3.AttemptStrategy.Start(); attempt.Next(); {
req := &request{
- method: "GET",
- bucket: m.Bucket.Name,
- path: m.Key,
- params: params,
+ method: "GET",
+ bucket: m.Bucket.Name,
+ path: m.Key,
+ params: params,
+ headers: headers,
}
var resp listPartsResp
err := m.Bucket.S3.query(ctx, req, &resp)
@@ -383,15 +392,17 @@ func (m *Multi) Complete(ctx context.Context, parts []Part) error {
// Setting Content-Length prevents breakage on DreamObjects
for attempt := m.Bucket.S3.AttemptStrategy.Start(); attempt.Next(); {
+ headers := map[string][]string{
+ "Content-Length": {strconv.Itoa(len(data))},
+ }
+ m.Bucket.addTokenHeader(headers)
req := &request{
method: "POST",
bucket: m.Bucket.Name,
path: m.Key,
params: params,
payload: bytes.NewReader(data),
- headers: map[string][]string{
- "Content-Length": {strconv.Itoa(len(data))},
- },
+ headers: headers,
}
resp := &completeResponse{}
@@ -432,12 +443,16 @@ func (m *Multi) Abort(ctx context.Context) error {
params := map[string][]string{
"uploadId": {m.UploadID},
}
+ headers := map[string][]string{}
+ m.Bucket.addTokenHeader(headers)
+
for attempt := m.Bucket.S3.AttemptStrategy.Start(); attempt.Next(); {
req := &request{
- method: "DELETE",
- bucket: m.Bucket.Name,
- path: m.Key,
- params: params,
+ method: "DELETE",
+ bucket: m.Bucket.Name,
+ path: m.Key,
+ params: params,
+ headers: headers,
}
err := m.Bucket.S3.query(ctx, req, nil)
if shouldRetry(err) && attempt.HasNext() {
diff --git a/go/chat/s3/s3.go b/go/chat/s3/s3.go
index ece5cb31486e..032f2e23caa6 100644
--- a/go/chat/s3/s3.go
+++ b/go/chat/s3/s3.go
@@ -225,10 +225,14 @@ func (b *Bucket) PutBucket(ctx context.Context, perm ACL) error {
//
// See http://goo.gl/GoBrY for details.
func (b *Bucket) DelBucket() (err error) {
+ headers := map[string][]string{}
+ b.addTokenHeader(headers)
+
req := &request{
- method: "DELETE",
- bucket: b.Name,
- path: "/",
+ method: "DELETE",
+ bucket: b.Name,
+ path: "/",
+ headers: headers,
}
for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
err = b.S3.query(context.Background(), req, nil)
@@ -321,10 +325,14 @@ func (b *Bucket) GetResponseWithHeaders(ctx context.Context, path string, header
// Exists checks whether or not an object exists on an S3 bucket using a HEAD request.
func (b *Bucket) Exists(path string) (exists bool, err error) {
+ headers := map[string][]string{}
+ b.addTokenHeader(headers)
+
req := &request{
- method: "HEAD",
- bucket: b.Name,
- path: path,
+ method: "HEAD",
+ bucket: b.Name,
+ path: path,
+ headers: headers,
}
err = b.S3.prepare(req)
if err != nil {
@@ -543,6 +551,7 @@ func (b *Bucket) PutBucketSubresource(subresource string, r io.Reader, length in
headers := map[string][]string{
"Content-Length": {strconv.FormatInt(length, 10)},
}
+ b.addTokenHeader(headers)
req := &request{
path: "/",
method: "PUT",
@@ -559,6 +568,9 @@ func (b *Bucket) PutBucketSubresource(subresource string, r io.Reader, length in
//
// See http://goo.gl/APeTt for details.
func (b *Bucket) Del(ctx context.Context, path string) error {
+ headers := map[string][]string{}
+ b.addTokenHeader(headers)
+
req := &request{
method: "DELETE",
bucket: b.Name,
@@ -598,6 +610,8 @@ func (b *Bucket) DelMulti(objects Delete) error {
"Content-MD5": {base64.StdEncoding.EncodeToString(digest.Sum(nil))},
"Content-Type": {"text/xml"},
}
+ b.addTokenHeader(headers)
+
req := &request{
path: "/",
method: "POST",
@@ -705,9 +719,13 @@ func (b *Bucket) List(prefix, delim, marker string, max int) (result *ListResp,
if max != 0 {
params["max-keys"] = []string{strconv.FormatInt(int64(max), 10)}
}
+ headers := map[string][]string{}
+ b.addTokenHeader(headers)
+
req := &request{
- bucket: b.Name,
- params: params,
+ bucket: b.Name,
+ params: params,
+ headers: headers,
}
result = &ListResp{}
for attempt := b.S3.AttemptStrategy.Start(); attempt.Next(); {
diff --git a/go/libkb/version.go b/go/libkb/version.go
index 9528c9354ba4..cc49f30461c5 100644
--- a/go/libkb/version.go
+++ b/go/libkb/version.go
@@ -4,4 +4,4 @@
package libkb
// Version is the current version (should be MAJOR.MINOR.PATCH)
-const Version = "6.3.2"
+const Version = "6.4.0"
diff --git a/shared/android/app/build.gradle b/shared/android/app/build.gradle
index 28570961a1df..5694bda25ae6 100644
--- a/shared/android/app/build.gradle
+++ b/shared/android/app/build.gradle
@@ -3,7 +3,7 @@ apply plugin: "com.facebook.react"
apply plugin: 'com.github.triplet.play'
// KB: app version
-def VERSION_NAME = "6.3.2"
+def VERSION_NAME = "6.4.0"
// KB: Number of commits, like ios
Integer getVersionCode() {
diff --git a/shared/common-adapters/image2.d.ts b/shared/common-adapters/image2.d.ts
index 1c74414e1dd4..4226fcc4d059 100644
--- a/shared/common-adapters/image2.d.ts
+++ b/shared/common-adapters/image2.d.ts
@@ -8,7 +8,6 @@ export type Props = {
showLoadingStateUntilLoaded?: boolean
onLoad?: (e: {target?: unknown; source?: {width: number; height: number}}) => void
onError?: () => void
- allowDownscaling?: boolean
}
declare const Image2: (p: Props) => React.ReactNode
diff --git a/shared/common-adapters/image2.native.tsx b/shared/common-adapters/image2.native.tsx
index 32e3a76c6c73..698cac9eaa2c 100644
--- a/shared/common-adapters/image2.native.tsx
+++ b/shared/common-adapters/image2.native.tsx
@@ -4,15 +4,7 @@ import type {Props} from './image2'
import {Image, type ImageLoadEventData} from 'expo-image'
const Image2 = (p: Props) => {
- const {
- showLoadingStateUntilLoaded,
- src,
- onLoad,
- onError,
- style,
- contentFit = 'contain',
- allowDownscaling,
- } = p
+ const {showLoadingStateUntilLoaded, src, onLoad, onError, style, contentFit = 'contain'} = p
// if we don't have showLoadingStateUntilLoaded then just mark as loaded and ignore this state
const [loading, setLoading] = React.useState(!showLoadingStateUntilLoaded)
const [lastSrc, setLastSrc] = React.useState(src)
@@ -40,14 +32,7 @@ const Image2 = (p: Props) => {
return (
<>
-
+
{showLoadingStateUntilLoaded && loading ? : null}
>
)
diff --git a/shared/common-adapters/zoomable-box.android.tsx b/shared/common-adapters/zoomable-box.android.tsx
index 81f3a2fa69b0..20027edad306 100644
--- a/shared/common-adapters/zoomable-box.android.tsx
+++ b/shared/common-adapters/zoomable-box.android.tsx
@@ -6,9 +6,9 @@ import Animated, {
useAnimatedReaction,
useDerivedValue,
withTiming,
+ withSpring,
cancelAnimation,
runOnJS,
- withDecay,
} from 'react-native-reanimated'
import {View, type LayoutChangeEvent} from 'react-native'
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
@@ -18,7 +18,7 @@ const needDiff = Styles.dimensionWidth / 3
// mostly based on https://github.com/intergalacticspacehighway/react-native-reanimated-zoom
export function ZoomableBox(props: Props) {
- const {children, minZoom = 1, maxZoom = 10, style, zoomScale} = props
+ const {children, minZoom = 1, maxZoom = 10, style} = props
const {onZoom, contentContainerStyle, onLayout: _onLayout, onSwipe, onTap: _onTap} = props
const onTap = React.useCallback(() => {
@@ -27,11 +27,9 @@ export function ZoomableBox(props: Props) {
const translationX = useSharedValue(0)
const translationY = useSharedValue(0)
- const velocityX = useSharedValue(0)
- const velocityY = useSharedValue(0)
const originX = useSharedValue(0)
const originY = useSharedValue(0)
- const scale = useSharedValue(zoomScale ?? 1)
+ const scale = useSharedValue(1)
const isPinching = useSharedValue(false)
const isZoomed = useSharedValue(false)
const viewHeight = useSharedValue(0)
@@ -45,24 +43,17 @@ export function ZoomableBox(props: Props) {
const panTranslateX = useSharedValue(0)
const panTranslateY = useSharedValue(0)
const panSwipedCounter = useSharedValue(0)
- const panFingers = useSharedValue(0)
const containerWidth = useSharedValue(0)
const containerHeight = useSharedValue(0)
- const lastZoomScaleRef = React.useRef()
- if (lastZoomScaleRef.current !== zoomScale && zoomScale !== undefined) {
- lastZoomScaleRef.current = zoomScale
- scale.value = lastZoomScaleRef.current
- }
-
const gesture = React.useMemo(() => {
const resetZoomState = () => {
'worklet'
// reset all state
- translationX.value = 0
- translationY.value = 0
- scale.value = withTiming(zoomScale ?? 1)
+ translationX.value = withTiming(0)
+ translationY.value = withTiming(0)
+ scale.value = withTiming(1)
originX.value = 0
originY.value = 0
isPinching.value = false
@@ -75,40 +66,53 @@ export function ZoomableBox(props: Props) {
// we only activate pan handler when the image is zoomed or user is not pinching
const pan = Gesture.Pan()
- .averageTouches(true)
- .onStart(e => {
- if (!isZoomed.value) {
- return
- }
+ .onStart(() => {
+ if (isPinching.value || !isZoomed.value) return
+
cancelAnimation(translationX)
cancelAnimation(translationY)
cancelAnimation(scale)
prevTranslationX.value = translationX.value
prevTranslationY.value = translationY.value
- panFingers.value = e.numberOfPointers
})
.onUpdate(e => {
- // if we're done panning ignore us letting go
- if (e.numberOfPointers < panFingers.value) {
- return
- }
- panFingers.value = e.numberOfPointers
- if (!isZoomed.value) {
+ if (isPinching.value || !isZoomed.value) {
panTranslateX.value = e.translationX
panTranslateY.value = e.translationY
} else {
- translationX.value = prevTranslationX.value + e.translationX
- translationY.value = prevTranslationY.value + e.translationY
- velocityX.value = e.velocityX
- velocityY.value = e.velocityY
+ // imagine what happens to pixels when we zoom in. (they get multiplied by x times scale)
+ const maxTranslateX = (viewWidth.value / 2) * scale.value - viewWidth.value / 2
+ const minTranslateX = -maxTranslateX
+
+ const maxTranslateY = (viewHeight.value / 2) * scale.value - viewHeight.value / 2
+ const minTranslateY = -maxTranslateY
+
+ const nextTranslateX = prevTranslationX.value + e.translationX - panTranslateX.value
+ const nextTranslateY = prevTranslationY.value + e.translationY - panTranslateY.value
+
+ if (nextTranslateX > maxTranslateX) {
+ translationX.value = maxTranslateX
+ } else if (nextTranslateX < minTranslateX) {
+ translationX.value = minTranslateX
+ } else {
+ translationX.value = nextTranslateX
+ }
+
+ if (nextTranslateY > maxTranslateY) {
+ translationY.value = maxTranslateY
+ } else if (nextTranslateY < minTranslateY) {
+ translationY.value = minTranslateY
+ } else {
+ translationY.value = nextTranslateY
+ }
}
})
- .onEnd(e => {
+ .onEnd(() => {
panSwipedCounter.value++
- panFingers.value = 0
- translationX.value = withDecay({deceleration: 0.9, velocity: e.velocityX})
- translationY.value = withDecay({deceleration: 0.9, velocity: e.velocityY})
+ if (isPinching.value || !isZoomed.value) return
+ panTranslateX.value = 0
+ panTranslateY.value = 0
})
const pinch = Gesture.Pinch()
@@ -120,33 +124,48 @@ export function ZoomableBox(props: Props) {
offsetScale.value = scale.value
})
.onUpdate(e => {
- const newScale = prevScale.value * e.scale
- if (newScale < minZoom || newScale > maxZoom) return
-
- scale.value = prevScale.value * e.scale
-
- // maybe this is always true... but not changing this now
- // eslint-disable-next-line
- if (isPinching.value) {
- // translate the image to the focal point as we're zooming
- translationX.value =
- prevTranslationX.value +
- -1 * ((scale.value - offsetScale.value) * (originX.value - viewWidth.value / 2))
- translationY.value =
- prevTranslationY.value +
- -1 * ((scale.value - offsetScale.value) * (originY.value - viewHeight.value / 2))
+ // when pointer is 1 we don't want to translate origin
+ if (e.numberOfPointers === 1 && isPinching.value) {
+ prevTranslationX.value = translationX.value
+ prevTranslationY.value = translationY.value
+ isPinching.value = false
+ } else if (e.numberOfPointers === 2) {
+ const newScale = prevScale.value * e.scale
+
+ if (newScale < minZoom || newScale > maxZoom) return
+
+ scale.value = prevScale.value * e.scale
+
+ // reset the origin
+ if (!isPinching.value) {
+ isPinching.value = true
+ originX.value = e.focalX
+ originY.value = e.focalY
+ prevTranslationX.value = translationX.value
+ prevTranslationY.value = translationY.value
+ offsetScale.value = scale.value
+ }
+
+ // maybe this is always true... but not changing this now
+ // eslint-disable-next-line
+ if (isPinching.value) {
+ // translate the image to the focal point as we're zooming
+ translationX.value =
+ prevTranslationX.value +
+ -1 * ((scale.value - offsetScale.value) * (originX.value - viewWidth.value / 2))
+ translationY.value =
+ prevTranslationY.value +
+ -1 * ((scale.value - offsetScale.value) * (originY.value - viewHeight.value / 2))
+ }
}
})
.onEnd(() => {
isPinching.value = false
prevTranslationX.value = translationX.value
prevTranslationY.value = translationY.value
- // stop pan
- panFingers.value = 3
- if (zoomScale !== undefined) {
- if (scale.value < zoomScale) {
- resetZoomState()
- }
+
+ if (scale.value < 1.1) {
+ resetZoomState()
}
})
@@ -161,7 +180,7 @@ export function ZoomableBox(props: Props) {
.numberOfTaps(2)
.onStart(e => {
// if zoomed in or zoomed out, we want to reset
- if (isZoomed.value) {
+ if (scale.value !== 1) {
resetZoomState()
} else {
const zoom = 3
@@ -194,15 +213,15 @@ export function ZoomableBox(props: Props) {
translationX,
translationY,
panSwipedCounter,
- panFingers,
- zoomScale,
- velocityX,
- velocityY,
])
useDerivedValue(() => {
- isZoomed.value = scale.value !== zoomScale
- }, [zoomScale])
+ if (scale.value > 1 && !isZoomed.value) {
+ isZoomed.value = true
+ } else if (scale.value === 1 && isZoomed.value) {
+ isZoomed.value = false
+ }
+ }, [])
const updateOnZoom = React.useCallback(
(scale: number, px: number, py: number) => {
@@ -210,20 +229,14 @@ export function ZoomableBox(props: Props) {
const width = scale * viewWidth.value
const x = width / 2 - px - containerWidth.value / 2
const y = height / 2 - py - containerHeight.value / 2
- onZoom?.({
- height,
- scale,
- width,
- x,
- y,
- })
+ onZoom?.({height, scale, width, x, y})
},
[onZoom, viewHeight, viewWidth, containerHeight, containerWidth]
)
useDerivedValue(() => {
runOnJS(updateOnZoom)(scale.value, translationX.value, translationY.value)
- }, [updateOnZoom])
+ }, [])
const lastPanSwipedCounter = useSharedValue(0)
useAnimatedReaction(
@@ -246,22 +259,10 @@ export function ZoomableBox(props: Props) {
const as = useAnimatedStyle(() => {
return {
- position: 'absolute',
transform: [
- // pan
- {translateX: translationX.value},
- {translateY: translationY.value},
- // center again
- {translateX: containerWidth.value / 2 - (viewWidth.value * scale.value) / 2},
- {translateY: containerHeight.value / 2 - (viewHeight.value * scale.value) / 2},
- // Move to center
- {translateX: -viewWidth.value / 2},
- {translateY: -viewHeight.value / 2},
- // Apply scale
+ {translateX: withSpring(translationX.value, {damping: 2000, stiffness: 1000})},
+ {translateY: withSpring(translationY.value, {damping: 2000, stiffness: 1000})},
{scale: scale.value},
- // Move back to upper left
- {translateX: viewWidth.value / 2},
- {translateY: viewHeight.value / 2},
],
}
}, [])
@@ -283,6 +284,7 @@ export function ZoomableBox(props: Props) {
)
const memoizedStyle = React.useMemo(() => [as, contentContainerStyle], [as, contentContainerStyle])
+
return (
diff --git a/shared/common-adapters/zoomable-box.d.ts b/shared/common-adapters/zoomable-box.d.ts
index ccef9a476c81..b03444dbb669 100644
--- a/shared/common-adapters/zoomable-box.d.ts
+++ b/shared/common-adapters/zoomable-box.d.ts
@@ -6,12 +6,6 @@ export type Props = {
maxZoom?: number
minZoom?: number
zoomScale?: number
- contentSize?:
- | {
- width: number
- height: number
- }
- | undefined
onLayout?: (e: Partial) => void
onZoom?: (e: {height: number; width: number; x: number; y: number; scale: number}) => void
style?: Styles.StylesCrossPlatform
diff --git a/shared/common-adapters/zoomable-box.ios.tsx b/shared/common-adapters/zoomable-box.ios.tsx
index ea77e0bd8992..2c17ebb55de2 100644
--- a/shared/common-adapters/zoomable-box.ios.tsx
+++ b/shared/common-adapters/zoomable-box.ios.tsx
@@ -8,7 +8,7 @@ import {runOnJS} from 'react-native-reanimated'
const Kb = {ScrollView}
export const ZoomableBox = (props: Props) => {
- const {onSwipe, onLayout, onTap: _onTap, zoomScale, contentSize, children, contentContainerStyle} = props
+ const {onSwipe, onLayout, onTap: _onTap} = props
const {width: windowWidth} = useWindowDimensions()
const needDiff = windowWidth / 3
const onTap = React.useCallback(() => {
@@ -37,7 +37,7 @@ export const ZoomableBox = (props: Props) => {
if (maxTouches !== 1) {
return
}
- const scaleDiff = Math.abs((zoomScale ?? 1) - curScaleRef.current)
+ const scaleDiff = Math.abs(1 - curScaleRef.current)
if (scaleDiff > 0.1) {
return
}
@@ -47,7 +47,7 @@ export const ZoomableBox = (props: Props) => {
onSwipe?.(true)
}
},
- [onSwipe, needDiff, zoomScale]
+ [onSwipe, needDiff]
)
const widthRef = React.useRef(0)
@@ -62,8 +62,7 @@ export const ZoomableBox = (props: Props) => {
)
const ref = React.useRef(null)
-
- const getScroll = React.useCallback(() => {
+ const onDoubleTap = React.useCallback(() => {
const scroll = ref.current as unknown as null | {
getScrollResponder?:
| undefined
@@ -79,38 +78,23 @@ export const ZoomableBox = (props: Props) => {
}) => void
})
}
- return scroll
+ scroll?.getScrollResponder?.()?.scrollResponderZoomTo(
+ curScaleRef.current > 1.01
+ ? {
+ animated: true,
+ height: 2000,
+ width: 2000,
+ }
+ : {
+ animated: true,
+ height: heightRef.current / 4,
+ width: widthRef.current / 4,
+ x: widthRef.current / 4,
+ y: heightRef.current / 4,
+ }
+ )
}, [])
- const onResetZoom = React.useCallback(() => {
- if (!contentSize) return
- const scroll = getScroll()
- scroll?.getScrollResponder?.()?.scrollResponderZoomTo({
- animated: true,
- height: contentSize.height,
- width: contentSize.width,
- x: 0,
- y: 0,
- })
- }, [contentSize, getScroll])
-
- const onDoubleTap = React.useCallback(() => {
- const zoomOut = curScaleRef.current > (zoomScale ?? 1)
- if (zoomOut) {
- onResetZoom()
- } else {
- const scroll = getScroll()
- scroll?.getScrollResponder?.()?.scrollResponderZoomTo({
- animated: true,
- height: (contentSize?.height ?? 100) / 4,
- width: (contentSize?.width ?? 100) / 4,
- // not correct
- x: ((contentSize?.width ?? 100) - widthRef.current) / 2,
- y: ((contentSize?.height ?? 100) - heightRef.current) / 2,
- })
- }
- }, [contentSize, zoomScale, onResetZoom, getScroll])
-
const singleTap = Gesture.Tap()
.maxDuration(250)
.numberOfTaps(1)
@@ -127,11 +111,6 @@ export const ZoomableBox = (props: Props) => {
})
const taps = Gesture.Exclusive(doubleTap, singleTap)
- // this is nasty but i need a way to force this component to render correctly. Passing new zoomScale
- // won't necessarily cause it to adopt it
- const old = zoomScale ?? 1
- const fixedZoomScale = old === 1 ? old : old + Math.random() * 0.0001
-
return (
{
centerContent={true}
alwaysBounceVertical={false}
bounces={props.bounces}
- children={children}
- contentContainerStyle={contentContainerStyle}
+ children={props.children}
+ contentContainerStyle={props.contentContainerStyle}
indicatorStyle="white"
maximumZoomScale={props.maxZoom || 10}
minimumZoomScale={props.minZoom || 1}
@@ -149,21 +128,19 @@ export const ZoomableBox = (props: Props) => {
onTouchEnd={onSwipe ? onTouchEnd : undefined}
onScroll={e => {
curScaleRef.current = e.nativeEvent?.zoomScale ?? 0
- const val = {
+ props.onZoom?.({
height: e.nativeEvent?.contentSize.height ?? 0,
scale: e.nativeEvent?.zoomScale ?? 0,
width: e.nativeEvent?.contentSize.width ?? 0,
x: e.nativeEvent?.contentOffset.x ?? 0,
y: e.nativeEvent?.contentOffset.y ?? 0,
- }
- props.onZoom?.(val)
+ })
}}
scrollEventThrottle={16}
scrollsToTop={false}
showsHorizontalScrollIndicator={props.showsHorizontalScrollIndicator}
showsVerticalScrollIndicator={props.showsVerticalScrollIndicator}
style={props.style}
- zoomScale={fixedZoomScale}
/>
)
diff --git a/shared/common-adapters/zoomable-image.native.tsx b/shared/common-adapters/zoomable-image.native.tsx
index bf559f75df02..04e86ded1e63 100644
--- a/shared/common-adapters/zoomable-image.native.tsx
+++ b/shared/common-adapters/zoomable-image.native.tsx
@@ -1,9 +1,9 @@
import * as React from 'react'
import * as Styles from '@/styles'
-import {type LayoutChangeEvent} from 'react-native'
import {ZoomableBox} from './zoomable-box'
import Image2 from './image2.native'
import type {Props} from './zoomable-image'
+import {type LayoutChangeEvent} from 'react-native'
import ProgressIndicator from './progress-indicator.native'
import {Box2} from './box'
@@ -14,120 +14,70 @@ const Kb = {
ZoomableBox,
}
-const dummySize = {height: 1, width: 1}
-
const ZoomableImage = (p: Props) => {
const {src, style, onChanged, onLoaded, onSwipe, onTap, onError} = p
+ const onZoom = onChanged
const [boxW, setBoxW] = React.useState(0)
const [boxH, setBoxH] = React.useState(0)
const [loading, setLoading] = React.useState(true)
const [lastSrc, setLastSrc] = React.useState(src)
- const [size, setSize] = React.useState(undefined)
- const [scale, setScale] = React.useState(1)
-
- const onZoom = onChanged
-
- const onLayout = React.useCallback((e: Partial) => {
- if (!e.nativeEvent) return
- const {width, height} = e.nativeEvent.layout
- setBoxW(width)
- setBoxH(height)
- }, [])
-
const onLoad = React.useCallback(
(e: {source?: {width: number; height: number}}) => {
- if (!e.source) {
- return
- }
- setSize(e.source)
+ if (!e.source) return
+ setLoading(false)
onLoaded?.()
},
[onLoaded]
)
- const initialZoomRef = React.useRef(false)
- React.useEffect(() => {
- if (initialZoomRef.current || !size || !boxW || !boxH) {
- return
- }
- initialZoomRef.current = true
- const sizeRatio = size.width / size.height
- const boxRatio = boxW / boxH
- const zoom = sizeRatio > boxRatio ? boxW / size.width : boxH / size.height
-
- // if zoom is the same we still calc this
- setScale(zoom + 0.1)
- setTimeout(() => {
- setScale(zoom)
- setTimeout(() => {
- setLoading(false)
- }, 10)
- }, 0)
- }, [boxW, boxH, size])
-
if (lastSrc !== src) {
setLastSrc(src)
setLoading(true)
- initialZoomRef.current = false
}
- const imageSize = React.useMemo(
- () =>
- size
- ? Styles.isAndroid
- ? {
- backgroundColor: Styles.globalColors.black,
- height: size.height,
- opacity: loading ? 0 : 1,
- position: 'relative',
- width: size.width,
- }
- : {
- height: size.height,
- opacity: loading ? 0 : 1,
- width: size.width,
- }
- : undefined,
- [size, loading]
- )
- const measuredStyle = size ? imageSize : dummySize
+ const boxOnLayout = React.useCallback((e: Partial) => {
+ if (!e.nativeEvent) return
+ const {width, height} = e.nativeEvent.layout
+ setBoxW(width)
+ setBoxH(height)
+ }, [])
- const content = (
- <>
- {src ? (
+ // in order for the images to not get downscaled we have to make it larger and then transform it
+ const manualScale = 5
+
+ return (
+
+
- ) : null}
+
{loading ? (
) : null}
- >
- )
-
- return (
-
- {content}
)
}
@@ -135,11 +85,29 @@ const ZoomableImage = (p: Props) => {
const styles = Styles.styleSheetCreate(
() =>
({
+ contentContainerStyle: {
+ alignContent: 'center',
+ height: '100%',
+ justifyContent: 'center',
+ maxHeight: '100%',
+ maxWidth: '100%',
+ width: '100%',
+ },
+ image: {
+ height: '100%',
+ width: '100%',
+ },
+ imageAndroid: {flexGrow: 1},
progress: {
left: '50%',
position: 'absolute',
top: '50%',
},
+ zoomableBoxContainerAndroid: {
+ flex: 1,
+ overflow: 'hidden',
+ position: 'relative',
+ },
}) as const
)
diff --git a/shared/desktop/CHANGELOG.txt b/shared/desktop/CHANGELOG.txt
index 295adee204a4..b1ec2d603fc0 100644
--- a/shared/desktop/CHANGELOG.txt
+++ b/shared/desktop/CHANGELOG.txt
@@ -1,8 +1,3 @@
-All platforms:
+• Improvements for chat attachments
+• Bug fixes
-Desktop:
-• Screenshots of the app disabled by default
-
-iOS:
-
-Android:
diff --git a/shared/ios/Keybase/Info.plist b/shared/ios/Keybase/Info.plist
index 6de287fb81e9..2ffe1827b573 100644
--- a/shared/ios/Keybase/Info.plist
+++ b/shared/ios/Keybase/Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 6.3.2
+ 6.4.0
CFBundleSignature
????
CFBundleURLTypes
diff --git a/shared/ios/KeybaseShare/Info.plist b/shared/ios/KeybaseShare/Info.plist
index 4763d80ca81d..44d5a03568a9 100644
--- a/shared/ios/KeybaseShare/Info.plist
+++ b/shared/ios/KeybaseShare/Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 6.3.2
+ 6.4.0
CFBundleVersion
1
NSExtension
diff --git a/shared/profile/edit-avatar/index.native.tsx b/shared/profile/edit-avatar/index.native.tsx
index a2ddc13bcce5..9ad61dcaba71 100644
--- a/shared/profile/edit-avatar/index.native.tsx
+++ b/shared/profile/edit-avatar/index.native.tsx
@@ -151,8 +151,7 @@ class AvatarUpload extends React.Component {
) : null
}
@@ -270,10 +269,6 @@ const styles = Kb.Styles.styleSheetCreate(
flexReallyGrow: {
flexGrow: 1000,
},
- image: {
- overflow: 'hidden',
- position: 'relative',
- },
placeholder: {
alignItems: 'center',
backgroundColor: Kb.Styles.globalColors.black_05,