forked from Ivasoft/mattermost-mobile
[Gekidou] Gallery (#6008)
* Gallery screen (ground work) * Open the gallery from posts * Open the gallery from post draft * feedback review * Feedback review 2 * do not remove dm channel names and localization fix * update to the latest network-client * do not override file width, height and imageThumbail if received file does not have it set * bring back ScrollView wrapper for message component * Remove Text wrapper for markdown paragraph * Fix YouTube play icon placeholder * Make video file play button container round * Add gif image placeholder * Save images & videos to camera roll * Feedback review 3 * load video thumbnail when post is in viewport * simplify prefix
This commit is contained in:
164
app/context/gallery/index.tsx
Normal file
164
app/context/gallery/index.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useEffect, useLayoutEffect} from 'react';
|
||||
import Animated, {makeMutable, runOnUI} from 'react-native-reanimated';
|
||||
|
||||
export interface GalleryManagerItem {
|
||||
index: number;
|
||||
ref: React.RefObject<unknown>;
|
||||
}
|
||||
|
||||
export interface GalleryManagerItems {
|
||||
[index: number]: GalleryManagerItem;
|
||||
}
|
||||
|
||||
interface GalleryInitProps {
|
||||
children: JSX.Element;
|
||||
galleryIdentifier: string;
|
||||
}
|
||||
|
||||
class Gallery {
|
||||
private init = false;
|
||||
private timeout: NodeJS.Timeout | null = null;
|
||||
|
||||
public refsByIndexSV: Animated.SharedValue<GalleryManagerItems> = makeMutable({});
|
||||
|
||||
public sharedValues: GalleryManagerSharedValues = {
|
||||
width: makeMutable(0),
|
||||
height: makeMutable(0),
|
||||
x: makeMutable(0),
|
||||
y: makeMutable(0),
|
||||
opacity: makeMutable(1),
|
||||
activeIndex: makeMutable(0),
|
||||
targetWidth: makeMutable(0),
|
||||
targetHeight: makeMutable(0),
|
||||
};
|
||||
|
||||
public items = new Map<number, GalleryManagerItem>();
|
||||
|
||||
public get isInitialized() {
|
||||
return this.init;
|
||||
}
|
||||
|
||||
public resolveItem(index: number) {
|
||||
return this.items.get(index);
|
||||
}
|
||||
|
||||
public initialize() {
|
||||
this.init = true;
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.init = false;
|
||||
this.items.clear();
|
||||
this.refsByIndexSV.value = {};
|
||||
}
|
||||
|
||||
public resetSharedValues() {
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
opacity,
|
||||
activeIndex,
|
||||
x,
|
||||
y,
|
||||
} = this.sharedValues;
|
||||
|
||||
runOnUI(() => {
|
||||
'worklet';
|
||||
|
||||
width.value = 0;
|
||||
height.value = 0;
|
||||
opacity.value = 1;
|
||||
activeIndex.value = -1;
|
||||
x.value = 0;
|
||||
y.value = 0;
|
||||
})();
|
||||
}
|
||||
|
||||
public registerItem(index: number, ref: React.RefObject<unknown>) {
|
||||
if (this.items.has(index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addItem(index, ref);
|
||||
}
|
||||
|
||||
private addItem(index: number, ref: GalleryManagerItem['ref']) {
|
||||
this.items.set(index, {
|
||||
index,
|
||||
ref,
|
||||
});
|
||||
|
||||
if (this.timeout !== null) {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.refsByIndexSV.value = this.convertMapToObject(this.items);
|
||||
|
||||
this.timeout = null;
|
||||
}, 16);
|
||||
}
|
||||
|
||||
private convertMapToObject<T extends Map<string | number, GalleryManagerItem>>(map: T) {
|
||||
const obj: Record<string, GalleryManagerItem> = {};
|
||||
for (const [key, value] of map) {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryManager {
|
||||
private galleries: Record<string, Gallery> = {};
|
||||
|
||||
public get(identifier: string): Gallery {
|
||||
if (this.galleries[identifier]) {
|
||||
return this.galleries[identifier];
|
||||
}
|
||||
|
||||
const gallery = new Gallery();
|
||||
this.galleries[identifier] = gallery;
|
||||
return gallery;
|
||||
}
|
||||
|
||||
public remove(identifier: string): boolean {
|
||||
return delete this.galleries[identifier];
|
||||
}
|
||||
}
|
||||
|
||||
const galleryManager = new GalleryManager();
|
||||
|
||||
export function useGallery(galleryIdentifier: string) {
|
||||
const gallery = galleryManager.get(galleryIdentifier);
|
||||
|
||||
if (!gallery) {
|
||||
throw new Error(
|
||||
'Cannot retrieve gallery manager from the context. Did you forget to wrap the app with GalleryProvider?',
|
||||
);
|
||||
}
|
||||
|
||||
return gallery;
|
||||
}
|
||||
|
||||
export function GalleryInit({children, galleryIdentifier}: GalleryInitProps) {
|
||||
const gallery = useGallery(galleryIdentifier);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
gallery.initialize();
|
||||
|
||||
return () => {
|
||||
gallery.reset();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
galleryManager.remove(galleryIdentifier);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return children;
|
||||
}
|
||||
Reference in New Issue
Block a user