Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All view port to mobile #196

Merged
merged 31 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
79531a9
Add widgetIconResolver to mobile
thomasgwatson Feb 2, 2025
5b1e301
Include context widgets on the groupPresenter
thomasgwatson Feb 3, 2025
f0bc84c
Add AllView widget; Add widgetUrl and dependent functions to mobile u…
thomasgwatson Feb 3, 2025
63b7c07
Add all view to the navigation structure
thomasgwatson Feb 3, 2025
9bb7daa
Render widgets in all view
thomasgwatson Feb 3, 2025
5e28df2
Merge branch 'dev' into all-view-port-to-mobile
thomasgwatson Feb 3, 2025
3ac1781
post-merge fix
thomasgwatson Feb 3, 2025
483aa08
chore: Move ContextWidgetPresenter
lorenjohnson Feb 3, 2025
720d263
chore(presenters,shared): Refactor ContextWidgetPresenter, subsuming …
lorenjohnson Feb 3, 2025
c209469
chore(shared,presenters,mobile,web): Reflects new location of Context…
lorenjohnson Feb 3, 2025
ad37e69
chore(mobile): Renames AllView screen to AllViews in line with other …
lorenjohnson Feb 3, 2025
617f89c
chore(presenters,web,mobile): Clean-up of ContextWidgetPresenter, add…
lorenjohnson Feb 3, 2025
ef3e6ee
chore(presenters): Add back findHomeView to ContextWidgetPresenter
lorenjohnson Feb 3, 2025
d262cae
Merge branch 'dev' into all-view-port-to-mobile
lorenjohnson Feb 4, 2025
fd2490b
Merge branch 'all-view-port-to-mobile' into all-view-port-to-mobile-lej
lorenjohnson Feb 4, 2025
b2a364e
Merge branch 'mobile-update-sub-handler-and-messages-fixes' into what…
lorenjohnson Feb 4, 2025
02e6d1e
chore(mobile): Fix merge issues
lorenjohnson Feb 4, 2025
e390132
chore: Shuffle around things for shared consideration
lorenjohnson Feb 4, 2025
f28b5cf
chore(mobile,presenters): Refactor Mobile Stream, clean-out GroupPres…
lorenjohnson Feb 4, 2025
10d33d8
chore(mobile): Refactors StreamList into Stream
lorenjohnson Feb 4, 2025
e87cab8
chore(mobile): Cleanup
lorenjohnson Feb 4, 2025
7ddc9d0
Merge branch 'dev' into all-view-port-to-mobile
lorenjohnson Feb 5, 2025
483a9cd
Merge branch 'all-view-port-to-mobile' into all-view-port-to-mobile-lej
lorenjohnson Feb 5, 2025
2a06528
Merge branch 'all-view-port-to-mobile-lej' into what-are-views
lorenjohnson Feb 5, 2025
e0573d6
chore(mobile): Get new notifications from subscription, handle Mark A…
lorenjohnson Feb 5, 2025
033ad18
chore(mobile): Code tidy Messages / Thread area and add notes
lorenjohnson Feb 5, 2025
3760652
chore(mobile): Reverts skipping of initial notifications query. Let's…
lorenjohnson Feb 5, 2025
5c9fc60
Merge branch 'what-are-views' into notifications-and-urlq-improvements
lorenjohnson Feb 5, 2025
f4647cf
Merge pull request #197 from Hylozoic/all-view-port-to-mobile-lej
thomasgwatson Feb 5, 2025
a64f02e
Merge pull request #212 from Hylozoic/what-are-views
thomasgwatson Feb 5, 2025
6f0521c
Merge pull request #213 from Hylozoic/notifications-and-urlq-improvem…
thomasgwatson Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions apps/mobile/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1333,7 +1333,7 @@ PODS:
- react-native-config/App (= 1.5.3)
- react-native-config/App (1.5.3):
- React-Core
- react-native-date-picker (5.0.8):
- react-native-date-picker (5.0.9):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -1473,7 +1473,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-webview (13.13.1):
- react-native-webview (13.13.2):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -1858,7 +1858,7 @@ PODS:
- React-Core
- SDWebImage (~> 5.11.1)
- SDWebImageWebPCoder (~> 0.8.4)
- RNGestureHandler (2.22.0):
- RNGestureHandler (2.22.1):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -2491,7 +2491,7 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: 1364ae5354f51b3ecee8eb718b5b6d1686d2ff4d
react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe
react-native-config: 8f7283449bbb048902f4e764affbbf24504454af
react-native-date-picker: 543274fea228bdf7427a917d19b71f8f6eca7a72
react-native-date-picker: d55302b0ff2c95854f60f40c5465109601fde317
react-native-document-picker: cd4d6b36a5207ad7a9e599ebb9eb0c2e84fa0b87
react-native-geolocation-service: 608e1da71a1ac31b4de64d9ef2815f697978c55b
react-native-image-picker: e7331948589e764ecd5a9c715c3fc14d4e6187e6
Expand All @@ -2500,7 +2500,7 @@ SPEC CHECKSUMS:
react-native-render-html: 984dfe2294163d04bf5fe25d7c9f122e60e05ebe
react-native-restart: 7595693413fe3ca15893702f2c8306c62a708162
react-native-safe-area-context: cbadf383376f589bb611c8ae0280c1d4b7b447e9
react-native-webview: ce6e24111244cea3b9cefbb65d8c32ad0a0c7b45
react-native-webview: 08a61d1d92fee7875c8702e70f7da1d706a9d2dc
React-nativeconfig: 539ff4de6ce3b694e8e751080568c281c84903ce
React-NativeModulesApple: 771cc40b086281b820fe455fedebbe4341fd0c90
React-perflogger: 4e80a5b8d61dfb38024e7d5b8efaf9ce1037d31f
Expand Down Expand Up @@ -2537,7 +2537,7 @@ SPEC CHECKSUMS:
RNCPicker: d23ebebb0c66864ac3edf260c20f8a4470aa2769
RNDeviceInfo: 3f2e5fcca3637f75c6d30ba287293c2f97206781
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
RNGestureHandler: 8ce7a079c513f96f9b580bcb2ecee621d511361f
RNGestureHandler: 41e106f2004f7b0d5fd1bb92179797890bcbb0ec
RNGoogleSignin: 24d3b3261f78b0fae0aa013f8ab6f8d25e369b0c
RNReanimated: 9ee6347ca0aa3cf78cae715455e781728ae142e2
RNScreens: b02af14099030cc1e63f74f2791574e909fc1541
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react'
import FastImage from 'react-native-fast-image'
import Icon from 'components/Icon'
import { Grid3x3 } from 'lucide-react-native'
import Avatar from 'components/Avatar'
import { ViewHelpers } from '@hylo/shared'

export function WidgetIconResolver({ widget, style, className }) {
if (!widget) {
console.warn('No widget passed into iconResolver')
return null
}
const type = widget.type

if (widget.icon) {
return <Icon name={widget.icon} style={style} className={className} />
}

if (widget.viewUser) {
return (
<Avatar
avatarUrl={widget.viewUser.avatarUrl}
name={widget.viewUser.name}
style={style}
className={className}
/>
)
}

if (widget.viewGroup) {
return (
<Avatar
avatarUrl={widget.viewGroup.avatarUrl}
name={widget.viewGroup.name}
style={style}
className={className}
/>
)
}

if (widget.customView?.icon) {
return <Icon name={widget.customView.icon} style={style} className={className} />
}

if (widget.context === 'my') {
return null
}

if (ViewHelpers.COMMON_VIEWS[type]) {
return <Icon name={ViewHelpers.COMMON_VIEWS[type].icon} style={style} className={className} />
}

// TODO redesign: - make a iconName method in WidgetHelpers / WidgetPresenter
// the name icon name is shared by web mobile?
// TOM COMMENT: waiting for full shake-out of Lucide transition before cleaning this up
switch (type) {
case 'chats':
return <Icon name='Message' style={style} className={className} />
case 'setup':
return <Icon name='Settings' style={style} className={className} />
case 'custom-views':
return <Icon name='Stack' style={style} className={className} />
case 'viewChat':
return <Icon name='Message' style={style} className={className} />
case 'chat':
return <Icon name='Message' style={style} className={className} />
case 'viewPost':
return <Icon name='Posticon' style={style} className={className} />
case 'about':
return <Icon name='Info' style={style} className={className} />
case 'all-views':
return <Grid3x3 className='h-[16px]'/>
}
return null
}

export default WidgetIconResolver
3 changes: 3 additions & 0 deletions apps/mobile/src/components/WidgetIconResolver/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import component from './WidgetIconResolver'

export default component
2 changes: 2 additions & 0 deletions apps/mobile/src/navigation/HomeNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Stream from 'screens/Stream'
import GroupExploreWebView from 'screens/GroupExploreWebView'
import GroupNavigation from 'screens/GroupNavigation'
import Groups from 'screens/Groups'
import AllView from 'screens/AllView'
import MemberDetails from 'screens/MemberProfile/MemberDetails'
import MemberProfile from 'screens/MemberProfile'
import MembersComponent from 'screens/Members'
Expand Down Expand Up @@ -71,6 +72,7 @@ export default function HomeNavigator ({ navigation }) {
<HomeTab.Navigator {...navigatorProps}>
<HomeTab.Screen name='Group Navigation' component={GroupNavigation} />
<HomeTab.Screen name='Stream' component={Stream} />
<HomeTab.Screen name='All Views' component={AllView} />
<HomeTab.Screen name='Post Details' key='Post Details' component={PostDetails} />
<HomeTab.Screen name='Projects' component={Stream} initialParams={{ streamType: 'project' }} />
<HomeTab.Screen name='Project Members' key='Project Members' component={ProjectMembers} />
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/src/navigation/linking/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const routingConfig = {
'/:context(groups)/:groupSlug/topics/:topicName': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Chat`,
'/:context(groups)/:groupSlug/members/:id': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Member`,
'/:context(groups)/:groupSlug/members': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Members`,
'/:context(groups)/:groupSlug/all-views': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/All Views`,
'/:context(groups)/:groupSlug': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Stream`,
'/:context(groups)/:groupSlug/custom/:customViewId': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Stream`,
'/:context(groups)/:groupSlug/explore': `${AUTH_ROOT_SCREEN_NAME}/Drawer/Tabs/Home Tab/Group Explore`,
Expand Down
13 changes: 12 additions & 1 deletion apps/mobile/src/presenters/GroupPresenter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Image } from 'react-native'
import { ALL_GROUPS_CONTEXT_SLUG, PUBLIC_CONTEXT_SLUG, MY_CONTEXT_SLUG } from '@hylo/shared'
import { getStaticMenuWidgets } from '@hylo/shared/src/ContextMenuHelpers'
import allGroupsBannerImage from 'assets/all-groups-banner.png'
import allGroupsAvatarUrl from 'assets/All_Groups2.png'
import myHomeAvatarUrl from 'assets/my-home.png'
Expand All @@ -10,9 +11,16 @@ import GREEN_ICON_AVATAR_PATH from 'assets/green-icon.jpg'
import PURPLE_HERO_BANNER_PATH from 'assets/purple-hero.jpg'
import PURPLE_ICON_AVATAR_PATH from 'assets/purple-icon.jpg'

export function getContextWidgetsForGroup (group){
if (!group) return []
if (isContextGroup(group.slug)){
return getStaticMenuWidgets({ isPublic: group.slug === PUBLIC_CONTEXT_SLUG, isMyContext: group.slug === MY_CONTEXT_SLUG, isAllContext: group.slug === ALL_GROUPS_CONTEXT_SLUG, profileUrl: '' })
}
return group?.contextWidgets.items || []
}

export default function GroupPresenter (group) {
if (!group) return null

return {
...group,
activeProjects: group.activeProjects || [],
Expand All @@ -25,6 +33,9 @@ export default function GroupPresenter (group) {
}))
: [],
// TODO: URQL - convert
contextWidgets: group?.contextWidgets.items
? getContextWidgetsForGroup(group)
: [],
customViews: group?.customViews?.items || [],
// customViews: group.customViews
// ? group.customViews.items.map(cv => ({
Expand Down
84 changes: 84 additions & 0 deletions apps/mobile/src/screens/AllView/AllView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useMemo } from 'react'
import { View, Text, ScrollView, TouchableOpacity } from 'react-native'
import { useTranslation } from 'react-i18next'
import { useNavigation } from '@react-navigation/native'
import { WidgetHelpers, NavigatorHelpers } from '@hylo/shared'
import { Plus } from 'lucide-react-native'
import useCurrentGroup from '@hylo/hooks/useCurrentGroup'
import useHasResponsibility, { RESP_ADMINISTRATION } from '@hylo/hooks/useHasResponsibility'
import ContextWidgetPresenter from '@hylo/shared/src/ContextWidgetPresenter'
import WidgetIconResolver from 'components/WidgetIconResolver'
import { openURL } from 'hooks/useOpenURL'
import { widgetUrl } from 'util/navigation'

function WidgetCard({ widget, onPress }) {
const { t } = useTranslation()
if (!widget) return null
const capitalizedType = widget?.humanReadableType.charAt(0).toUpperCase() + widget?.humanReadableType.slice(1)
const capitalizedView = widget.view ? widget.view.charAt(0).toUpperCase() + widget.view.slice(1) : ''

return (
<TouchableOpacity
onPress={onPress}
className='p-4 border border-foreground/20 rounded-md shadow-sm bg-background'
>
<View className='items-center'>
<Text className='text-lg font-semibold text-foreground mb-2'>{widget.title}</Text>
{widget.humanReadableType && (
<Text className='text-sm text-foreground/70'>
{t('Type')}: {t(capitalizedType)}
</Text>
)}
{widget.view && (
<Text className='text-sm text-foreground/70'>
{t('View')}: {t(capitalizedView)}
</Text>
)}
<View className='mt-2'>
<WidgetIconResolver widget={widget} size={24} />
</View>
</View>
</TouchableOpacity>
)
}

export default function AllView() {
const { t } = useTranslation()
const navigation = useNavigation()
const [{ currentGroup }] = useCurrentGroup()
const hasResponsibility = useHasResponsibility({ forCurrentGroup: true, forCurrentUser: true })
const canAdminister = hasResponsibility(RESP_ADMINISTRATION)
const contextWidgets = currentGroup.contextWidgets

const visibleWidgets = useMemo(() => {
return contextWidgets.filter(widget => {
if (widget.visibility === 'admin' && !canAdminister) return false
if (widget.type === 'home') return false
return true
}).map(widget => ContextWidgetPresenter(widget, { t }))
}, [contextWidgets, canAdminister])

const handleWidgetPress = (widget) => {
const widgetUrl = NavigatorHelpers.widgetUrl({ widget, groupSlug: currentGroup?.slug })

if (widgetUrl) {
openURL(widgetUrl)
} else {
console.warn('Could not determine navigation for widget:', widget)
}
}

return (
<ScrollView className='flex-1 bg-background p-4'>
<View className='grid grid-cols-2 gap-4'>
{visibleWidgets.map(widget => (
<WidgetCard
key={widget.id}
widget={widget}
onPress={() => handleWidgetPress(widget)}
/>
))}
</View>
</ScrollView>
)
}
3 changes: 3 additions & 0 deletions apps/mobile/src/screens/AllView/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import component from './AllView'

export default component
40 changes: 39 additions & 1 deletion apps/mobile/src/util/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { host } from 'config'
import { get, isEmpty, isNumber, omitBy } from 'lodash/fp'
import qs from 'query-string'
import { ALL_GROUPS_CONTEXT_SLUG, PUBLIC_CONTEXT_SLUG, MY_CONTEXT_SLUG } from '@hylo/shared'
import { isContextGroup } from 'presenters/GroupPresenter'

export const HYLO_ID_MATCH = '\\d+'
export const POST_ID_MATCH = HYLO_ID_MATCH
Expand Down Expand Up @@ -148,7 +149,7 @@ export function personUrl (id, groupSlug) {
return `${base}/members/${id}`
}

// Topics URLs
// Topics and Chat URLs
export function topicsUrl (opts, defaultUrl = allGroupsUrl()) {
return baseUrl({ ...opts, view: 'topics' }, defaultUrl)
}
Expand All @@ -157,6 +158,43 @@ export function topicUrl (topicName, opts) {
return `${topicsUrl(opts)}/${topicName}`
}

export function chatUrl (chatName, { context, groupSlug }) {
return `${baseUrl({ context, groupSlug })}/chat/${chatName}`
}

// CustomView urls

export function customViewUrl (customViewId, rootPath, opts) {
return `${rootPath}/custom/${customViewId}`
}

// Widget urls

export function widgetUrl ({ widget, rootPath, groupSlug: providedSlug, context = 'group' }) {
if (!widget) return null

const groupSlug = isContextGroup(providedSlug) ? null : providedSlug
let url = ''
if (widget.url) return widget.url
if (widget.view === 'about') {
url = groupDetailUrl(groupSlug, { rootPath, groupSlug, context })
} else if (widget.view) {
url = viewUrl(widget.view, { groupSlug, context: widget.context || context })
} else if (widget.viewGroup) {
url = groupUrl(widget.viewGroup.slug)
} else if (widget.viewUser) {
url = personUrl(widget.viewUser.id, groupSlug)
} else if (widget.viewPost) {
url = postUrl(widget.viewPost.id, { groupSlug, context })
} else if (widget.viewChat) {
url = chatUrl(widget.viewChat.name, { rootPath, groupSlug, context })
} else if (widget.customView) {
url = customViewUrl(widget.customView.id, groupUrl(groupSlug))
}

return url
}

// URL utility functions

export function addQuerystringToPath (path, querystringParams) {
Expand Down
Loading