Skip to content

Commit

Permalink
Merge pull request #31308 from margelo/feat/use-attachment-gallery-ev…
Browse files Browse the repository at this point in the history
…erywhere

Feature: Use attachment gallery everywhere
  • Loading branch information
pecanoro authored Dec 15, 2023
2 parents 275bd03 + db5e3d4 commit 6c110a4
Show file tree
Hide file tree
Showing 22 changed files with 792 additions and 778 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java
index 1339f5c..9dfec0c 100644
--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java
+++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java
@@ -176,7 +176,8 @@ class FastImageViewWithUrl extends AppCompatImageView {
.apply(FastImageViewConverter
.getOptions(context, imageSource, mSource)
.placeholder(mDefaultSource) // show until loaded
- .fallback(mDefaultSource)); // null will not be treated as error
+ .fallback(mDefaultSource))
+ .transform(new ResizeTransformation());

if (key != null)
builder.listener(new FastImageRequestListener(key));
diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/ResizeTransformation.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/ResizeTransformation.java
new file mode 100644
index 0000000..1daa227
--- /dev/null
+++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/ResizeTransformation.java
@@ -0,0 +1,41 @@
+package com.dylanvann.fastimage;
+
+ import android.content.Context;
+ import android.graphics.Bitmap;
+
+ import androidx.annotation.NonNull;
+
+ import com.bumptech.glide.load.Transformation;
+ import com.bumptech.glide.load.engine.Resource;
+ import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
+ import com.bumptech.glide.load.resource.bitmap.BitmapResource;
+
+ import java.security.MessageDigest;
+
+ public class ResizeTransformation implements Transformation<Bitmap> {
+
+ private final double MAX_BYTES = 25000000.0;
+
+ @NonNull
+ @Override
+ public Resource<Bitmap> transform(@NonNull Context context, @NonNull Resource<Bitmap> resource, int outWidth, int outHeight) {
+ Bitmap toTransform = resource.get();
+
+ if (toTransform.getByteCount() > MAX_BYTES) {
+ double scaleFactor = Math.sqrt(MAX_BYTES / (double) toTransform.getByteCount());
+ int newHeight = (int) (outHeight * scaleFactor);
+ int newWidth = (int) (outWidth * scaleFactor);
+
+ BitmapPool pool = GlideApp.get(context).getBitmapPool();
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(toTransform, newWidth, newHeight, true);
+ return BitmapResource.obtain(scaledBitmap, pool);
+ }
+
+ return resource;
+ }
+
+ @Override
+ public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
+ messageDigest.update(("ResizeTransformation").getBytes());
+ }
+ }
\ No newline at end of file
139 changes: 71 additions & 68 deletions src/components/AttachmentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {Animated, Keyboard, View} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -425,78 +426,80 @@ function AttachmentModal(props) {
}}
propagateSwipe
>
{props.isSmallScreenWidth && <HeaderGap />}
<HeaderWithBackButton
title={headerTitle}
shouldShowBorderBottom
shouldShowDownloadButton={shouldShowDownloadButton}
onDownloadButtonPress={() => downloadAttachment(source)}
shouldShowCloseButton={!props.isSmallScreenWidth}
shouldShowBackButton={props.isSmallScreenWidth}
onBackButtonPress={closeModal}
onCloseButtonPress={closeModal}
shouldShowThreeDotsButton={shouldShowThreeDotsButton}
threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)}
threeDotsMenuItems={threeDotsMenuItems}
shouldOverlay
/>
<View style={styles.imageModalImageCenterContainer}>
{!_.isEmpty(props.report) && !props.isReceiptAttachment ? (
<AttachmentCarousel
report={props.report}
onNavigate={onNavigate}
source={props.source}
onClose={closeModal}
onToggleKeyboard={updateConfirmButtonVisibility}
setDownloadButtonVisibility={setDownloadButtonVisibility}
/>
) : (
Boolean(sourceForAttachmentView) &&
shouldLoadAttachment && (
<AttachmentView
containerStyles={[styles.mh5]}
source={sourceForAttachmentView}
isAuthTokenRequired={isAuthTokenRequired}
file={file}
<GestureHandlerRootView style={styles.flex1}>
{props.isSmallScreenWidth && <HeaderGap />}
<HeaderWithBackButton
title={headerTitle}
shouldShowBorderBottom
shouldShowDownloadButton={shouldShowDownloadButton}
onDownloadButtonPress={() => downloadAttachment(source)}
shouldShowCloseButton={!props.isSmallScreenWidth}
shouldShowBackButton={props.isSmallScreenWidth}
onBackButtonPress={closeModal}
onCloseButtonPress={closeModal}
shouldShowThreeDotsButton={shouldShowThreeDotsButton}
threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)}
threeDotsMenuItems={threeDotsMenuItems}
shouldOverlay
/>
<View style={styles.imageModalImageCenterContainer}>
{!_.isEmpty(props.report) && !props.isReceiptAttachment ? (
<AttachmentCarousel
report={props.report}
onNavigate={onNavigate}
source={props.source}
onClose={closeModal}
onToggleKeyboard={updateConfirmButtonVisibility}
isWorkspaceAvatar={props.isWorkspaceAvatar}
fallbackSource={props.fallbackSource}
isUsedInAttachmentModal
transactionID={props.transaction.transactionID}
setDownloadButtonVisibility={setDownloadButtonVisibility}
/>
)
)}
</View>
{/* If we have an onConfirm method show a confirmation button */}
{Boolean(props.onConfirm) && (
<SafeAreaConsumer>
{({safeAreaPaddingBottomStyle}) => (
<Animated.View style={[StyleUtils.fade(confirmButtonFadeAnimation), safeAreaPaddingBottomStyle]}>
<Button
success
style={[styles.buttonConfirm, props.isSmallScreenWidth ? {} : styles.attachmentButtonBigScreen]}
textStyles={[styles.buttonConfirmText]}
text={translate('common.send')}
onPress={submitAndClose}
disabled={isConfirmButtonDisabled}
pressOnEnter
) : (
Boolean(sourceForAttachmentView) &&
shouldLoadAttachment && (
<AttachmentView
containerStyles={[styles.mh5]}
source={sourceForAttachmentView}
isAuthTokenRequired={isAuthTokenRequired}
file={file}
onToggleKeyboard={updateConfirmButtonVisibility}
isWorkspaceAvatar={props.isWorkspaceAvatar}
fallbackSource={props.fallbackSource}
isUsedInAttachmentModal
transactionID={props.transaction.transactionID}
/>
</Animated.View>
)
)}
</SafeAreaConsumer>
)}
{props.isReceiptAttachment && (
<ConfirmModal
title={translate('receipt.deleteReceipt')}
isVisible={isDeleteReceiptConfirmModalVisible}
onConfirm={deleteAndCloseModal}
onCancel={closeConfirmModal}
prompt={translate('receipt.deleteConfirmation')}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
danger
/>
)}
</View>
{/* If we have an onConfirm method show a confirmation button */}
{Boolean(props.onConfirm) && (
<SafeAreaConsumer>
{({safeAreaPaddingBottomStyle}) => (
<Animated.View style={[StyleUtils.fade(confirmButtonFadeAnimation), safeAreaPaddingBottomStyle]}>
<Button
success
style={[styles.buttonConfirm, props.isSmallScreenWidth ? {} : styles.attachmentButtonBigScreen]}
textStyles={[styles.buttonConfirmText]}
text={translate('common.send')}
onPress={submitAndClose}
disabled={isConfirmButtonDisabled}
pressOnEnter
/>
</Animated.View>
)}
</SafeAreaConsumer>
)}
{props.isReceiptAttachment && (
<ConfirmModal
title={translate('receipt.deleteReceipt')}
isVisible={isDeleteReceiptConfirmModalVisible}
onConfirm={deleteAndCloseModal}
onCancel={closeConfirmModal}
prompt={translate('receipt.deleteConfirmation')}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
danger
/>
)}
</GestureHandlerRootView>
</Modal>
{!props.isReceiptAttachment && (
<ConfirmModal
Expand Down
18 changes: 13 additions & 5 deletions src/components/Attachments/AttachmentCarousel/CarouselItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ const propTypes = {
transactionID: PropTypes.string,
}).isRequired,

/** Whether the attachment is currently being viewed in the carousel */
isFocused: PropTypes.bool.isRequired,
/** Whether there is only one element in the attachment carousel */
isSingleItem: PropTypes.bool.isRequired,

/** The index of the carousel item */
index: PropTypes.number.isRequired,

/** The index of the currently active carousel item */
activeIndex: PropTypes.number.isRequired,

/** onPress callback */
onPress: PropTypes.func,
Expand All @@ -48,7 +54,7 @@ const defaultProps = {
onPress: undefined,
};

function CarouselItem({item, isFocused, onPress}) {
function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isAttachmentHidden} = useContext(ReportAttachmentsContext);
Expand Down Expand Up @@ -98,9 +104,11 @@ function CarouselItem({item, isFocused, onPress}) {
source={item.source}
file={item.file}
isAuthTokenRequired={item.isAuthTokenRequired}
isFocused={isFocused}
onPress={onPress}
isUsedInCarousel
isSingleCarouselItem={isSingleItem}
carouselItemIndex={index}
carouselActiveItemIndex={activeIndex}
onPress={onPress}
transactionID={item.transactionID}
/>
</View>
Expand Down
Loading

0 comments on commit 6c110a4

Please sign in to comment.