Repository: Hylozoic/hylo
Pull request #325: Create-group-review-LEJ
Author: login: lorenjohnson
Created: 2025-03-01T12:50:41Z | Merged: 2025-03-02T11:04:23Z

- Updates CreateGroupVisibilityAccessibility to look better, adds matched translated string (title were blank)
- Adds KeyboardManager handling for Purpose screen, removing the legacy hacked together stuff
- Fixes WorkflowModalHeader to default to white background instead of green (Signup which still uses the green background is unaffected as it sets that color explicitly)
- Adds back ParentGroups to Review screen, and now has icons for Group Privacy stuff
- Moves to zustand store


Diff:
diff --git a/apps/mobile/locales/en.json b/apps/mobile/locales/en.json
index 2588fe1b0f..34da24e5ca 100644
--- a/apps/mobile/locales/en.json
+++ b/apps/mobile/locales/en.json
@@ -313,9 +313,7 @@
   "Whats the purpose of the group?": "What's the purpose of the group?",
   "Where do you call home": "Where do you call home",
   "Which of the following skills & interests are relevant to you": "Which of the following skills & interests are relevant to you?",
-  "Who can join this group": "",
   "Who can join this group?": "Who can join this group?",
-  "Who can see this group": "",
   "Who can see this group?": "Who can see this group?",
   "Why was this": "Why was this",
   "Write a comment": "Write a comment...",
diff --git a/apps/mobile/locales/es.json b/apps/mobile/locales/es.json
index e0622bd0d3..d25d2f767d 100644
--- a/apps/mobile/locales/es.json
+++ b/apps/mobile/locales/es.json
@@ -312,9 +312,7 @@
   "Whats the purpose of the group?": "¿Cuál es el propósito del grupo?",
   "Where do you call home": "¿A dónde llamas hogar?",
   "Which of the following skills & interests are relevant to you": "¿Cuál de las siguientes habilidades",
-  "Who can join this group": "¿Quién puede unirse a este grupo?",
   "Who can join this group?": "¿Quién puede unirse a este grupo?",
-  "Who can see this group": "¿Quién puede ver este grupo?",
   "Who can see this group?": "¿Quién puede ver este grupo?",
   "Why was this": "¿Por qué fue esto?",
   "Write a comment": "Escribir un comentario...",
diff --git a/apps/mobile/src/components/RoundCheckBox/RoundCheckbox.js b/apps/mobile/src/components/RoundCheckBox/RoundCheckbox.js
index 37a68d5c3b..a7ea7a7d7f 100644
--- a/apps/mobile/src/components/RoundCheckBox/RoundCheckbox.js
+++ b/apps/mobile/src/components/RoundCheckBox/RoundCheckbox.js
@@ -26,7 +26,7 @@ const RoundCheckbox = ({
 
   return (
     <TouchableWithoutFeedback onPress={handlePress}>
-      <View 
+      <View
         className={`
           justify-center items-center rounded-full border
           ${checked ? 'bg-secondary border-background' : 'border-foreground/50'}
diff --git a/apps/mobile/src/navigation/CreateGroupTabsNavigator.js b/apps/mobile/src/navigation/CreateGroupTabsNavigator.js
index c69b422595..09c35fe38c 100644
--- a/apps/mobile/src/navigation/CreateGroupTabsNavigator.js
+++ b/apps/mobile/src/navigation/CreateGroupTabsNavigator.js
@@ -3,6 +3,7 @@ import { isEmpty } from 'lodash/fp'
 import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
 import WorkflowModalHeader from 'navigation/headers/WorkflowModalHeader'
 import CreateGroupTabBar from 'screens/CreateGroupFlow/CreateGroupTabBar'
+import useCurrentUser from '@hylo/hooks/useCurrentUser'
 import { GROUP_ACCESSIBILITY } from '@hylo/presenters/GroupPresenter'
 // Screens
 import CreateGroupName from 'screens/CreateGroupFlow/CreateGroupName'
@@ -11,8 +12,6 @@ import CreateGroupVisibilityAccessibility from 'screens/CreateGroupFlow/CreateGr
 import CreateGroupPurpose from 'screens/CreateGroupFlow/CreateGroupPurpose'
 import CreateGroupParentGroups from 'screens/CreateGroupFlow/CreateGroupParentGroups'
 import CreateGroupReview from 'screens/CreateGroupFlow/CreateGroupReview'
-import { white20onCaribbeanGreen } from 'style/colors'
-import useCurrentUser from '@hylo/hooks/useCurrentUser'
 
 const CreateGroupTabs = createBottomTabNavigator()
 
@@ -38,7 +37,6 @@ export default function CreateGroupTabsNavigator () {
             {...headerProps}
             headerLeftCloseIcon
             headerLeftOnPress={close}
-            style={{ backgroundColor: white20onCaribbeanGreen }}
           />
         )
       }
diff --git a/apps/mobile/src/navigation/headers/WorkflowModalHeader.js b/apps/mobile/src/navigation/headers/WorkflowModalHeader.js
index b0db17ca11..3f294f05bc 100644
--- a/apps/mobile/src/navigation/headers/WorkflowModalHeader.js
+++ b/apps/mobile/src/navigation/headers/WorkflowModalHeader.js
@@ -1,6 +1,6 @@
 import React from 'react'
 import ModalHeader from './ModalHeader'
-import { white, caribbeanGreen, white60onCaribbeanGreen } from 'style/colors'
+import { white, white60onCaribbeanGreen, rhino } from 'style/colors'
 
 export default function WorkflowModalHeader (props) {
   return (
@@ -8,17 +8,17 @@ export default function WorkflowModalHeader (props) {
       headerBackTitleVisible={false}
       headerLeftCloseIcon={false}
       headerStyle={{
-        backgroundColor: props?.style?.backgroundColor || caribbeanGreen,
+        backgroundColor: props?.style?.backgroundColor || white,
         shadowColor: 'transparent'
       }}
       headerTitleStyle={{
-        color: white,
+        color: rhino,
         fontFamily: 'Circular-Bold',
         fontSize: 16
       }}
       headerTintColor={white60onCaribbeanGreen}
       statusBarOptions={{
-        backgroundColor: props?.style?.backgroundColor || caribbeanGreen,
+        backgroundColor: props?.style?.backgroundColor || white,
         barStyle: 'light-content'
       }}
       {...props}
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.js
index f6f9f1494d..1a925b8d95 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.js
@@ -1,136 +1,43 @@
-import { gql } from 'urql'
-import { GROUP_ACCESSIBILITY, GROUP_VISIBILITY } from '@hylo/presenters/GroupPresenter'
-import groupFieldsFragment from '@hylo/graphql/fragments/groupFieldsFragment'
-import groupPrerequisiteGroupsFieldsFragment from '@hylo/graphql/fragments/groupPrerequisiteGroupsFieldsFragment'
+import { create } from 'zustand'
+import { DEFAULT_ACCESSIBILITY_OPTION, DEFAULT_VISIBILITY_OPTION } from './CreateGroupVisibilityAccessibility'
 
-export const MODULE_NAME = 'CreateGroupFlow'
-export const UPDATE_GROUP_DATA = `${MODULE_NAME}/UPDATE_GROUP_DATA`
-export const FETCH_URL_EXISTS = `${MODULE_NAME}/FETCH_URL_EXISTS`
-export const CREATE_GROUP = `${MODULE_NAME}/CREATE_GROUP`
-export const CLEAR_CREATE_GROUP_STORE = `${MODULE_NAME}/CLEAR_CREATE_GROUP_STORE`
-export const FETCH_GROUP_EXISTS = `${MODULE_NAME}/FETCH_GROUP_EXISTS`
-export const SET_WORKFLOW_OPTIONS = `${MODULE_NAME}/SET_WORKFLOW_OPTIONS`
-
-export const initialState = {
-  // New Group Defaults
+const initialState = {
   groupData: {
     name: '',
     slug: '',
     purpose: '',
-    visibility: GROUP_VISIBILITY.Protected,
-    accessibility: GROUP_ACCESSIBILITY.Restricted,
-    parentIds: []
+    visibility: DEFAULT_VISIBILITY_OPTION,
+    accessibility: DEFAULT_ACCESSIBILITY_OPTION,
+    parentGroups: []
   },
-  workflowOptions: {},
-  urlExists: false,
+  disableContinue: false,
   edited: false
 }
 
-export default function reducer (state = initialState, action) {
-  const { type, payload } = action
-  switch (type) {
-    case SET_WORKFLOW_OPTIONS:
-      return {
-        ...state,
-        workflowOptions: payload
-      }
-    case UPDATE_GROUP_DATA:
-      return {
-        ...state,
-        groupData: {
-          ...state.groupData,
-          ...payload
-        },
-        edited: true
-      }
-    case CLEAR_CREATE_GROUP_STORE:
-      return initialState
-    case FETCH_URL_EXISTS:
-      return {
-        ...state,
-        urlExists: action.payload.data.groupExists.exists,
-        edited: true
-      }
-  }
-  return state
-}
-
-export const createGroupMutation = gql`
-  mutation CreateGroupMutation ($data: GroupInput) {
-    createGroup(data: $data) {
-      ...GroupFieldsFragment
-      ...GroupPrerequisiteGroupsFieldsFragment
-      memberships {
-        items {
-          id
-          hasModeratorRole
-          person {
-            id
-          }
-          settings {
-            agreementsAcceptedAt
-            joinQuestionsAnsweredAt
-            sendEmail
-            showJoinForm
-            sendPushNotifications
-          }
-        }
-      }
-    }
-  }
-  ${groupFieldsFragment}
-  ${groupPrerequisiteGroupsFieldsFragment}
-`
-
-export const groupExistsCheckQuery = gql`
-  query GroupExistsCheckQuery ($slug: String) {
-    groupExists (slug: $slug) {
-      exists
-    }
-  }
-`
-
-export function setWorkflowOptions (value = {}) {
-  return {
-    type: SET_WORKFLOW_OPTIONS,
-    payload: value
-  }
-}
-
-export function updateGroupData (groupData) {
-  return {
-    type: UPDATE_GROUP_DATA,
-    payload: groupData
-  }
-}
+export const useCreateGroupStore = create((set, get) => ({
+  ...initialState,
 
-export function getGroupData (state) {
-  return state[MODULE_NAME]?.groupData
-}
+  setEdited: edited => set({ edited }),
 
-export function getGroupUrlExists (state) {
-  return state[MODULE_NAME].urlExists
-}
+  setDisableContinue: disableContinue => set({ disableContinue }),
 
-export function getWorkflowOptions (state) {
-  return state[MODULE_NAME].workflowOptions
-}
+  updateGroupData: (updates) => set((state) => ({
+    groupData: { ...state.groupData, ...updates },
+    edited: true
+  })),
 
-export function getEdited (state) {
-  return state[MODULE_NAME]?.edited
-}
-
-export function clearCreateGroupStore () {
-  return {
-    type: CLEAR_CREATE_GROUP_STORE
-  }
-}
+  // https://github.com/Hylozoic/hylo/blob/0cc010845b1c9fa9354ad678dd09fca992ae30e2/apps/backend/api/graphql/schema.graphql#L2938-L2939
+  getMutationData: () => {
+    const groupData = get().groupData
+    return {
+      name: groupData.name,
+      slug: groupData.slug,
+      purpose: groupData.purpose,
+      visibility: groupData.visibility ? groupData.visibility.value : null,
+      accessibility: groupData.accessibility ? groupData.accessibility.value : null,
+      parentIds: groupData.parentGroups.map(parentGroup => parentGroup.id)
+    }
+  },
 
-// export const getNewGroupParentGroups = ormCreateSelector(
-//   orm,
-//   getGroupData,
-//   (session, { parentIds }) => session.Group.all()
-//     .toRefArray()
-//     .filter(g => parentIds.includes(g.id))
-//     .sort((a, b) => a.name.localeCompare(b.name))
-// )
+  clearStore: () => set(() => ({ ...initialState }))
+}))
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.test.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.test.js
index c847eea728..4edcec7744 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.test.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.store.test.js
@@ -28,26 +28,6 @@ describe('reducer', () => {
       expect(newState).toEqual(initialState)
     })
   })
-  describe('on FETCH_URL_EXISTS', () => {
-    const exists = true
-    const action = {
-      type: FETCH_URL_EXISTS,
-      payload: {
-        data: {
-          groupExists: {
-            exists
-          }
-        }
-      }
-    }
-    it('sets display', () => {
-      const state = {
-        urlExists: null
-      }
-      const newState = reducer(state, action)
-      expect(newState.urlExists).toEqual(exists)
-    })
-  })
 })
 
 describe('createGroup', () => {
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.styles.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.styles.js
deleted file mode 100644
index 64bc40ab5a..0000000000
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupFlow.styles.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { caribbeanGreen, white, white80onCaribbeanGreen, rhino30 } from 'style/colors'
-import { StyleSheet } from 'react-native'
-import { isIOS } from 'util/platform'
-
-export default {
-  container: {
-    backgroundColor: caribbeanGreen,
-    padding: 20,
-    flex: 1
-  },
-  header: {
-    marginBottom: 20
-  },
-  content: {},
-  footer: {
-    flex: 1,
-    justifyContent: 'flex-end',
-    marginBottom: 40
-  },
-  heading: {
-    color: white,
-    fontSize: 20,
-    fontWeight: 'bold',
-    paddingBottom: 10
-  },
-  description: {
-    color: white80onCaribbeanGreen,
-    marginBottom: 4
-  },
-  button: {
-    width: 134,
-    height: 40,
-    fontSize: 16,
-    backgroundColor: 'white',
-    color: caribbeanGreen,
-    alignSelf: 'flex-end'
-  },
-  textInputContainer: {
-    marginBottom: 15,
-    borderBottomWidth: StyleSheet.hairlineWidth,
-    borderBottomColor: white80onCaribbeanGreen
-  },
-  textInputLabel: {
-    color: white80onCaribbeanGreen,
-    fontWeight: 'bold'
-  },
-  textInput: {
-    color: white,
-    fontSize: 18,
-    fontWeight: 'bold',
-    marginVertical: isIOS ? 10 : 1,
-  },
-  errorBubble: {
-    marginTop: -8
-  },
-  textInputWithButton: {
-    display: 'flex',
-    flexDirection: 'row',
-    justifyContent: 'space-between'
-  },
-  editContainer: {
-    width: 30,
-    height: 30
-  },
-  editText: {
-    color: white,
-    fontSize: 16,
-    marginTop: 10
-  },
-  pressSelectionSection: {
-    borderBottomWidth: 1,
-    padding: 15
-  },
-  pressSelection: {
-    flexDirection: 'row',
-    justifyContent: 'space-between',
-    alignItems: 'center'
-  },
-  pressSelectionRight: {
-    borderWidth: 1,
-    borderRadius: 20,
-    padding: 5
-  },
-  pressSelectionRightIcon: {
-    fontSize: 20
-  },
-  pressSelectionValue: {
-    marginTop: 10,
-    fontSize: 16
-  },
-  groupRemoveIcon: {
-    fontSize: 16,
-    color: rhino30
-  }
-}
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupName.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupName.js
index ad2f0756e5..8329536510 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupName.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupName.js
@@ -1,31 +1,24 @@
 import React, { useCallback, useEffect, useState } from 'react'
-import { useSelector, useDispatch } from 'react-redux'
-import { useFocusEffect } from '@react-navigation/native'
 import { Text, View, ScrollView, TextInput } from 'react-native'
+import { useFocusEffect } from '@react-navigation/native'
+import { useTranslation } from 'react-i18next'
 import useCurrentGroup from '@hylo/hooks/useCurrentGroup'
 import useRouteParams from 'hooks/useRouteParams'
-import {
-  getGroupData, getEdited, updateGroupData, setWorkflowOptions,
-  clearCreateGroupStore
-} from './CreateGroupFlow.store'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import ErrorBubble from 'components/ErrorBubble'
-import styles from './CreateGroupFlow.styles'
-import { useTranslation } from 'react-i18next'
 
 export default function CreateGroupName ({ route }) {
-  const dispatch = useDispatch()
   const { t } = useTranslation()
   // Add current group in as pre-selected as a parent group for Parent Groups Step
-  const edited = useSelector(getEdited)
+  const { groupData, updateGroupData, edited, setDisableContinue, clearStore } = useCreateGroupStore()
   const [{ currentGroup }] = useCurrentGroup()
-  const groupData = useSelector(getGroupData)
   const [groupName, setGroupName] = useState()
   const [error, setError] = useState()
   const { reset } = useRouteParams()
 
   useEffect(() => {
     if (reset) {
-      dispatch(clearCreateGroupStore())
+      clearStore()
       setGroupName('')
     } else {
       setGroupName(groupData?.name)
@@ -34,32 +27,32 @@ export default function CreateGroupName ({ route }) {
 
   useFocusEffect(useCallback(() => {
     if (!groupName || groupName.length === 0) {
-      dispatch(setWorkflowOptions({ disableContinue: true }))
+      setDisableContinue(true)
     } else {
-      dispatch(updateGroupData({ name: groupName }))
+      updateGroupData({ name: groupName })
       setError()
-      dispatch(setWorkflowOptions({ disableContinue: false }))
+      setDisableContinue(false)
     }
   }, [groupName]))
 
   useFocusEffect(useCallback(() => {
     if (!edited && !currentGroup?.isContextGroup) {
-      dispatch(updateGroupData({ parentIds: [currentGroup?.id] }))
+      updateGroupData({ parentGroups: [currentGroup] })
     }
   }, [edited, currentGroup?.id]))
 
   return (
-    <View className="bg-background p-5 flex-1">
+    <View className='bg-background p-5 flex-1'>
       <ScrollView keyboardDismissMode='on-drag' keyboardShouldPersistTaps='handled'>
-        <View className="mb-5">
-          <Text className="text-foreground text-xl font-bold pb-2.5">{t('Lets get started!')}</Text>
-          <Text className="text-foreground/80 mb-1">{t('All good things start somewhere! Lets kick things off with a catchy name for your group')}</Text>
+        <View className='mb-5'>
+          <Text className='text-foreground text-xl font-bold pb-2.5'>{t('Lets get started!')}</Text>
+          <Text className='text-foreground/80 mb-1'>{t('All good things start somewhere! Lets kick things off with a catchy name for your group')}</Text>
         </View>
         <View>
-          <View className="mb-4 border-b border-foreground/20">
-            <Text className="text-foreground/90 font-bold">{t('Whats the name of your group?')}</Text>
+          <View className='mb-4 border-b border-foreground/20'>
+            <Text className='text-foreground/90 font-bold'>{t('Whats the name of your group?')}</Text>
             <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
+              className='text-foreground text-lg font-bold my-2.5'
               onChangeText={setGroupName}
               returnKeyType='next'
               autoCapitalize='none'
@@ -69,7 +62,7 @@ export default function CreateGroupName ({ route }) {
               maxLength={60}
             />
           </View>
-          {error && <View className="mt-[-8]"><ErrorBubble text={error} topArrow /></View>}
+          {error && <View className='mt-[-8]'><ErrorBubble text={error} topArrow /></View>}
         </View>
       </ScrollView>
     </View>
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupParentGroups.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupParentGroups.js
index bf21f53946..6bbf3124c2 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupParentGroups.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupParentGroups.js
@@ -1,29 +1,21 @@
-import React, { useState, useCallback, useRef } from 'react'
-import { useSelector, useDispatch } from 'react-redux'
-import { useFocusEffect } from '@react-navigation/native'
+import React, { useRef } from 'react'
 import { Text, TouchableOpacity, View } from 'react-native'
-import KeyboardFriendlyView from 'components/KeyboardFriendlyView'
-import { GROUP_ACCESSIBILITY } from '@hylo/presenters/GroupPresenter'
-import { getGroupData, updateGroupData } from './CreateGroupFlow.store'
 import { useTranslation } from 'react-i18next'
-import useCurrentUser from '@hylo/hooks/useCurrentUser'
-import ItemSelectorModal from 'components/ItemSelectorModal'
 import FastImage from 'react-native-fast-image'
+import { GROUP_ACCESSIBILITY } from '@hylo/presenters/GroupPresenter'
+import useCurrentUser from '@hylo/hooks/useCurrentUser'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import Icon from 'components/Icon'
-
-const styles = {
-  pressSelectionValue: {
-    marginTop: 10,
-    fontSize: 16
-  }
-}
+import ItemSelectorModal from 'components/ItemSelectorModal'
+import KeyboardFriendlyView from 'components/KeyboardFriendlyView'
 
 export default function CreateGroupParentGroups ({ navigation }) {
-  const dispatch = useDispatch()
   const { t } = useTranslation()
   const [{ currentUser }] = useCurrentUser()
-  const groupData = useSelector(getGroupData)
-  const [parentIds, setParentGroupIds] = useState(groupData.parentIds)
+  const { groupData, updateGroupData } = useCreateGroupStore()
+  const parentGroups = groupData.parentGroups
+  const setParentGroups = newParentGroups => updateGroupData({ parentGroups: newParentGroups })
+  const clearParentGroups = () => setParentGroups([])
   const memberships = currentUser?.memberships
   const parentGroupOptions = memberships
     .filter(m => m.hasModeratorRole || m.group.accessibility === GROUP_ACCESSIBILITY.Open)
@@ -32,47 +24,37 @@ export default function CreateGroupParentGroups ({ navigation }) {
 
   const groupSelectorModalRef = useRef(null)
 
-  const isChosen = item => !!parentIds.find(groupId => groupId === item.id)
+  const isChosen = group => !!parentGroups.find(parentGroup => parentGroup.id === group.id)
 
   const handleAddGroup = group => {
     if (!isChosen(group)) {
-      setParentGroupIds([...parentIds, group.id])
+      setParentGroups([...parentGroups, group])
     }
   }
 
-  const handleRemoveGroup = groupSlug => {
-    const group = parentGroupOptions.find(g => g.slug === groupSlug)
-    if (group) {
-      setParentGroupIds(parentIds.filter(id => id !== group.id))
-    }
+  const handleRemoveGroup = removingGroup => {
+    setParentGroups(parentGroups.filter(parentGroup => parentGroup.id !== removingGroup.id))
   }
 
-  const clear = () => setParentGroupIds([])
-
-  useFocusEffect(useCallback(() => {
-    // Extra step necessary to reject a default group selection which isn't valid
-    parentIds.filter(id => parentGroupOptions.find(validId => id === validId))
-    dispatch(updateGroupData({ parentIds }))
-  }, [parentIds]))
-
-  const selectedGroups = parentGroupOptions.filter(group => isChosen(group))
+  // Extra step necessary to reject a default group selection which isn't valid
+  // groupData.parentGroups.filter(parentGroup => parentGroupOptions.find(validGroup => parentGroup.id === validGroup.id))
 
   return (
-    <KeyboardFriendlyView className="bg-background p-5 flex-1">
-      <Text className="text-foreground text-xl font-bold pb-2.5">{t('Is this group a member of other groups?')}</Text>
-      <Text className="text-foreground/80 mb-2.5">{t('Please select below:')}</Text>
+    <KeyboardFriendlyView className='bg-background p-5 flex-1'>
+      <Text className='text-foreground text-xl font-bold pb-2.5'>{t('Is this group a member of other groups?')}</Text>
+      <Text className='text-foreground/80 mb-2.5'>{t('Please select below:')}</Text>
 
-      <View className="border border-foreground/20 bg-card rounded-lg p-4">
-        <TouchableOpacity 
-          className="flex-row justify-between items-center"
+      <View className='border border-foreground/20 bg-card rounded-lg p-4'>
+        <TouchableOpacity
+          className='flex-row justify-between items-center'
           onPress={() => groupSelectorModalRef.current.show()}
         >
-          <Text className="text-foreground/90 font-bold">{t('Parent Groups')}</Text>
-          <View className="border border-foreground rounded-full p-1">
-            <Icon className="text-foreground text-xl" name='Plus' />
+          <Text className='text-foreground/90 font-bold'>{t('Parent Groups')}</Text>
+          <View className='border border-foreground rounded-full p-1'>
+            <Icon className='text-foreground text-xl' name='Plus' />
           </View>
         </TouchableOpacity>
-        
+
         <ItemSelectorModal
           ref={groupSelectorModalRef}
           title={t('Select Parent Groups')}
@@ -83,37 +65,45 @@ export default function CreateGroupParentGroups ({ navigation }) {
               : item
             )
           )}
-          chosenItems={selectedGroups}
+          chosenItems={parentGroups}
           onItemPress={handleAddGroup}
           searchPlaceholder={t('Search for group by name')}
         />
 
-        {selectedGroups.length > 0 && (
-          <View className="mt-3 space-y-3">
-            {selectedGroups.map(group => (
-              <View key={group.id} className="flex-row items-center py-0.5 px-1 gap-1">
-                <FastImage 
-                  source={{ uri: group.avatarUrl }} 
-                  style={{ width: 20, height: 20, borderRadius: 10 }}
-                  className="mr-3"
-                />
-                <Text className="flex-1 text-foreground text-base">{group.name}</Text>
-                <TouchableOpacity
-                  className="ml-3"
-                  hitSlop={{ top: 5, right: 5, left: 5, bottom: 5 }}
-                  onPress={() => handleRemoveGroup(group.slug)}
-                >
-                  <Icon className="text-foreground/60 text-lg" name='Ex' />
-                </TouchableOpacity>
-              </View>
+        {parentGroups.length > 0 && (
+          <View className='mt-3 space-y-3'>
+            {parentGroups.map(group => (
+              <GroupRow group={group} onRemove={handleRemoveGroup} key={group.id} />
             ))}
           </View>
         )}
       </View>
 
-      <TouchableOpacity onPress={clear}>
-        <Text className="text-foreground font-bold mt-2.5 self-end mr-5">{t('Clear')}</Text>
+      <TouchableOpacity onPress={clearParentGroups}>
+        <Text className='text-foreground font-bold mt-2.5 self-end mr-5'>{t('Clear')}</Text>
       </TouchableOpacity>
     </KeyboardFriendlyView>
   )
 }
+
+export function GroupRow ({ group, onRemove }) {
+  return (
+    <View className='flex-row items-center py-0.5 px-1 gap-1'>
+      <FastImage
+        source={{ uri: group.avatarUrl }}
+        style={{ width: 20, height: 20, borderRadius: 10 }}
+        className='mr-3'
+      />
+      <Text className='flex-1 text-foreground text-base font-bold'>{group.name}</Text>
+      {onRemove && (
+        <TouchableOpacity
+          className='ml-3'
+          hitSlop={{ top: 5, right: 5, left: 5, bottom: 5 }}
+          onPress={() => onRemove(group)}
+        >
+          <Icon className='text-foreground/60 text-lg' name='Ex' />
+        </TouchableOpacity>
+      )}
+    </View>
+  )
+}
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupPurpose.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupPurpose.js
index 0a6429168d..81254ddc3f 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupPurpose.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupPurpose.js
@@ -1,31 +1,32 @@
 import React, { useCallback, useEffect, useState } from 'react'
-import { useSelector, useDispatch } from 'react-redux'
 import { useFocusEffect } from '@react-navigation/native'
 import { Text, View, ScrollView, TextInput } from 'react-native'
 import { useTranslation } from 'react-i18next'
-import useRouteParams from 'hooks/useRouteParams'
+import KeyboardManager from 'react-native-keyboard-manager'
 import useCurrentGroup from '@hylo/hooks/useCurrentGroup'
-import {
-  getGroupData, getEdited, updateGroupData, setWorkflowOptions,
-  clearCreateGroupStore
-} from './CreateGroupFlow.store'
+import useRouteParams from 'hooks/useRouteParams'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import ErrorBubble from 'components/ErrorBubble'
-import styles from './CreateGroupFlow.styles'
 
 export default function CreateGroupPurpose ({ route }) {
   const { t } = useTranslation()
-  const dispatch = useDispatch()
+  const { edited, groupData, updateGroupData, setDisableContinue, clearStore } = useCreateGroupStore()
   // Add current group in as pre-selected as a parent group for Parent Groups Step
-  const edited = useSelector(getEdited)
   const [{ currentGroup }] = useCurrentGroup()
-  const groupData = useSelector(getGroupData)
   const [groupPurpose, setGroupPurpose] = useState()
   const [error, setError] = useState()
   const { reset } = useRouteParams()
 
+  useFocusEffect(useCallback(() => {
+    KeyboardManager.setEnable(true)
+    return () => {
+      KeyboardManager.setEnable(false)
+    }
+  }, []))
+
   useEffect(() => {
     if (reset) {
-      dispatch(clearCreateGroupStore())
+      clearStore()
       setGroupPurpose('')
     } else {
       setGroupPurpose(groupData?.purpose)
@@ -33,30 +34,30 @@ export default function CreateGroupPurpose ({ route }) {
   }, [reset])
 
   useFocusEffect(useCallback(() => {
-    dispatch(updateGroupData({ purpose: groupPurpose }))
+    updateGroupData({ purpose: groupPurpose })
     setError()
-    dispatch(setWorkflowOptions({ disableContinue: false }))
+    setDisableContinue(false)
   }, [groupPurpose]))
 
   useFocusEffect(useCallback(() => {
     if (!edited && !currentGroup?.isContextGroup) {
-      dispatch(updateGroupData({ parentIds: [currentGroup?.id] }))
+      updateGroupData({ parentGroups: [currentGroup] })
     }
   }, [edited, currentGroup?.id]))
 
   return (
-    <View className="bg-background p-5 flex-1">
+    <View className='bg-background p-5 flex-1'>
       <ScrollView keyboardDismissMode='on-drag' keyboardShouldPersistTaps='handled'>
-        <View className="mb-5">
-          <Text className="text-foreground text-xl font-bold pb-2.5">{t('Group Purpose')}</Text>
-          <Text className="text-foreground/80 mb-1">{t('Your purpose statement is a concise summary of why your group')}</Text>
-          <Text className="text-foreground/80 mb-1">{t('Aim for one or two sentences')}</Text>
+        <View className='mb-5'>
+          <Text className='text-foreground text-xl font-bold pb-2.5'>{t('Group Purpose')}</Text>
+          <Text className='text-foreground/80 mb-1'>{t('Your purpose statement is a concise summary of why your group')}</Text>
+          <Text className='text-foreground/80 mb-1'>{t('Aim for one or two sentences')}</Text>
         </View>
         <View>
-          <View className="mb-4 border-b border-foreground/20">
-            <Text className="text-foreground/90 font-bold">{t('Whats the purpose of the group?')}</Text>
+          <View className='mb-4 border-b border-foreground/20'>
+            <Text className='text-foreground/90 font-bold'>{t('Whats the purpose of the group?')}</Text>
             <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
+              className='text-foreground text-lg my-2.5'
               onChangeText={setGroupPurpose}
               returnKeyType='next'
               autoCapitalize='none'
@@ -67,7 +68,7 @@ export default function CreateGroupPurpose ({ route }) {
               multiline
             />
           </View>
-          {error && <View className="mt-[-8]"><ErrorBubble text={error} topArrow /></View>}
+          {error && <View className='mt-[-8]'><ErrorBubble text={error} topArrow /></View>}
         </View>
       </ScrollView>
     </View>
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupReview.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupReview.js
index da32dfc47a..1012a175f3 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupReview.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupReview.js
@@ -1,30 +1,24 @@
 import React, { useCallback, useEffect, useState } from 'react'
-import { useDispatch, useSelector } from 'react-redux'
-import {
-  Text, View, TextInput, ScrollView, TouchableOpacity
-} from 'react-native'
+import { Text, View, TextInput, ScrollView, TouchableOpacity } from 'react-native'
 import { useMutation } from 'urql'
 import { useNavigation } from '@react-navigation/native'
 import { useTranslation } from 'react-i18next'
+import createGroupMutation from '@hylo/graphql/mutations/createGroupMutation'
 import { AnalyticsEvents } from '@hylo/shared'
 import mixpanel from 'services/mixpanel'
 import { formatDomainWithUrl } from './util'
-import { createGroupMutation, clearCreateGroupStore, getGroupData } from './CreateGroupFlow.store'
-import { accessibilityDescription, visibilityDescription } from '@hylo/presenters/GroupPresenter'
 import { openURL } from 'hooks/useOpenURL'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import ErrorBubble from 'components/ErrorBubble'
-import Avatar from 'components/Avatar'
-import styles from './CreateGroupFlow.styles'
+import { GroupPrivacyOption } from './CreateGroupVisibilityAccessibility'
+import { GroupRow } from './CreateGroupParentGroups'
 import { white } from 'style/colors'
 
 export default function CreateGroupReview () {
   const { t } = useTranslation()
   const navigation = useNavigation()
-  const dispatch = useDispatch()
+  const { groupData, getMutationData, clearStore } = useCreateGroupStore()
   const [, createGroup] = useMutation(createGroupMutation)
-  const groupData = useSelector(getGroupData)
-  // TODO: URQL! - query for parent groups, remove related method in store
-  // const parentGroups = useSelector(getNewGroupParentGroups)
   const [error, setError] = useState(null)
 
   useEffect(() => {
@@ -36,11 +30,11 @@ export default function CreateGroupReview () {
 
   const submit = useCallback(async () => {
     try {
-      const { data, error } = await createGroup({ data: groupData })
+      const { data, error } = await createGroup({ data: getMutationData() })
       const newGroup = data?.createGroup
       if (newGroup) {
         mixpanel.track(AnalyticsEvents.GROUP_CREATED)
-        dispatch(clearCreateGroupStore())
+        clearStore()
         openURL(`/groups/${newGroup.slug}`)
       } else {
         setError('Group may have been created, but there was an error. Please contact Hylo support.', error)
@@ -51,20 +45,20 @@ export default function CreateGroupReview () {
   }, [groupData])
 
   return (
-    <View className="bg-background p-5 flex-1">
-      <ScrollView className="flex-1">
-        <View className="mb-5">
-          <Text className="text-foreground text-xl font-bold pb-2.5">{t('Everything look good?')}</Text>
-          <Text className="text-foreground/80 mb-1">{t('You can always come back and change your details at any time')}</Text>
+    <View className='bg-background p-5 flex-1'>
+      <ScrollView className='flex-1'>
+        <View className='mb-5'>
+          <Text className='text-foreground text-xl font-bold pb-2.5'>{t('Everything look good?')}</Text>
+          <Text className='text-foreground/80 mb-1'>{t('You can always come back and change your details at any time')}</Text>
         </View>
-        <View className="flex-1">
-          <View className="mb-4 border-b border-foreground/20">
-            <View className="flex-row justify-between items-center">
-              <Text className="text-foreground/90 font-bold">{t('Whats the name of your group?')}</Text>
+        <View className='flex-1'>
+          <View className='mb-4 pb-2.5 border-b border-foreground/20'>
+            <View className='flex-row justify-between items-center'>
+              <Text className='text-foreground/90 font-bold'>{t('Whats the name of your group?')}</Text>
               <EditButton onPress={() => navigation.navigate('CreateGroupName')} />
             </View>
             <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
+              className='text-foreground text-lg'
               value={groupData.name}
               underlineColorAndroid='transparent'
               editable={false}
@@ -72,13 +66,13 @@ export default function CreateGroupReview () {
             />
           </View>
 
-          <View className="mb-4 border-b border-foreground/20">
-            <View className="flex-row justify-between items-center">
-              <Text className="text-foreground/90 font-bold">{t('Whats the URL of your group?')}</Text>
+          <View className='mb-4 pb-2.5 border-b border-foreground/20'>
+            <View className='flex-row justify-between items-center'>
+              <Text className='text-foreground/90 font-bold'>{t('Whats the URL of your group?')}</Text>
               <EditButton onPress={() => navigation.navigate('CreateGroupUrl')} />
             </View>
             <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
+              className='text-foreground text-lg'
               value={formatDomainWithUrl(groupData.slug)}
               underlineColorAndroid='transparent'
               editable={false}
@@ -86,13 +80,13 @@ export default function CreateGroupReview () {
             />
           </View>
 
-          <View className="mb-4 border-b border-foreground/20">
-            <View className="flex-row justify-between items-center">
-              <Text className="text-foreground/90 font-bold">{t('What is the purpose of this group')}</Text>
+          <View className='mb-4 pb-2.5 border-b border-foreground/20'>
+            <View className='flex-row justify-between items-center'>
+              <Text className='text-foreground/90 font-bold'>{t('What is the purpose of this group')}</Text>
               <EditButton onPress={() => navigation.navigate('CreateGroupPurpose')} />
             </View>
             <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
+              className='text-foreground text-lg'
               multiline
               value={groupData.purpose}
               underlineColorAndroid='transparent'
@@ -101,68 +95,47 @@ export default function CreateGroupReview () {
             />
           </View>
 
-          <View className="mb-4 border-b border-foreground/20">
-            <View className="flex-row justify-between items-center">
-              <Text className="text-foreground/90 font-bold">{t('Who can see this group?')}</Text>
+          <View className='mb-4 pb-2.5 border-b border-foreground/20'>
+            <View className='flex-row justify-between items-center'>
+              <Text className='text-foreground/90 font-bold'>{t('Who can see this group?')}</Text>
               <EditButton onPress={() => navigation.navigate('CreateGroupVisibilityAccessibility')} />
             </View>
-            <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
-              multiline
-              value={visibilityDescription(groupData.visibility)}
-              underlineColorAndroid='transparent'
-              editable={false}
-              selectTextOnFocus={false}
-            />
+            <GroupPrivacyOption option={groupData.visibility} />
           </View>
 
-          <View className="mb-4 border-b border-foreground/20">
-            <View className="flex-row justify-between items-center">
-              <Text className="text-foreground/90 font-bold">{t('Who can join this group?')}</Text>
+          <View className='mb-4 pb-2.5 border-b border-foreground/20'>
+            <View className='flex-row justify-between items-center'>
+              <Text className='text-foreground/90 font-bold'>{t('Who can join this group?')}</Text>
               <EditButton onPress={() => navigation.navigate('CreateGroupVisibilityAccessibility')} />
             </View>
-            <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
-              multiline
-              value={accessibilityDescription(groupData.accessibility)}
-              underlineColorAndroid='transparent'
-              editable={false}
-              selectTextOnFocus={false}
-            />
+            <GroupPrivacyOption option={groupData.accessibility} />
           </View>
         </View>
 
-        {/* {parentGroups.length > 0 && (
-          <View style={styles.textInputContainer}>
-            <View style={stepStyles.itemHeader}>
-              <Text style={stepStyles.textInputLabel}>{t('Is this group a member of other groups?')}</Text>
+        {groupData.parentGroups.length > 0 && (
+          <View className='mb-4 pb-2.5 border-b border-foreground/20'>
+            <View className='flex-row justify-between items-center'>
+              <Text className='text-foreground/90 font-bold'>{t('Is this group a member of other groups?')}</Text>
               <EditButton onPress={() => navigation.navigate('CreateGroupParentGroups')} />
             </View>
             <View style={stepStyles.groupRows}>
-              {parentGroups.map(parentGroup => <GroupRow group={parentGroup} key={parentGroup.id} />)}
+              {groupData.parentGroups.map(parentGroup => <GroupRow group={parentGroup} key={parentGroup.id} />)}
             </View>
           </View>
-        )} */}
+        )}
 
-        {error && <View className="mt-[-8]"><ErrorBubble text={error} /></View>}
+        {error && <View className='mt-[-8]'><ErrorBubble text={error} /></View>}
       </ScrollView>
     </View>
   )
 }
 
-const GroupRow = ({ group }) => (
-  <View className="flex-row mb-2.5" key={group.name}>
-    <Avatar className="mr-3.5" avatarUrl={group.avatarUrl} dimension={20} />
-    <Text className="text-foreground font-circular-bold flex-1 text-sm">{group.name}</Text>
-  </View>
-)
-
 const EditButton = ({ onPress }) => {
   const { t } = useTranslation()
 
   return (
     <TouchableOpacity onPress={onPress}>
-      <Text className="text-foreground/80 text-xs font-bold">{t('Edit')}</Text>
+      <Text className='text-foreground/80 text-xs font-bold'>{t('Edit')}</Text>
     </TouchableOpacity>
   )
 }
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupTabBar.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupTabBar.js
index 44a86f6de1..e56eadd256 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupTabBar.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupTabBar.js
@@ -1,45 +1,18 @@
-import React, { useEffect, useState } from 'react'
-import { LayoutAnimation, View, Keyboard } from 'react-native'
-import { useSelector } from 'react-redux'
-import { getWorkflowOptions } from 'screens/CreateGroupFlow/CreateGroupFlow.store'
-import { isIOS } from 'util/platform'
-import { useKeyboard } from '@react-native-community/hooks'
+import React, { useState } from 'react'
+import { View } from 'react-native'
 import { useTranslation } from 'react-i18next'
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import ButtonNW from 'components/Button/ButtonNW'
 
 export default function CreateGroupTabBar ({ state, descriptors, navigation }) {
   const { t } = useTranslation()
-  const workflowOptions = useSelector(getWorkflowOptions)
-  const disableContinue = !!workflowOptions?.disableContinue
+  const insets = useSafeAreaInsets()
+  const { disableContinue } = useCreateGroupStore()
   const [completeButtonDisabled, setCompleteButtonDisabled] = useState(false)
   const prevStepScreenName = state.routeNames[state.index - 1]
   const nextStepScreenName = state.routeNames[state.index + 1]
   const currentStepRouteKey = state.routes[state.index].key
-  const keyboard = useKeyboard()
-  const [keyboardWillShow, setKeyboardWillShow] = useState(false)
-
-  useEffect(() => {
-    const willShowSubscription = Keyboard.addListener('keyboardWillShow', (e) => {
-      LayoutAnimation.configureNext(LayoutAnimation.create(
-        e.duration,
-        LayoutAnimation.Types[e.easing],
-        LayoutAnimation.Properties.scaleXY
-      ))
-      setKeyboardWillShow(true)
-    })
-    const willHideSubscription = Keyboard.addListener('keyboardWillHide', (e) => {
-      LayoutAnimation.configureNext(LayoutAnimation.create(
-        e.duration,
-        LayoutAnimation.Types[e.easing],
-        LayoutAnimation.Properties.scaleXY
-      ))
-      setKeyboardWillShow(false)
-    })
-    return () => {
-      willShowSubscription.remove()
-      willHideSubscription.remove()
-    }
-  }, [])
 
   const gotoPrevStep = () => {
     setCompleteButtonDisabled(false)
@@ -58,22 +31,15 @@ export default function CreateGroupTabBar ({ state, descriptors, navigation }) {
     setCompleteButtonDisabled(true)
   }
 
-  const keyboardAdjustedHeight = keyboardWillShow
-    ? keyboard.keyboardHeight + (isIOS ? 60 : 40)
-    : (isIOS ? 80 : 60)
-
   return (
-    <View 
-      className="flex-row justify-between px-5 pt-2.5 bg-midground"
-      style={{ height: keyboardAdjustedHeight }}
-    >
-      <View className="w-[100px]">
+    <View className='flex-row justify-between px-5 bg-midground' style={{ paddingBottom: insets.bottom }}>
+      <View className='w-[100px]'>
         {prevStepScreenName && (
           <ButtonNW
             text={t('< Back')}
             onPress={gotoPrevStep}
-            className="w-[100px] bg-foreground/5"
-            textClassName="text-foreground/80"
+            className='w-[100px] bg-foreground/5'
+            textClassName='text-foreground/80'
           />
         )}
       </View>
@@ -82,17 +48,17 @@ export default function CreateGroupTabBar ({ state, descriptors, navigation }) {
           text={t('Continue')}
           onPress={gotoNextStep}
           disabled={disableContinue}
-          className="w-[134px] border-2 border-foreground/50"
-          textClassName="text-foreground"
+          className='w-[134px] border-2 border-foreground/50'
+          textClassName='text-foreground'
         />
       )}
       {!nextStepScreenName && (
         <ButtonNW
-          text={t("Lets Do This!")}
+          text={t('Lets Do This!')}
           onPress={completeWorkflow}
           disabled={completeButtonDisabled}
-          className="w-[134px] border-2 border-foreground/50"
-          textClassName="text-foreground"
+          className='w-[134px] border-2 border-foreground/50'
+          textClassName='text-foreground'
         />
       )}
     </View>
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupUrl.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupUrl.js
index 19f0a73c6e..90a0203ca3 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupUrl.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupUrl.js
@@ -1,31 +1,23 @@
 import React, { useCallback, useMemo, useState } from 'react'
-import { debounce } from 'lodash/fp'
-import { useSelector, useDispatch } from 'react-redux'
-import { useQuery } from 'urql'
 import { Text, View, ScrollView, TextInput } from 'react-native'
 import { useFocusEffect } from '@react-navigation/native'
+import { useQuery } from 'urql'
 import { useTranslation } from 'react-i18next'
+import { debounce } from 'lodash/fp'
+import groupExistsQuery from '@hylo/graphql/queries/groupExistsQuery'
+import { slugValidatorRegex, invalidSlugMessage, formatDomainWithUrl, removeDomainFromURL } from './util'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import ErrorBubble from 'components/ErrorBubble'
-import {
-  slugValidatorRegex, invalidSlugMessage,
-  formatDomainWithUrl, removeDomainFromURL
-} from './util'
-import {
-  updateGroupData, setWorkflowOptions,
-  groupExistsCheckQuery, getGroupData
-} from './CreateGroupFlow.store'
-import styles from './CreateGroupFlow.styles'
 
 export default function CreateGroupUrl ({ navigation }) {
   const { t } = useTranslation()
-  const dispatch = useDispatch()
-  const groupData = useSelector(getGroupData)
+  const { groupData, updateGroupData, setDisableContinue } = useCreateGroupStore()
   const [error, providedSetError] = useState()
   const [groupSlug, setGroupSlug] = useState(groupData?.slug)
-  const [groupExistsCheckResult] = useQuery({ query: groupExistsCheckQuery, variables: { slug: groupSlug } })
+  const [groupExistsCheckResult] = useQuery({ query: groupExistsQuery, variables: { slug: groupSlug } })
 
   const setError = error => {
-    dispatch(setWorkflowOptions({ disableContinue: true }))
+    setDisableContinue(true)
     providedSetError(error)
   }
   const clearError = () => providedSetError()
@@ -34,7 +26,7 @@ export default function CreateGroupUrl ({ navigation }) {
     try {
       if (!slug || slug.length === 0) {
         // setError('Please enter a URL')
-        dispatch(setWorkflowOptions({ disableContinue: true }))
+        setDisableContinue(true)
         return false
       }
 
@@ -48,9 +40,9 @@ export default function CreateGroupUrl ({ navigation }) {
       if (result?.error) {
         setError(t('There was an error please try again'))
       } else if (groupExists === false) {
-        dispatch(updateGroupData({ slug }))
+        updateGroupData({ slug })
         clearError()
-        dispatch(setWorkflowOptions({ disableContinue: false }))
+        setDisableContinue(false)
       } else if (groupExists) {
         setError(t('This URL already exists Please choose another one'))
       } else {
@@ -63,22 +55,22 @@ export default function CreateGroupUrl ({ navigation }) {
   }), [])
 
   useFocusEffect(useCallback(() => {
-    dispatch(setWorkflowOptions({ disableContinue: true }))
+    setDisableContinue(true)
     validateAndSave(groupExistsCheckResult, groupSlug)
   }, [groupExistsCheckResult?.data]))
 
   return (
-    <View className="bg-background p-5 flex-1">
+    <View className='bg-background p-5 flex-1'>
       <ScrollView keyboardDismissMode='on-drag' keyboardShouldPersistTaps='handled'>
-        <View className="mb-5">
-          <Text className="text-foreground text-xl font-bold pb-2.5">{t('Choose an address for your group')}</Text>
-          <Text className="text-foreground/80 mb-1">{t('Your URL is the address that members will use to access your group online The shorter the better')}</Text>
+        <View className='mb-5'>
+          <Text className='text-foreground text-xl font-bold pb-2.5'>{t('Choose an address for your group')}</Text>
+          <Text className='text-foreground/80 mb-1'>{t('Your URL is the address that members will use to access your group online The shorter the better')}</Text>
         </View>
         <View>
-          <View className="mb-4 border-b border-foreground/20">
-            <Text className="text-foreground/90 font-bold">{t('Whats the address for your group')}</Text>
+          <View className='mb-4 border-b border-foreground/20'>
+            <Text className='text-foreground/90 font-bold'>{t('Whats the address for your group')}</Text>
             <TextInput
-              className="text-foreground text-lg font-bold my-2.5"
+              className='text-foreground text-lg font-bold my-2.5'
               onChangeText={slug => setGroupSlug(removeDomainFromURL(slug))}
               returnKeyType='next'
               autoCapitalize='none'
@@ -87,7 +79,7 @@ export default function CreateGroupUrl ({ navigation }) {
               underlineColorAndroid='transparent'
             />
           </View>
-          {error && <View className="mt-[-8]"><ErrorBubble text={error} topArrow /></View>}
+          {error && <View className='mt-[-8]'><ErrorBubble text={error} topArrow /></View>}
         </View>
       </ScrollView>
     </View>
diff --git a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupVisibilityAccessibility.js b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupVisibilityAccessibility.js
index d94c7a6ae5..367209334c 100644
--- a/apps/mobile/src/screens/CreateGroupFlow/CreateGroupVisibilityAccessibility.js
+++ b/apps/mobile/src/screens/CreateGroupFlow/CreateGroupVisibilityAccessibility.js
@@ -1,19 +1,16 @@
 import React, { useCallback, useState } from 'react'
-import { useSelector, useDispatch } from 'react-redux'
 import { useFocusEffect } from '@react-navigation/native'
 import { Text, View, ScrollView, TouchableOpacity } from 'react-native'
-import RoundCheckbox from 'components/RoundCheckBox'
-import Icon from 'components/Icon'
+import { useTranslation } from 'react-i18next'
 import {
   GROUP_ACCESSIBILITY, GROUP_VISIBILITY,
   visibilityDescription, accessibilityDescription,
   visibilityIcon, accessibilityIcon
 } from '@hylo/presenters/GroupPresenter'
-import { caribbeanGreen, white20onCaribbeanGreen, white } from 'style/colors'
-import { getGroupData, updateGroupData } from './CreateGroupFlow.store'
-import styles from './CreateGroupFlow.styles'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import KeyboardFriendlyView from 'components/KeyboardFriendlyView'
-import { useTranslation } from 'react-i18next'
+import RoundCheckbox from 'components/RoundCheckBox'
+import Icon from 'components/Icon'
 
 const groupVisibilityOptions = Object.keys(GROUP_VISIBILITY).map(label => ({
   label: label + ': ' + visibilityDescription(GROUP_VISIBILITY[label]),
@@ -21,45 +18,50 @@ const groupVisibilityOptions = Object.keys(GROUP_VISIBILITY).map(label => ({
   value: GROUP_VISIBILITY[label]
 }))
 
+export const DEFAULT_VISIBILITY_OPTION = groupVisibilityOptions.find(visibility => visibility.value === GROUP_VISIBILITY.Protected)
+
 const groupAccessibilityOptions = Object.keys(GROUP_ACCESSIBILITY).map(label => ({
   label: label + ': ' + accessibilityDescription(GROUP_ACCESSIBILITY[label]),
   iconName: accessibilityIcon(GROUP_ACCESSIBILITY[label]),
   value: GROUP_ACCESSIBILITY[label]
 }))
 
+export const DEFAULT_ACCESSIBILITY_OPTION = groupAccessibilityOptions.find(accessibility => accessibility.value === GROUP_ACCESSIBILITY.Restricted)
+
 export default function CreateGroupVisibilityAccessibility ({ navigation }) {
   const { t } = useTranslation()
-  const dispatch = useDispatch()
-  const groupData = useSelector(getGroupData)
-  const [visibility, setVisibility] = useState(groupData.visibility)
-  const [accessibility, setAccessibility] = useState(groupData.accessibility)
+  const { groupData, updateGroupData } = useCreateGroupStore()
+  const visibility = groupData.visibility
+  const setVisibility = newVisibility => updateGroupData({ visibility: newVisibility })
+  const accessibility = groupData.accessibility
+  const setAccessibility = newAccessibility => updateGroupData({ accessibility: newAccessibility })
 
   useFocusEffect(useCallback(() => {
-    dispatch(updateGroupData({ visibility, accessibility }))
+    updateGroupData({ visibility, accessibility })
   }, [visibility, accessibility]))
 
   return (
-    <KeyboardFriendlyView className="bg-background p-5 flex-1">
-      <ScrollView style={{ margins: 0 }}>
+    <KeyboardFriendlyView className='bg-background p-5 flex-1'>
+      <ScrollView>
         <View>
-          <View className="mb-6">
-            <Text className="text-foreground text-xl font-bold mb-2.5">{t('Who can see this group')}</Text>
+          <View className='mb-6'>
+            <Text className='text-foreground text-xl font-bold mb-2.5'>{t('Who can see this group?')}</Text>
             {groupVisibilityOptions.map(option => (
-              <Option
-                option={option} 
-                chosen={option.value === visibility}
-                onPress={setVisibility} 
+              <GroupPrivacyOption
+                option={option}
+                chosen={option.value === visibility.value}
+                onPress={setVisibility}
                 key={option.value}
               />
             ))}
           </View>
-          <View className="mb-6">
-            <Text className="text-foreground text-xl font-bold mb-2.5">{t('Who can join this group')}</Text>
+          <View className='mb-6'>
+            <Text className='text-foreground text-xl font-bold mb-2.5'>{t('Who can join this group?')}</Text>
             {groupAccessibilityOptions.map(option => (
-              <Option
-                option={option} 
-                chosen={option.value === accessibility}
-                onPress={setAccessibility} 
+              <GroupPrivacyOption
+                option={option}
+                chosen={option.value === accessibility.value}
+                onPress={setAccessibility}
                 key={option.value}
               />
             ))}
@@ -70,58 +72,17 @@ export default function CreateGroupVisibilityAccessibility ({ navigation }) {
   )
 }
 
-export function Option ({ option, chosen, onPress }) {
+export function GroupPrivacyOption ({ option, chosen, onPress }) {
+  if (!option) return
   return (
-    <TouchableOpacity className="p-4 pb-0 mb-2.5 flex-row justify-start" onPress={() => onPress(option.value)}>
-      <RoundCheckbox
-        className="mt-2.5"
-        size={24}
-        checked={chosen}
-        onValueChange={() => onPress(option.value)}
-      />
-      <Icon className="ml-2.5 text-xl text-foreground" name={option.iconName} />
-      <Text className="mt-[-4] ml-2.5 font-circular-bold flex-1 text-base text-foreground">
+    <TouchableOpacity className='flex-row items-center p-4 pb-0 mb-2.5' onPress={() => onPress && onPress(option)}>
+      {onPress && (
+        <RoundCheckbox className='mr-3' size={24} checked={chosen} onValueChange={() => onPress(option)} />
+      )}
+      <Icon className='mr-3 self-center' size={22} name={option.iconName} />
+      <Text className='mt-[-4] ml-2.5 font-circular-bold flex-1 self-center font-bold'>
         {option.label}
       </Text>
     </TouchableOpacity>
   )
 }
-
-const stepStyles = {
-  optionsContainer: {
-    marginBottom: 24
-  },
-  optionsLabel: {
-    fontSize: 20,
-    color: white,
-    fontWeight: 'bold',
-    marginBottom: 10
-  },
-}
-
-const optionStyles = {
-  optionRow: {
-    padding: 15,
-    paddingBottom: 0,
-    marginBottom: 10,
-    flexDirection: 'row',
-    justifyContent: 'flex-start'
-  },
-  optionCheckbox: {
-    marginTop: 10,
-    color: caribbeanGreen
-  },
-  optionIcon: {
-    marginLeft: 10,
-    fontSize: 20,
-    color: white
-  },
-  optionsLabel: {
-    marginTop: -4,
-    marginLeft: 10,
-    fontFamily: 'Circular-Bold',
-    flex: 1,
-    fontSize: 16,
-    color: white
-  }
-}
diff --git a/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeFlow.store.js b/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeFlow.store.js
index 7e4231a7b3..1f835c8060 100644
--- a/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeFlow.store.js
+++ b/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeFlow.store.js
@@ -1,56 +1,21 @@
 import { create } from 'zustand'
 import isEmpty from 'lodash/isEmpty'
-import { GROUP_ACCESSIBILITY, GROUP_VISIBILITY } from '@hylo/presenters/GroupPresenter'
-
-// Constants
-export const GROUP_WELCOME_LANDING = 'Group Welcome'
-export const GROUP_WELCOME_AGREEMENTS = 'Agreements'
-export const GROUP_WELCOME_JOIN_QUESTIONS = 'Join Questions'
-export const GROUP_WELCOME_SUGGESTED_SKILLS = 'Suggested Skills'
-
-// Initial State
-const initialState = {
-  groupData: {
-    name: '',
-    slug: '',
-    purpose: '',
-    visibility: GROUP_VISIBILITY.Protected,
-    accessibility: GROUP_ACCESSIBILITY.Restricted,
-    parentIds: []
-  },
-  currentStepIndex: 0,
-  workflowOptions: { disableContinue: false },
-  edited: false
-}
 
 export const useGroupWelcomeStore = create((set, get) => ({
-  ...initialState,
-
-  // Actions
-  setWorkflowOptions: (value = {}) => set({ workflowOptions: value }),
-
+  currentStepIndex: 0,
   incrementCurrentStepIndex: () => set((state) => ({
     currentStepIndex: state.currentStepIndex + 1
   })),
-
   decrementCurrentStepIndex: () => set((state) => ({
     currentStepIndex: state.currentStepIndex === 0 ? 0 : state.currentStepIndex - 1
-  })),
-
-  updateGroupData: (groupData) => set((state) => ({
-    groupData: { ...state.groupData, ...groupData },
-    edited: true
-  })),
-
-  clearWelcomeGroupStore: () => set(initialState),
-
-  // Selectors (optional but useful for API consistency)
-  getGroupData: () => get().groupData,
-  getCurrentStepIndex: () => get().currentStepIndex,
-  getWorkflowOptions: () => get().workflowOptions
+  }))
 }))
 
-// ✅ Route Name Generator (kept as a function, not part of store)
+export const GROUP_WELCOME_LANDING = 'Group Welcome'
+export const GROUP_WELCOME_AGREEMENTS = 'Agreements'
+export const GROUP_WELCOME_JOIN_QUESTIONS = 'Join Questions'
+export const GROUP_WELCOME_SUGGESTED_SKILLS = 'Suggested Skills'
+
 export function getRouteNames (group, currentMembership) {
   const routeNames = [GROUP_WELCOME_LANDING]
   const { agreements, settings } = group
diff --git a/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeTabBar.js b/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeTabBar.js
index cf82b0acb4..184f9b76b2 100644
--- a/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeTabBar.js
+++ b/apps/mobile/src/screens/GroupWelcomeFlow/GroupWelcomeTabBar.js
@@ -27,12 +27,7 @@ export default function GroupWelcomeTabBar ({
 
   const [{ currentUser }] = useCurrentUser()
   const [{ currentGroup: group }] = useCurrentGroup()
-  const {
-    currentStepIndex,
-    decrementCurrentStepIndex,
-    incrementCurrentStepIndex,
-    workflowOptions
-  } = useGroupWelcomeStore()
+  const { currentStepIndex, decrementCurrentStepIndex, incrementCurrentStepIndex } = useGroupWelcomeStore()
   const currentMemberships = currentUser?.memberships
   const currentMembership = currentMemberships && currentMemberships.find(m => m.group.id === group.id)
 
@@ -41,7 +36,6 @@ export default function GroupWelcomeTabBar ({
   const nextStepScreenName = routeNames[currentStepIndex + 1]
   const currentStepName = routeNames[currentStepIndex]
 
-  const disableContinue = !!workflowOptions?.disableContinue
   const [completeButtonDisabled, setCompleteButtonDisabled] = useState(false)
 
   const onAgreementStepButNotReady = currentStepName === GROUP_WELCOME_AGREEMENTS && !acceptedAllAgreements
@@ -116,7 +110,6 @@ export default function GroupWelcomeTabBar ({
           text={t('Continue')}
           onPress={(onAgreementStepButNotReady || onJoinQuestionStepButNotReady) ? handleBackOut : gotoNextStep}
           style={styles.continueButton}
-          disabled={disableContinue}
         />
       )}
       {!nextStepScreenName && (
diff --git a/apps/mobile/src/store/reducers/index.js b/apps/mobile/src/store/reducers/index.js
index 05c318d0e7..2fb0bf42d8 100644
--- a/apps/mobile/src/store/reducers/index.js
+++ b/apps/mobile/src/store/reducers/index.js
@@ -2,8 +2,6 @@ import { combineReducers } from 'redux'
 import pending from './pending'
 import resetStore from './resetStore'
 import { SET_STATE } from 'store/constants'
-// Local store
-import CreateGroupFlow from 'screens/CreateGroupFlow/CreateGroupFlow.store'
 
 export const composeReducers = (...reducers) => (state, action) =>
   reducers.reduce((newState, reducer) => reducer(newState, action), state)
@@ -12,10 +10,7 @@ export const handleSetState = (state = {}, { type, payload }) =>
   type === SET_STATE ? payload : state
 
 export const createCombinedReducers = () => combineReducers({
-  // Global store
-  pending,
-  // Local store (Component)
-  CreateGroupFlow
+  pending
 })
 
 export default function createRootReducer () {
diff --git a/packages/graphql/mutations/createGroupMutation.js b/packages/graphql/mutations/createGroupMutation.js
new file mode 100644
index 0000000000..987ef834f7
--- /dev/null
+++ b/packages/graphql/mutations/createGroupMutation.js
@@ -0,0 +1,30 @@
+import { gql } from 'urql'
+import groupFieldsFragment from '@hylo/graphql/fragments/groupFieldsFragment'
+import groupPrerequisiteGroupsFieldsFragment from '@hylo/graphql/fragments/groupPrerequisiteGroupsFieldsFragment'
+
+export default gql`
+  mutation CreateGroupMutation ($data: GroupInput) {
+    createGroup(data: $data) {
+      ...GroupFieldsFragment
+      ...GroupPrerequisiteGroupsFieldsFragment
+      memberships {
+        items {
+          id
+          hasModeratorRole
+          person {
+            id
+          }
+          settings {
+            agreementsAcceptedAt
+            joinQuestionsAnsweredAt
+            sendEmail
+            showJoinForm
+            sendPushNotifications
+          }
+        }
+      }
+    }
+  }
+  ${groupFieldsFragment}
+  ${groupPrerequisiteGroupsFieldsFragment}
+`
diff --git a/packages/graphql/queries/groupExistsQuery.js b/packages/graphql/queries/groupExistsQuery.js
new file mode 100644
index 0000000000..afa8e3323a
--- /dev/null
+++ b/packages/graphql/queries/groupExistsQuery.js
@@ -0,0 +1,9 @@
+import { gql } from 'urql'
+
+export default gql`
+  query GroupExistsQuery ($slug: String) {
+    groupExists (slug: $slug) {
+      exists
+    }
+  }
+`


Reviews:
user: login: thomasgwatson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-02T02:03:40Z
user: login: thomasgwatson
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-02T02:13:16Z
user: login: lorenjohnson
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-02T11:03:24Z

Review comments:
diff_hunk: @@ -1,29 +1,22 @@
-import React, { useState, useCallback, useRef } from 'react'
-import { useSelector, useDispatch } from 'react-redux'
-import { useFocusEffect } from '@react-navigation/native'
+import React, { useRef } from 'react'
 import { Text, TouchableOpacity, View } from 'react-native'
-import KeyboardFriendlyView from 'components/KeyboardFriendlyView'
-import { GROUP_ACCESSIBILITY } from '@hylo/presenters/GroupPresenter'
-import { getGroupData, updateGroupData } from './CreateGroupFlow.store'
 import { useTranslation } from 'react-i18next'
-import useCurrentUser from '@hylo/hooks/useCurrentUser'
-import ItemSelectorModal from 'components/ItemSelectorModal'
 import FastImage from 'react-native-fast-image'
+import { GROUP_ACCESSIBILITY } from '@hylo/presenters/GroupPresenter'
+import useCurrentUser from '@hylo/hooks/useCurrentUser'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import Icon from 'components/Icon'
-
-const styles = {
-  pressSelectionValue: {
-    marginTop: 10,
-    fontSize: 16
-  }
-}
+import ItemSelectorModal from 'components/ItemSelectorModal'
+import KeyboardFriendlyView from 'components/KeyboardFriendlyView'
 
 export default function CreateGroupParentGroups ({ navigation }) {
-  const dispatch = useDispatch()
   const { t } = useTranslation()
   const [{ currentUser }] = useCurrentUser()
-  const groupData = useSelector(getGroupData)
-  const [parentIds, setParentGroupIds] = useState(groupData.parentIds)
+  const { groupData, updateGroupData } = useCreateGroupStore()
+  const parentGroups = groupData.parentGroups
+  const setParentGroups = newParentGroups => updateGroupData({ parentGroups: newParentGroups })
+  const clearParentGroups = () => setParentGroups([])
+  // const [parentGroups, setParentGroups] = useState(groupData.parentGroups)
path: apps/mobile/src/screens/CreateGroupFlow/CreateGroupParentGroups.js
user: login: thomasgwatson
body: Still want this?
created_at: 2025-03-02T02:03:40Z
author_association: COLLABORATOR
diff_hunk: @@ -1,29 +1,22 @@
-import React, { useState, useCallback, useRef } from 'react'
-import { useSelector, useDispatch } from 'react-redux'
-import { useFocusEffect } from '@react-navigation/native'
+import React, { useRef } from 'react'
 import { Text, TouchableOpacity, View } from 'react-native'
-import KeyboardFriendlyView from 'components/KeyboardFriendlyView'
-import { GROUP_ACCESSIBILITY } from '@hylo/presenters/GroupPresenter'
-import { getGroupData, updateGroupData } from './CreateGroupFlow.store'
 import { useTranslation } from 'react-i18next'
-import useCurrentUser from '@hylo/hooks/useCurrentUser'
-import ItemSelectorModal from 'components/ItemSelectorModal'
 import FastImage from 'react-native-fast-image'
+import { GROUP_ACCESSIBILITY } from '@hylo/presenters/GroupPresenter'
+import useCurrentUser from '@hylo/hooks/useCurrentUser'
+import { useCreateGroupStore } from './CreateGroupFlow.store'
 import Icon from 'components/Icon'
-
-const styles = {
-  pressSelectionValue: {
-    marginTop: 10,
-    fontSize: 16
-  }
-}
+import ItemSelectorModal from 'components/ItemSelectorModal'
+import KeyboardFriendlyView from 'components/KeyboardFriendlyView'
 
 export default function CreateGroupParentGroups ({ navigation }) {
-  const dispatch = useDispatch()
   const { t } = useTranslation()
   const [{ currentUser }] = useCurrentUser()
-  const groupData = useSelector(getGroupData)
-  const [parentIds, setParentGroupIds] = useState(groupData.parentIds)
+  const { groupData, updateGroupData } = useCreateGroupStore()
+  const parentGroups = groupData.parentGroups
+  const setParentGroups = newParentGroups => updateGroupData({ parentGroups: newParentGroups })
+  const clearParentGroups = () => setParentGroups([])
+  // const [parentGroups, setParentGroups] = useState(groupData.parentGroups)
path: apps/mobile/src/screens/CreateGroupFlow/CreateGroupParentGroups.js
user: login: lorenjohnson
body: Good catch. Removed
created_at: 2025-03-02T11:03:23Z
author_association: MEMBER

PR comments:
user: login: lorenjohnson
created_at: 2025-03-01T17:20:18Z
body: ![Simulator Screenshot - iPhone 15 Pro Max - 2025-03-01 at 18 20 09](https://github.com/user-attachments/assets/048c8b73-fcc3-4625-8747-b03542dbcc48)
![Simulator Screenshot - iPhone 15 Pro Max - 2025-03-01 at 18 19 54](https://github.com/user-attachments/assets/aea37bbf-407a-4e68-ae14-9b8e9cd32a35)

author_association: MEMBER
user: login: sentry[bot]
created_at: 2025-03-14T07:43:27Z
body: ## Suspect Issues
This pull request was deployed and Sentry observed the following issues:

- ‼️ **React ErrorBoundary TypeError: Cannot read property 'askJoinQuestions' of undefined** `set$argument_0(/Users/lorenjohnson/dev/hylo/hyl...` [View Issue](https://hylo.sentry.io/issues/6398575361/?referrer=github-pr-bot)
- ‼️ **React ErrorBoundary TypeError: Cannot read property 'agreements' of undefined** `<global>(/Users/lorenjohnson/dev/hylo/hylo/apps...` [View Issue](https://hylo.sentry.io/issues/6399069708/?referrer=github-pr-bot)

<sub>Did you find this useful? React with a 👍 or 👎</sub>
author_association: NONE