diff --git a/.eslintrc.json b/.eslintrc.json index ef8ecd6db87afabfe4808b27a63635891daba7fe..fabd327f7079220147102f08fc9ba668b2a29a0e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -23,6 +23,7 @@ }, "plugins": ["react", "@typescript-eslint", "prettier"], "rules": { + "linebreak-style": "off", "func-names": "off", "max-len": "error", "no-console": "off", diff --git a/src/SampleGroups.ts b/src/SampleGroups.ts new file mode 100644 index 0000000000000000000000000000000000000000..07475e9069f58eb3cd7f878eb2cd8a5ba5fd3ca0 --- /dev/null +++ b/src/SampleGroups.ts @@ -0,0 +1,67 @@ +import { Group } from './store/groups/GroupState'; + +export const sampleGroups: Group[] = [ + { + groupId: '0', + groupName: 'School Squad', + memberList: [ + { + userId: 'will.brown', + permissions: { admin: true }, + }, + { + userId: 'jayson', + permissions: { admin: false }, + }, + { + userId: 'krisztian', + permissions: { admin: false }, + }, + ], + cacheIdList: [ + '1', + '2', + ], + }, + { + groupId: '1', + groupName: 'Cool Club', + memberList: [ + { + userId: 'will.brown', + permissions: { admin: true }, + }, + { + userId: 'krisztian', + permissions: { admin: false }, + }, + { + userId: 'jason', + permissions: { admin: false }, + }, + { + userId: 'milan.digiuseppe', + permissions: { admin: false }, + }, + { + userId: 'roxy.the.dog', + permissions: { admin: false }, + }, + { + userId: 'clifford.the.big.dog', + permissions: { admin: false }, + }, + { + userId: 'dogmeat', + permissions: { admin: false }, + }, + { + userId: 'epic.gamer.cool.dude', + permissions: { admin: false }, + }, + ], + cacheIdList: [ + '3', + ], + }, +]; diff --git a/src/navigation/HomeNavigator.tsx b/src/navigation/HomeNavigator.tsx index c43e608a283e7c624cf57aaf6dde7f954a41805f..3f7b8d209c7cf736c2fb37a9106ef33a0dee34df 100644 --- a/src/navigation/HomeNavigator.tsx +++ b/src/navigation/HomeNavigator.tsx @@ -8,6 +8,7 @@ import SavedListScreen from '../screens/SavedListScreen'; import CacheDetailScreen, { NavParams } from '../screens/CacheDetailScreen'; import RevisitCacheScreen from '../screens/RevisitCacheScreen'; import DevScreen from '../screens/DevScreen'; +import GroupScreen from '../screens/GroupScreen'; const Stack = createStackNavigator(); const HomeNavigator = () => ( @@ -23,7 +24,7 @@ const HomeNavigator = () => ( ( name={'Dev'} component={DevScreen} /> + ); diff --git a/src/screens/GroupScreen.tsx b/src/screens/GroupScreen.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f308dd18cd48336f50b005aa591257e921ac8ef2 --- /dev/null +++ b/src/screens/GroupScreen.tsx @@ -0,0 +1,146 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { useRoute, useNavigation } from '@react-navigation/native'; + +import { + View, StyleSheet, FlatList, Text, +} from 'react-native'; + + +import { + Avatar, ListItem, Icon, +} from 'react-native-elements'; +import CacheListItem from '../components/CacheListItem'; +import { Cache } from '../store/caches/CachesState'; + + +import GeoText from '../components/GeoText'; +import theme from '../theme'; + +import { + getUserName, + getUserImageUrl, +} from '../store/auth/AuthSelectors'; +import { getGroupMemgers, getGroupCaches, getGroup } from '../store/groups/GroupSelectors'; +import { Membership } from '../store/groups/GroupState'; + +const styles = StyleSheet.create({ + avatar: { + backgroundColor: theme.colors.mediumgray, + marginTop: theme.spacing.medium, + }, +}); + +const renderMember = (item: { item: Membership }) => { + const member = item.item; + return ( + + ); +}; + +const renderCache = (item: {item: Cache}) => { + const { + id, + found, + name, + message, + isUserCreated, + + type, + game, + foundReverseGeocode, + imageUri, + } = item.item; + return ( + + ); +}; + +const GroupScreen = () => { + const { params } = useRoute(); + const userName = useSelector(getUserName); + const userImageUrl = useSelector(getUserImageUrl); + + + const { groupId } = params; + const group = useSelector(getGroup(groupId)); + const memberList = useSelector(getGroupMemgers(groupId)); + const cacheList = useSelector(getGroupCaches(groupId)); + + return ( + + + + + + + + cache.id} + /> + + + + + + member.userId} + /> + + + ); +}; + +export default GroupScreen; diff --git a/src/screens/MainDrawer.tsx b/src/screens/MainDrawer.tsx index 32c963baa44fa0d67421ae68265054a5675408ea..e032b9d92fc3c864b4c2be779e4b5b91750666ae 100644 --- a/src/screens/MainDrawer.tsx +++ b/src/screens/MainDrawer.tsx @@ -1,9 +1,12 @@ import React, { useCallback, useMemo } from 'react'; -import { View, StyleSheet } from 'react-native'; -import { Avatar, ListItem, Icon } from 'react-native-elements'; +import { View, StyleSheet, Group } from 'react-native'; +import { + Avatar, ListItem, Icon, +} from 'react-native-elements'; import { useSelector, useDispatch } from 'react-redux'; import { useNavigation } from '@react-navigation/native'; import { getUserImageUrl, getUserName } from '../store/auth/AuthSelectors'; +import { getGroups } from '../store/groups/GroupSelectors'; import theme from '../theme'; import GeoText from '../components/GeoText'; import GeoButton from '../components/GeoButton'; @@ -40,6 +43,7 @@ const MainDrawer: React.FC = ({ onClose }) => { const { navigate } = useNavigation(); const userImageUrl = useSelector(getUserImageUrl); const userName = useSelector(getUserName); + const groups = useSelector(getGroups); const onSettingsPress = useCallback(() => { onClose(); @@ -55,6 +59,11 @@ const MainDrawer: React.FC = ({ onClose }) => { dispatch(signOutAsync()); }, [dispatch, onClose]); + const onGroupSettings = useCallback(() => { + // onClose(); + navigate('Group', {}); + }, [onClose, navigate]); + const items: ActionItem[] = useMemo(() => ([ { icon: 'user-edit', @@ -73,6 +82,7 @@ const MainDrawer: React.FC = ({ onClose }) => { }, ]), [onSettingsPress, onDevPress]); + return ( @@ -104,6 +114,34 @@ const MainDrawer: React.FC = ({ onClose }) => { onPress={item.onPress} /> ))} + + + { + groups.map((group) => ( + navigate('Group', { groupId: group.groupId })} + /> + )) + } + diff --git a/src/store/RootReducer.ts b/src/store/RootReducer.ts index f58382c825f661486b665e0d37b203bde09cb3d7..27db24d9404f62261b76a554d6ad59a5fb03978e 100644 --- a/src/store/RootReducer.ts +++ b/src/store/RootReducer.ts @@ -3,12 +3,14 @@ import authSlice from './auth/AuthSlice'; import cachesSlice from './caches/CachesSlice'; import locationSlice from './location/LocationSlice'; import pendingCacheSlice from './pendingNewCache/PendingCacheSlice'; +import groupsSlice from './groups/GroupSlice'; const rootReducer = combineReducers({ auth: authSlice.reducer, caches: cachesSlice.reducer, location: locationSlice.reducer, pendingCache: pendingCacheSlice.reducer, + groups: groupsSlice.reducer, }); export type RootState = ReturnType; diff --git a/src/store/groups/GroupActions.ts b/src/store/groups/GroupActions.ts new file mode 100644 index 0000000000000000000000000000000000000000..d080d24f6d0839b2b56f18e21055e6cacddb1fb9 --- /dev/null +++ b/src/store/groups/GroupActions.ts @@ -0,0 +1,2 @@ +import { Dispatch } from '@reduxjs/toolkit'; +import { LatLng } from 'react-native-maps'; diff --git a/src/store/groups/GroupSelectors.ts b/src/store/groups/GroupSelectors.ts new file mode 100644 index 0000000000000000000000000000000000000000..de628bb4064cd8aee82d461682c0516c850863bd --- /dev/null +++ b/src/store/groups/GroupSelectors.ts @@ -0,0 +1,30 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { State } from 'react-native-gesture-handler'; +import { getDiscoveredCaches } from '../caches/CachesSelectors'; +import { RootState } from '../RootReducer'; + +export const getGroups = (state: RootState) => Object.values(state.groups.groups); + +export const getGroup = (id: string) => createSelector( + [getGroups], + (groups) => groups[id], +); + +export const getGroupMemgers = (id: string) => createSelector( + [getGroups], + (groups) => groups[id].memberList, +); + +export const getGroupCaches = (id: string) => createSelector( + [ + getGroups, + (state) => state.caches.saved, + ], + (groups, caches) => { + const result: Cache[] = []; + for (const c in Object.values(groups[id].cacheIdList)) { + result.push(caches[c]); + } + return result; + }, +); diff --git a/src/store/groups/GroupSlice.ts b/src/store/groups/GroupSlice.ts new file mode 100644 index 0000000000000000000000000000000000000000..58a09f7cd7fab36b64a160617c32b83d0111cc24 --- /dev/null +++ b/src/store/groups/GroupSlice.ts @@ -0,0 +1,35 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { sampleCaches } from '../../SampleCaches'; +import { CachesState } from './CachesState'; +import { sampleGroups } from '../../SampleGroups'; +import { GroupState } from './GroupState'; + +const groupsSlice = createSlice({ + name: 'groups', + initialState: { + groups: { + [sampleGroups[0].groupId]: sampleGroups[0], + [sampleGroups[1].groupId]: sampleGroups[1], + }, + } as GroupState, + reducers: { + /* + addSaved(state, action) { + const cache = action.payload; + state.saved[cache.id] = cache; + }, + removeSaved(state, action) { + delete state.saved[action.payload]; + }, + addDiscovered(state, action) { + const cache = action.payload; + state.discovered[cache.id] = cache; + }, + removeDiscovered(state, action) { + delete state.discovered[action.payload]; + }, + */ + }, +}); + +export default groupsSlice; diff --git a/src/store/groups/GroupState.ts b/src/store/groups/GroupState.ts new file mode 100644 index 0000000000000000000000000000000000000000..a690d5aeafd35b6e4d182be2706e46b2f4f1361d --- /dev/null +++ b/src/store/groups/GroupState.ts @@ -0,0 +1,25 @@ +import { User } from 'firebase'; +import { UserInterfaceIdiom } from 'expo-constants'; + + +export interface GroupPermissions { + admin: boolean; +} + +export interface Membership { + userId: string; // correct type ?? + permissions: GroupPermissions; +} + +export interface Group { + groupId: string; + groupName: string; + memberList: Membership[]; + cacheIdList: string[]; // just reference caches.. don't need to duplicate info +} + +export interface GroupState { + groups: { + [id: string]: Group; + }; +}