Add Photos component

parent 798b52c1
......@@ -513,6 +513,109 @@ exports[`Sidebar component renders correctly 1`] = `
},
]
}
>
<View
style={
Array [
Object {
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "flex-start",
"padding": 8,
},
Object {
"backgroundColor": "#FFFFFF",
"borderRadius": 0,
},
Array [
Object {
"padding": 16,
},
Object {},
],
]
}
>
<Text
accessible={true}
allowFontScaling={false}
ellipsizeMode="tail"
onPress={[Function]}
style={
Array [
Object {
"color": "#313131",
"fontSize": 24,
},
Array [
Object {
"marginRight": 10,
},
Object {
"marginRight": 30,
"textAlign": "center",
"width": 28,
},
],
Object {
"fontFamily": "Material Icons",
"fontStyle": "normal",
"fontWeight": "normal",
},
]
}
>
</Text>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
style={
Array [
Object {
"backgroundColor": "transparent",
"fontWeight": "600",
},
Object {
"color": "#313131",
},
]
}
>
Photos
</Text>
</View>
</View>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
hasTVPreferredFocus={undefined}
hitSlop={undefined}
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Array [
Object {
"overflow": "hidden",
},
Object {
"backgroundColor": "#FFFFFF",
"borderRadius": 0,
},
]
}
testID={undefined}
tvParallaxProperties={undefined}
>
<View
style={
......
import React from 'react';
import renderer from 'react-test-renderer';
import AlbumListItem from '../../../../app/ui/screens/photos/AlbumListItem';
describe('AlbumListItem component', () => {
const photo = {
file: {
small: 'http://test.local/small.png',
medium: 'http://test.local/medium.png',
large: 'http://test.local/large.png',
full: 'http://test.local/full.png',
},
album: 0,
pk: 0,
size: 10,
};
const album = {
pk: 1,
title: 'Title',
date: '01-01-2017',
cover: photo,
};
it('renders correctly', () => {
const tree = renderer
.create(<AlbumListItem openAlbum={() => {}} album={album} size={20} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
import React from 'react';
import renderer from 'react-test-renderer';
import PhotoView from '../../../../app/ui/screens/photos/PhotoView';
describe('PhotoView component', () => {
const photo = {
file: {
small: 'http://test.local/small.png',
medium: 'http://test.local/medium.png',
large: 'http://test.local/large.png',
full: 'http://test.local/full.png',
},
album: 0,
pk: 0,
size: 10,
};
it('renders correctly', () => {
const tree = renderer
.create(<PhotoView onPress={() => {}} photo={photo} size={20} token="token" />)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlbumListItem component renders correctly 1`] = `
<View
style={
Array [
Object {},
Object {
"height": 20,
"width": 20,
},
]
}
>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
hasTVPreferredFocus={undefined}
hitSlop={undefined}
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"flex": 1,
}
}
testID={undefined}
tvParallaxProperties={undefined}
>
<View
style={
Object {
"flex": 1,
}
}
>
<Image
source={
Object {
"uri": "http://test.local/small.png",
}
}
style={
Array [
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
Object {
"height": undefined,
"width": undefined,
},
undefined,
]
}
/>
<BVLinearGradient
colors={
Array [
5570560,
4278190080,
]
}
endPoint={undefined}
locations={null}
startPoint={undefined}
style={
Object {
"bottom": 0,
"left": 0,
"opacity": 0.6,
"position": "absolute",
"right": 0,
"top": "50%",
}
}
/>
<View
style={
Object {
"bottom": 4,
"left": 6,
"position": "absolute",
"right": 6,
}
}
>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
style={
Object {
"backgroundColor": "transparent",
"color": "#FFFFFF",
"fontSize": 12,
}
}
>
Title
</Text>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
style={
Object {
"backgroundColor": "transparent",
"color": "#FFFFFF",
"fontSize": 10,
}
}
>
01-01-17
</Text>
</View>
</View>
</View>
</View>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PhotoView component renders correctly 1`] = `
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
hasTVPreferredFocus={undefined}
hitSlop={undefined}
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={Object {}}
testID={undefined}
tvParallaxProperties={undefined}
>
<View
style={
Array [
Object {},
Object {
"height": 20,
"width": 20,
},
]
}
>
<Image
source={
Object {
"headers": Object {
"Authorization": "Token token",
},
"uri": "http://test.local/small.png",
}
}
style={
Array [
Object {},
Object {
"height": 20,
"width": 20,
},
]
}
/>
</View>
</View>
`;
export const PHOTOS_ALBUMS_OPEN = 'PHOTOS_ALBUMS_OPEN';
export const PHOTOS_ALBUMS_SUCCESS = 'PHOTOS_ALBUMS_SUCCESS';
export const PHOTOS_ALBUMS_FAILURE = 'PHOTOS_ALBUMS_FAILURE';
export const PHOTOS_ALBUMS_FETCHING = 'PHOTOS_ALBUMS_FETCHING';
export const PHOTOS_ALBUM_OPEN = 'PHOTOS_ALBUM_OPEN';
export const PHOTOS_ALBUM_SUCCESS = 'PHOTOS_ALBUM_SUCCESS';
export const PHOTOS_ALBUM_FAILURE = 'PHOTOS_ALBUM_FAILURE';
export const PHOTOS_ALBUM_FETCHING = 'PHOTOS_ALBUM_FETCHING';
export const PHOTOS_PHOTO_SHOW = 'PHOTOS_PHOTO_SHOW';
export function openAlbums() {
return {
type: PHOTOS_ALBUMS_OPEN,
};
}
export function successAlbums(albums, more) {
return {
type: PHOTOS_ALBUMS_SUCCESS,
payload: { albums, more },
};
}
export function failureAlbums() {
return { type: PHOTOS_ALBUMS_FAILURE };
}
export function fetchingAlbums() {
return {
type: PHOTOS_ALBUMS_FETCHING,
};
}
export function openAlbum(pk) {
return {
type: PHOTOS_ALBUM_OPEN,
payload: { pk },
};
}
export function successAlbum(album) {
return {
type: PHOTOS_ALBUM_SUCCESS,
payload: album,
};
}
export function failureAlbum() {
return { type: PHOTOS_ALBUM_FAILURE };
}
export function fetchingAlbum() {
return {
type: PHOTOS_ALBUM_FETCHING,
};
}
export function showPhoto(photo) {
return {
type: PHOTOS_PHOTO_SHOW,
payload: photo,
};
}
......@@ -15,6 +15,8 @@ import Profile from './ui/screens/profile/ProfileScreenConnector';
import Pizza from './ui/screens/pizza/PizzaScreenConnector';
import Registration from './ui/screens/events/RegistrationScreenConnector';
import MemberList from './ui/screens/memberList/MemberListScreenConnector';
import Photos from './ui/screens/photos/AlbumsOverviewScreenContainer';
import PhotoAlbum from './ui/screens/photos/AlbumDetailScreenContainer';
import SplashScreen from './ui/screens/splash/SplashScreen';
import Settings from './ui/screens/settings/SettingsScreenConnector';
import EventAdmin from './ui/screens/events/EventAdminScreenConnector';
......@@ -24,6 +26,7 @@ const MainNavigator = createDrawerNavigator({
Welcome,
Calendar,
MemberList,
Photos,
Settings,
}, {
contentComponent: Sidebar,
......@@ -34,6 +37,7 @@ const SignedInNavigator = createStackNavigator({
Event,
Profile,
Pizza,
PhotoAlbum,
Registration,
EventAdmin,
}, {
......
......@@ -8,6 +8,7 @@ import pizza from './pizza';
import registration from './registration';
import members from './members';
import settings from './settings';
import photos from './photos';
export default combineReducers({
session,
......@@ -19,4 +20,5 @@ export default combineReducers({
registration,
members,
settings,
photos,
});
import * as photosActions from '../actions/photos';
import { PHOTO_SHOWING } from '../actions/photos';
export const STATUS_INITIAL = 'initial';
export const STATUS_SUCCESS = 'success';
export const STATUS_FAILURE = 'failure';
const initialState = {
albums: {
status: STATUS_INITIAL,
fetching: true,
data: [],
},
album: {
status: STATUS_INITIAL,
fetching: true,
data: {},
selection: undefined,
},
};
export default function photos(state = initialState, action = {}) {
switch (action.type) {
case photosActions.PHOTOS_ALBUMS_FETCHING:
return {
...state,
albums: initialState.albums,
};
case photosActions.PHOTOS_ALBUMS_SUCCESS:
return {
...state,
albums: {
status: STATUS_SUCCESS,
fetching: false,
data: action.payload.albums,
more: action.payload.more,
},
};
case photosActions.PHOTOS_ALBUMS_FAILURE:
return {
...state,
albums: {
status: STATUS_FAILURE,
fetching: false,
data: [],
},
};
case photosActions.PHOTOS_ALBUM_FETCHING:
return {
...state,
album: initialState.album,
};
case photosActions.PHOTOS_ALBUM_SUCCESS:
return {
...state,
album: {
status: STATUS_SUCCESS,
fetching: false,
data: action.payload,
selection: undefined,
},
};
case photosActions.PHOTOS_ALBUM_FAILURE:
return {
...state,
album: {
status: STATUS_FAILURE,
fetching: false,
data: {},
selection: undefined,
},
};
default:
return state;
}
}
......@@ -12,6 +12,7 @@ import registrationSaga from './registration';
import deepLinkingSaga from './deepLinking';
import membersSaga from './members';
import settingsSaga from './settings';
import photosSaga from './photos';
export default function* () {
yield all([
......@@ -23,6 +24,7 @@ export default function* () {
fork(calendarSaga),
fork(pushNotificationsSaga),
fork(pizzaSaga),
fork(photosSaga),
fork(registrationSaga),
fork(deepLinkingSaga),
fork(membersSaga),
......
......@@ -11,6 +11,7 @@ import * as membersActions from '../actions/members';
import * as welcomeActions from '../actions/welcome';
import { settingsActions } from '../actions/settings';
import NavigationService from '../navigation';
import * as photosActions from '../actions/photos';
function* navigate(routeName) {
yield call(NavigationService.navigate, routeName);
......@@ -42,6 +43,8 @@ export default function* () {
yield takeEvery(registrationActions.FIELDS, navigate, 'Registration');
yield takeEvery(registrationActions.SUCCESS, back);
yield takeEvery(pizzaActions.PIZZA, navigate, 'Pizza');
yield takeEvery(photosActions.PHOTOS_ALBUMS_OPEN, navigate, 'Photos');
yield takeEvery(photosActions.PHOTOS_ALBUM_OPEN, navigate, 'PhotoAlbum');
yield takeEvery(sessionActions.SIGNED_IN, navigate, 'SignedIn');
yield takeEvery([sessionActions.TOKEN_INVALID, sessionActions.SIGN_OUT], navigate, 'Auth');
}
import {
call, put, select, takeEvery,
} from 'redux-saga/effects';
import { apiRequest, tokenSelector } from '../utils/url';
import * as photosActions from '../actions/photos';
function* loadAlbums() {
const token = yield select(tokenSelector);
yield put(photosActions.fetchingAlbums());
const data = {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Token ${token}`,
},
};
const params = {
limit: 12,
};
try {
const response = yield call(apiRequest, 'photos/albums', data, params);
yield put(photosActions.successAlbums(response.results, response.next));
} catch (error) {
yield put(photosActions.failureAlbums());
}
}
function* loadAlbum(action) {
const { pk } = action.payload;
const token = yield select(tokenSelector);
yield put(photosActions.fetchingAlbum());
const data = {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Token ${token}`,
},
};
try {
const album = yield call(apiRequest, `photos/albums/${pk}`, data);
yield put(photosActions.successAlbum(album));
} catch (error) {
yield put(photosActions.failureAlbum());
}
}
export default function* photosSaga() {
yield takeEvery(photosActions.PHOTOS_ALBUMS_OPEN, loadAlbums);
yield takeEvery(photosActions.PHOTOS_ALBUM_OPEN, loadAlbum);
}
import { StyleSheet } from 'react-native';
import Colors from '../../../style/Colors';
import StyleSheet from '../../../style/StyleSheet';