Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support refreshing photo selection on iOS #2291

Merged
merged 2 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package/expo-package/src/handlers/getPhotos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type ReturnType = {
assets: Array<Omit<Asset, 'source'> & { source: 'picker' }>;
endCursor: string | undefined;
hasNextPage: boolean;
iOSLimited: boolean;
};

export const getPhotos = async ({
Expand All @@ -16,7 +17,7 @@ export const getPhotos = async ({
// should always check first before requesting permission
// because always requesting permission will cause
// the app to go to background even if it was granted
const { status } = await MediaLibrary.getPermissionsAsync();
const { accessPrivileges, status } = await MediaLibrary.getPermissionsAsync();
if (status !== 'granted') {
const { status: newStatus } = await MediaLibrary.requestPermissionsAsync();
if (newStatus !== 'granted') {
Expand All @@ -42,7 +43,7 @@ export const getPhotos = async ({

const hasNextPage = results.hasNextPage;
const endCursor = results.endCursor;
return { assets, endCursor, hasNextPage };
return { assets, endCursor, hasNextPage, iOSLimited: accessPrivileges === 'limited' };
} catch {
throw new Error('getPhotos Error');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ export function oniOS14GalleryLibrarySelectionChange(callback: () => void): {
unsubscribe: () => {},
};
}

export const iOS14RefreshGallerySelection = (): Promise<void> => {
if (isAboveIOS14) {
return MediaLibrary.presentPermissionsPickerAsync();
}
return Promise.resolve();
};
2 changes: 1 addition & 1 deletion package/expo-package/src/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ export * from './compressImage';
export * from './deleteFile';
export * from './getLocalAssetUri';
export * from './getPhotos';
export * from './iOS14RefreshGallerySelection';
export * from './NetInfo';
export * from './saveFile';
export * from './Sound';
export * from './takePhoto';
export * from './Video';
export * from './oniOS14GalleryLibrarySelectionChange';
2 changes: 2 additions & 0 deletions package/expo-package/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
deleteFile,
getLocalAssetUri,
getPhotos,
iOS14RefreshGallerySelection,
NetInfo,
oniOS14GalleryLibrarySelectionChange,
saveFile,
Expand All @@ -28,6 +29,7 @@ registerNativeHandlers({
FlatList,
getLocalAssetUri,
getPhotos,
iOS14RefreshGallerySelection,
NetInfo,
oniOS14GalleryLibrarySelectionChange,
pickDocument,
Expand Down
3 changes: 2 additions & 1 deletion package/native-package/src/handlers/getPhotos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type ReturnType = {
assets: Array<Omit<Asset, 'source'> & { source: 'picker' }>;
endCursor: string | undefined;
hasNextPage: boolean;
iOSLimited: boolean;
};

const verifyAndroidPermissions = async () => {
Expand Down Expand Up @@ -84,7 +85,7 @@ export const getPhotos = async ({
}));
const hasNextPage = results.page_info.has_next_page;
const endCursor = results.page_info.end_cursor;
return { assets, endCursor, hasNextPage };
return { assets, endCursor, hasNextPage, iOSLimited: !!results.limited };
} catch (_error) {
throw new Error('getPhotos Error');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Platform } from 'react-native';

import { iosRefreshGallerySelection } from '@react-native-camera-roll/camera-roll';

const isAboveIOS14 = Platform.OS === 'ios' && parseInt(Platform.Version as string, 10) >= 14;

export const iOS14RefreshGallerySelection = (): Promise<void> => {
if (isAboveIOS14) {
return iosRefreshGallerySelection().then(() => {
//do nothing
});
}
return Promise.resolve();
};
1 change: 1 addition & 0 deletions package/native-package/src/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './takePhoto';
export * from './Sound';
export * from './Video';
export * from './oniOS14GalleryLibrarySelectionChange';
export * from './iOS14RefreshGallerySelection';
2 changes: 2 additions & 0 deletions package/native-package/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
deleteFile,
getLocalAssetUri,
getPhotos,
iOS14RefreshGallerySelection,
NetInfo,
oniOS14GalleryLibrarySelectionChange,
saveFile,
Expand All @@ -29,6 +30,7 @@ registerNativeHandlers({
FlatList,
getLocalAssetUri,
getPhotos,
iOS14RefreshGallerySelection,
NetInfo,
oniOS14GalleryLibrarySelectionChange,
pickDocument,
Expand Down
23 changes: 21 additions & 2 deletions package/src/components/AttachmentPicker/AttachmentPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { BackHandler, Dimensions, Keyboard, Platform, StatusBar, StyleSheet } from 'react-native';
import {
BackHandler,
Button,
Dimensions,
Keyboard,
Platform,
StatusBar,
StyleSheet,
} from 'react-native';

import BottomSheet, { BottomSheetFlatList, BottomSheetHandleProps } from '@gorhom/bottom-sheet';
import dayjs from 'dayjs';
Expand All @@ -11,7 +19,12 @@ import { renderAttachmentPickerItem } from './components/AttachmentPickerItem';

import { useAttachmentPickerContext } from '../../contexts/attachmentPickerContext/AttachmentPickerContext';
import { useTheme } from '../../contexts/themeContext/ThemeContext';
import { getPhotos, oniOS14GalleryLibrarySelectionChange } from '../../native';
import { useTranslationContext } from '../../contexts/translationContext/TranslationContext';
import {
getPhotos,
iOS14RefreshGallerySelection,
oniOS14GalleryLibrarySelectionChange,
} from '../../native';
import type { Asset } from '../../types/types';
import { vh } from '../../utils/utils';

Expand Down Expand Up @@ -97,10 +110,12 @@ export const AttachmentPicker = React.forwardRef(
const [currentIndex, setCurrentIndex] = useState(-1);
const endCursorRef = useRef<string>();
const [photoError, setPhotoError] = useState(false);
const [iOSLimited, setIosLimited] = useState(false);
const hasNextPageRef = useRef(true);
const [loadingPhotos, setLoadingPhotos] = useState(false);
const [photos, setPhotos] = useState<Asset[]>([]);
const attemptedToLoadPhotosOnOpenRef = useRef(false);
const { t } = useTranslationContext();

const getMorePhotos = useCallback(async () => {
if (
Expand All @@ -121,6 +136,7 @@ export const AttachmentPicker = React.forwardRef(
setPhotos((prevPhotos) =>
endCursor ? [...prevPhotos, ...results.assets] : results.assets,
);
setIosLimited(results.iOSLimited);
hasNextPageRef.current = !!results.hasNextPage;
} catch (error) {
setPhotoError(true);
Expand Down Expand Up @@ -299,6 +315,9 @@ export const AttachmentPicker = React.forwardRef(
ref={ref}
snapPoints={snapPoints}
>
{iOSLimited && (
<Button onPress={iOS14RefreshGallerySelection} title={t('Select More Photos')} />
)}
<BottomSheetFlatList
contentContainerStyle={[
styles.container,
Expand Down
1 change: 1 addition & 0 deletions package/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} Replies",
"{{ replyCount }} Thread Replies": "{{ replyCount }} Thread Replies",
"{{ user }} is typing": "{{ user }} is typing",
"Select More Photos": "Select More Photos",
"🏙 Attachment...": "🏙 Attachment...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB."
}
1 change: 1 addition & 0 deletions package/src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} Réponses",
"{{ replyCount }} Thread Replies": "{{replyCount}} Réponses à la discussion",
"{{ user }} is typing": "{{ user }} est en train d'écrire",
"Select More Photos": "Sélectionner plus de photos",
"🏙 Attachment...": "🏙 Pièce jointe...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "Taille maximale de téléchargement de fichier atteinte. Veuillez télécharger un fichier inférieur à {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} Mo."
}
1 change: 1 addition & 0 deletions package/src/i18n/he.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} תגובות",
"{{ replyCount }} Thread Replies": "{{ replyCount }} תגובות שרשור",
"{{ user }} is typing": "{{ user }} מקליד/ה",
"Select More Photos": "בחר עוד תמונות",
"🏙 Attachment...": "🏙 קובץ מצורף...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "הגעת למגבלת העלאת גודל הקובץ המקסימלית. אנא העלה קובץ מתחת ל-{{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB"
}
1 change: 1 addition & 0 deletions package/src/i18n/hi.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} रिप्लाई",
"{{ replyCount }} Thread Replies": "{{ replyCount }}} थ्रेड उत्तर",
"{{ user }} is typing": "{{ user }} टाइप कर रहा है",
"Select More Photos": "अधिक फ़ोटो चुनें",
"🏙 Attachment...": "🏙 अटैचमेंट...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "अधिकतम फ़ाइल आकार अपलोड सीमा पूरी हो गई। कृपया {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} एमबी से नीचे की फ़ाइल अपलोड करें।"
}
1 change: 1 addition & 0 deletions package/src/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} Risposte",
"{{ replyCount }} Thread Replies": "{{replyCount}} Risposte alle Conversazione",
"{{ user }} is typing": "{{ user }} sta scrivendo",
"Select More Photos": "Seleziona Altre foto",
"🏙 Attachment...": "🏙 Allegato...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "È stato raggiunto il limite massimo di caricamento delle dimensioni del file. Carica un file inferiore a {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB."
}
1 change: 1 addition & 0 deletions package/src/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"Reply to Message": "メッセージに返信",
"🏙 Attachment...": "🏙 アタッチメント...",
"Not supported": "サポートしていません",
"Select More Photos": "さらに写真を選択",
"File type not supported": "サポートされていないファイルです",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "最大ファイル サイズのアップロード制限に達しました。 {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB 以下のファイルをアップロードしてください"
}
1 change: 1 addition & 0 deletions package/src/i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"Reply to Message": "메시지에 답장",
"🏙 Attachment...": "🏙 부착...",
"File type not supported": "지원하지 않는 파일입니다.",
"Select More Photos": "추가 사진 선택",
"Not supported": "지원하지 않습니다",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "최대 파일 크기 업로드 제한에 도달했습니다. {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}}MB 미만의 파일을 업로드하세요."
}
1 change: 1 addition & 0 deletions package/src/i18n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} Antwoorden",
"{{ replyCount }} Thread Replies": "{{replyCount}} Discussiereacties",
"{{ user }} is typing": "{{ user }} is aan het typen",
"Select More Photos": "Selecteer Meer foto's",
"🏙 Attachment...": "🏙 Bijlage...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "Maximale uploadlimiet voor bestandsgrootte bereikt. Upload een bestand van minder dan {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB."
}
1 change: 1 addition & 0 deletions package/src/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} Ответов",
"{{ replyCount }} Thread Replies": "{{replyCount}} Ответы в темах",
"{{ user }} is typing": "{{ user }} пишет",
"Select More Photos": "Выбрать больше фотографий",
"🏙 Attachment...": "🏙 Вложение...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "Достигнут предел максимального размера файла для загрузки. Загрузите файл размером менее {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} МБ."
}
1 change: 1 addition & 0 deletions package/src/i18n/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"{{ replyCount }} Replies": "{{ replyCount }} Cevap",
"{{ replyCount }} Thread Replies": "{{responseCount}} Konu Cevapı",
"{{ user }} is typing": "{{ user }} yazıyor",
"Select More Photos": "Daha Fazla Fotoğraf Seçin",
"🏙 Attachment...": "🏙 Ek...",
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "Maksimum dosya boyutu yükleme sınırına ulaşıldı. Lütfen {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB'ın altında bir dosya yükleyin."
}
9 changes: 9 additions & 0 deletions package/src/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ export let getLocalAssetUri: GetLocalAssetUri = fail;
type OniOS14LibrarySelectionChange = (callback: () => void) => { unsubscribe: () => void };
export let oniOS14GalleryLibrarySelectionChange: OniOS14LibrarySelectionChange = fail;

type iOS14RefreshGallerySelection = () => Promise<void>;
export let iOS14RefreshGallerySelection: iOS14RefreshGallerySelection = fail;

type GetPhotos = ({ after, first }: { first: number; after?: string }) =>
| Promise<{
assets: Array<Omit<Asset, 'source'> & { source: 'picker' }>;
endCursor: string;
hasNextPage: boolean;
iOSLimited: boolean;
}>
| never;
export let getPhotos: GetPhotos = fail;
Expand Down Expand Up @@ -213,6 +217,7 @@ export type VideoType = {
export let Video: React.ComponentType<VideoType>;

type Handlers = {
iOS14RefreshGallerySelection: iOS14RefreshGallerySelection;
compressImage?: CompressImage;
deleteFile?: DeleteFile;
FlatList?: typeof DefaultFlatList;
Expand Down Expand Up @@ -255,6 +260,10 @@ export const registerNativeHandlers = (handlers: Handlers) => {
getPhotos = handlers.getPhotos;
}

if (handlers.iOS14RefreshGallerySelection) {
iOS14RefreshGallerySelection = handlers.iOS14RefreshGallerySelection;
}

if (handlers.oniOS14GalleryLibrarySelectionChange) {
oniOS14GalleryLibrarySelectionChange = handlers.oniOS14GalleryLibrarySelectionChange;
}
Expand Down
Loading