Skip to content

Commit

Permalink
added image viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
ragsav committed Sep 30, 2022
1 parent 195299f commit 665d9f3
Show file tree
Hide file tree
Showing 15 changed files with 445 additions and 17 deletions.
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
android:name=".MainApplication"
Expand Down
70 changes: 70 additions & 0 deletions js/components/ImageAttachmentsGallery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {FlatList, Image, Pressable, View} from 'react-native';
import React from 'react';
import {Text, useTheme} from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import {useState} from 'react';
import {useEffect} from 'react';

export const ImageAttachmentGallery = ({
URIs,
removeURI,
imageToView,
setImageToView,
}) => {
const theme = useTheme();

const _renderURIImages = ({item, index}) => {
const _split = String(item).split('/');

return (
<Pressable
style={{position: 'relative', marginRight: 12}}
onPress={() =>
setImageToView({
images: URIs?.map(uri => {
return {uri};
}),
index: index,
visible: true,
})
}>
<Image
source={{uri: item}}
style={{height: 120, width: 200, borderRadius: 6}}
/>
<Pressable
onPress={() => removeURI(item)}
style={{
position: 'absolute',
top: 6,
right: 6,

backgroundColor: theme?.colors.backdrop,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 6,
borderRadius: 20,
}}>
<MaterialCommunityIcons
name="close"
color={theme?.colors.surface}
size={12}
style={{padding: 0}}
/>
</Pressable>
</Pressable>
);
};
return (
<View>
<FlatList
data={URIs}
horizontal={true}
contentContainerStyle={{padding: 12}}
renderItem={_renderURIImages}
keyExtractor={(item, index) => item}
/>
</View>
);
};
177 changes: 177 additions & 0 deletions js/components/ImagePickerBottomSheet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import BottomSheet, {
BottomSheetBackdrop,
BottomSheetFlatList,
BottomSheetView,
useBottomSheetDynamicSnapPoints,
} from '@gorhom/bottom-sheet';
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import withObservables from '@nozbe/with-observables';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import {Pressable, View} from 'react-native';
import {
Appbar,
Divider,
IconButton,
List,
Surface,
Text,
TouchableRipple,
useTheme,
} from 'react-native-paper';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import {database} from '../db/db';
import Label from '../db/models/Label';
import {Logger} from '../utils/logger';

export const ImagePickerBottomSheet = ({
visible,
setVisible,
addURI,
removeURI,
}) => {
// hooks
const sheetRef = useRef(null);
const theme = useTheme();

const snapPoints = useMemo(() => ['CONTENT_HEIGHT'], []);

const {
animatedHandleHeight,
animatedSnapPoints,
animatedContentHeight,
handleContentLayout,
} = useBottomSheetDynamicSnapPoints(snapPoints);

useEffect(() => {
if (!visible) {
_handleCloseFilterSheet();
} else {
_handleOpenFilterSheet(0);
}
}, [visible, sheetRef]);

// callbacks
const _handleSheetChange = useCallback(index => {}, []);
const _handleOpenFilterSheet = useCallback(index => {
sheetRef.current?.snapToIndex(index);
}, []);
const _handleCloseFilterSheet = useCallback(() => {
sheetRef.current?.close();
}, []);

const _renderBackdrop = useCallback(
props => (
<BottomSheetBackdrop
{...props}
appearsOnIndex={0}
disappearsOnIndex={-1}
opacity={0.4}
/>
),
[],
);

const _handleOpenCamera = async () => {
const result = await launchCamera({saveToPhotos: true});
if (result.didCancel || result.errorCode) {
Logger.pageLogger(
'ImagePickerBottomSheet.js:_handleOpenCamera:cancel or error',
result.errorMessage,
);
} else {
if (
result.assets &&
Array.isArray(result.assets) &&
result.assets.length > 0
) {
addURI(result.assets[0].uri);
}
Logger.pageLogger(
'ImagePickerBottomSheet.js:_handleOpenCamera:result',
result,
);
}
setVisible(false);
};

const _handleOpenFileSystem = async () => {
const result = await launchImageLibrary();
if (result.didCancel || result.errorCode) {
Logger.pageLogger(
'ImagePickerBottomSheet.js:_handleOpenFileSystem:cancel or error',
result.errorMessage,
);
} else {
if (
result.assets &&
Array.isArray(result.assets) &&
result.assets.length > 0
) {
addURI(result.assets[0].uri);
}
Logger.pageLogger(
'ImagePickerBottomSheet.js:_handleOpenFileSystem:result',
result,
);
}
setVisible(false);
};

return (
<BottomSheet
ref={sheetRef}
index={-1}
snapPoints={animatedSnapPoints}
handleHeight={animatedHandleHeight}
contentHeight={animatedContentHeight}
backgroundStyle={{borderRadius: 6}}
enablePanDownToClose={false}
handleStyle={{display: 'none'}}
backdropComponent={_renderBackdrop}
onClose={() => setVisible(false)}
onChange={_handleSheetChange}>
<BottomSheetView onLayout={handleContentLayout}>
<View
style={{
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingHorizontal: 12,
}}>
<Text>Upload from</Text>
<IconButton
icon={'close'}
onPress={() => {
setVisible(false);
}}
/>
</View>

<Surface
elevation={0}
style={{
width: '100%',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'flex-start',
}}>
<List.Item
left={() => <List.Icon icon={'folder'} />}
style={{width: '100%', paddingVertical: 0}}
onPress={_handleOpenFileSystem}
title="From files"
titleEllipsizeMode="tail"
/>
<List.Item
left={() => <List.Icon icon={'camera'} />}
style={{width: '100%', paddingVertical: 0}}
onPress={_handleOpenCamera}
title="Camera"
titleEllipsizeMode="tail"
/>
</Surface>
</BottomSheetView>
</BottomSheet>
);
};
1 change: 1 addition & 0 deletions js/components/LabelItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import withObservables from '@nozbe/with-observables';
import {Q} from '@nozbe/watermelondb';
import {useNavigation} from '@react-navigation/native';
import React, {useState} from 'react';
import {View} from 'react-native';
Expand Down
4 changes: 2 additions & 2 deletions js/components/NoteItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const NoteItem = ({note, tasksCount, handleDeleteNote, dispatch}) => {
alignItems: 'stretch',
paddingHorizontal: 12,
paddingRight: 0,
paddingVertical: note.isArchived ? 2 : 8,
paddingVertical: note.isArchived ? 2 : 12,
borderLeftColor:
note && note.colorString ? note.colorString : theme?.colors.error,
borderLeftWidth: 5,
Expand Down Expand Up @@ -106,7 +106,7 @@ const NoteItem = ({note, tasksCount, handleDeleteNote, dispatch}) => {
{note.isArchived ? (
<IconButton icon={'package-up'} onPress={_handleUnarchiveNote} />
) : (
<Button>{tasksCount}</Button>
<Text style={{marginLeft: 12, paddingRight: 12}}>{tasksCount}</Text>
)}
</View>
</TouchableRipple>
Expand Down
1 change: 0 additions & 1 deletion js/components/TaskItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ const TaskItem = ({task, onLongPress, noteColor, isActive, dispatch, note}) => {
? theme?.colors.errorContainer
: theme?.colors.primaryContainer,
borderRadius: 4,
paddingBottom: 6,
}}
onPress={_navigateToTaskScreen}
onLongPress={() => {
Expand Down
4 changes: 4 additions & 0 deletions js/db/migration.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,9 @@ export const xpenseDBMigration = schemaMigrations({
toVersion: 17,
steps: [],
},
{
toVersion: 18,
steps: [],
},
],
});
1 change: 1 addition & 0 deletions js/db/models/Task.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default class Task extends Model {

@text('title') title;
@text('description') description;
@text('image_uris') imageURIs;
@text('note_id') noteID;
@field('is_bookmarked') isBookmarked;
@field('is_done') isDone;
Expand Down
3 changes: 2 additions & 1 deletion js/db/schema.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {appSchema, tableSchema} from '@nozbe/watermelondb';

export const xpenserSchema = appSchema({
version: 17,
version: 18,
tables: [
tableSchema({
name: 'labels',
Expand Down Expand Up @@ -36,6 +36,7 @@ export const xpenserSchema = appSchema({
{name: 'is_marked_deleted', type: 'boolean', isOptional: true},
{name: 'marked_deleted_timestamp', type: 'number', isOptional: true},
{name: 'repeat_cron', type: 'string', isOptional: true},
{name: 'image_uris', type: 'string', isOptional: true},
{name: 'created_at', type: 'number'},
{name: 'updated_at', type: 'number'},
],
Expand Down
65 changes: 65 additions & 0 deletions js/redux/actions/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,71 @@ export const editTaskRemoveDueDate =
dispatch(editTaskState({loading: false, success: true, error}));
}
};
export const editTaskAddImageURI =
({id, URI}) =>
async dispatch => {
dispatch(editTaskState({loading: true, success: false, error: null}));
try {
const taskToBeUpdated = await database.get('tasks').find(id);
Logger.pageLogger('task.js:editTaskAddImageURI:taskToBeUpdated', {
taskToBeUpdated,
});
let _eu = JSON.parse(taskToBeUpdated.imageURIs);
console.log(_eu);
if (Array.isArray(_eu)) {
const urii = _eu.indexOf(URI);
if (urii < 0) {
_eu.push(URI);
}
} else {
_eu = [URI];
}
database.write(async () => {
await taskToBeUpdated.update(task => {
task.imageURIs = JSON.stringify(_eu);
});
});

dispatch(editTaskState({loading: false, success: true, error: null}));

// dispatch(getTasks());
Logger.pageLogger('task.js:editTaskAddImageURI:success');
} catch (error) {
Logger.pageLogger('task.js:editTaskAddImageURI:catch', {error});
dispatch(editTaskState({loading: false, success: true, error}));
}
};
export const editTaskRemoveImageURI =
({id, URI}) =>
async dispatch => {
dispatch(editTaskState({loading: true, success: false, error: null}));
try {
const taskToBeUpdated = await database.get('tasks').find(id);
Logger.pageLogger('task.js:editTaskRemoveImageURI:taskToBeUpdated', {
taskToBeUpdated,
});
const _eu = JSON.parse(taskToBeUpdated.imageURIs);
if (Array.isArray(_eu)) {
const urii = _eu.indexOf(URI);
if (urii > -1) {
_eu.splice(urii, 1);
}
}
database.write(async () => {
await taskToBeUpdated.update(task => {
task.imageURIs = JSON.stringify(_eu);
});
});

dispatch(editTaskState({loading: false, success: true, error: null}));

// dispatch(getTasks());
Logger.pageLogger('task.js:editTaskRemoveImageURI:success');
} catch (error) {
Logger.pageLogger('task.js:editTaskRemoveImageURI:catch', {error});
dispatch(editTaskState({loading: false, success: true, error}));
}
};
export const editTaskIsRepeating =
({id, isRepeating}) =>
async dispatch => {
Expand Down
Loading

0 comments on commit 665d9f3

Please sign in to comment.