Skip to content

Commit

Permalink
feat(lib): add pinning messages
Browse files Browse the repository at this point in the history
  • Loading branch information
piotr-suwala committed Sep 19, 2023
2 parents dba9a69 + f93cbe5 commit af306e2
Show file tree
Hide file tree
Showing 42 changed files with 928 additions and 767 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"plugin:react-hooks/recommended",
"prettier"
],
"rules": {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"eslint": "8.22.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-react-hooks": "4.6.0",
"husky": "8.0.1",
"lint-staged": "13.0.3",
"npm-run-all": "4.1.5",
Expand Down
88 changes: 61 additions & 27 deletions samples/react-native-group-chat/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { useContext, useEffect, useState } from "react"
import { View, StyleSheet, ActivityIndicator, KeyboardAvoidingView, Platform } from "react-native"
import {
View,
StyleSheet,
ActivityIndicator,
KeyboardAvoidingView,
Platform,
LogBox,
} from "react-native"
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context"
import { GestureHandlerRootView } from "react-native-gesture-handler"
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet"
import { NavigationContainer } from "@react-navigation/native"
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"
import { createStackNavigator, StackScreenProps } from "@react-navigation/stack"
import { PaperProvider } from "react-native-paper"
import { MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
import { StatusBar } from "expo-status-bar"
import { Chat, Membership, User } from "@pubnub/chat"
import { Channel, Chat, Membership, User } from "@pubnub/chat"
import {
useFonts,
Roboto_400Regular,
Expand All @@ -24,6 +30,8 @@ import { ChatContext } from "./context"
import { RootStackParamList, BottomTabsParamList } from "./types"
import { defaultTheme, colorPalette as colors } from "./ui-components"

LogBox.ignoreLogs(["Require cycle:", "Sending"])

const Tab = createBottomTabNavigator<BottomTabsParamList>()
const MainStack = createStackNavigator<RootStackParamList>()

Expand All @@ -34,8 +42,10 @@ function TabNavigator({ route }: StackScreenProps<RootStackParamList, "tabs">) {
useEffect(() => {
async function init() {
const chat = await Chat.init({
publishKey: process.env.EXPO_PUBLIC_PUBNUB_PUB_KEY || "pub-c-0457cb83-0786-43df-bc70-723b16a6e816",
subscribeKey: process.env.EXPO_PUBLIC_PUBNUB_SUB_KEY || "sub-c-e654122d-85b5-49a6-a3dd-8ebc93c882de",
publishKey:
process.env.EXPO_PUBLIC_PUBNUB_PUB_KEY || "pub-c-0457cb83-0786-43df-bc70-723b16a6e816",
subscribeKey:
process.env.EXPO_PUBLIC_PUBNUB_SUB_KEY || "sub-c-e654122d-85b5-49a6-a3dd-8ebc93c882de",
userId: name || "test-user",
typingTimeout: 2000,
storeUserActivityTimestamps: true,
Expand All @@ -45,7 +55,7 @@ function TabNavigator({ route }: StackScreenProps<RootStackParamList, "tabs">) {
}

init()
}, [name])
}, [name, setChat])

if (!chat) {
return (
Expand Down Expand Up @@ -119,8 +129,28 @@ function TabNavigator({ route }: StackScreenProps<RootStackParamList, "tabs">) {
function App() {
const [chat, setChat] = useState<Chat | null>(null)
const [users, setUsers] = useState<User[]>([])
const [loading, setLoading] = useState(false)
const [currentChannel, setCurrentChannel] = useState<Channel>()
const [currentChannelMembers, setCurrentChannelMembers] = useState<Membership[]>([])
const [userMemberships, setUserMemberships] = useState<Membership[]>([])

async function setCurrentChannelWithMembers(channel: Channel) {
const { members } = await channel.getMembers()
setCurrentChannelMembers(members)
setCurrentChannel(channel)
}

function getUser(userId: string) {
const existingUser = users.find((u) => u.id === userId)
if (!existingUser) {
chat?.getUser(userId).then((fetchedUser) => {
if (fetchedUser) setUsers((users) => [...users, fetchedUser])
})
return null
}
return existingUser
}

const [fontsLoaded] = useFonts({
Roboto_400Regular,
Roboto_500Medium,
Expand All @@ -134,38 +164,42 @@ function App() {
return (
<ChatContext.Provider
value={{
loading,
setLoading,
chat,
setChat,
currentChannel,
setCurrentChannel: setCurrentChannelWithMembers,
currentChannelMembers,
users,
setUsers,
getUser,
memberships: userMemberships,
setMemberships: setUserMemberships,
}}
>
<GestureHandlerRootView style={{ flex: 1 }}>
<BottomSheetModalProvider>
<PaperProvider settings={{ rippleEffectEnabled: false }} theme={defaultTheme}>
<StatusBar style="inverted" backgroundColor={defaultTheme.colors.navy800} />
<SafeAreaProvider>
<SafeAreaView
style={[styles.safeArea, { backgroundColor: defaultTheme.colors.navy800 }]}
edges={["top", "left", "right"]}
<StatusBar style="inverted" backgroundColor={defaultTheme.colors.navy800} />
<SafeAreaProvider>
<SafeAreaView
style={[styles.safeArea, { backgroundColor: defaultTheme.colors.navy800 }]}
edges={["top", "left", "right"]}
>
{/* TODO: for some reason KeyboardAvoidingView doesn't work on any page other than login */}
<KeyboardAvoidingView
{...(Platform.OS === "ios" ? { behavior: "padding" } : {})}
style={{ flex: 1 }}
>
{/* TODO: for some reason KeyboardAvoidingView doesn't work on any page other than login */}
<KeyboardAvoidingView
{...(Platform.OS === "ios" ? { behavior: "padding" } : {})}
style={{ flex: 1 }}
>
<NavigationContainer>
<MainStack.Navigator screenOptions={{ headerShown: false }}>
<MainStack.Screen name="login" component={LoginScreen} />
<MainStack.Screen name="tabs" component={TabNavigator} />
</MainStack.Navigator>
</NavigationContainer>
</KeyboardAvoidingView>
</SafeAreaView>
</SafeAreaProvider>
</PaperProvider>
<NavigationContainer>
<MainStack.Navigator screenOptions={{ headerShown: false }}>
<MainStack.Screen name="login" component={LoginScreen} />
<MainStack.Screen name="tabs" component={TabNavigator} />
</MainStack.Navigator>
</NavigationContainer>
</KeyboardAvoidingView>
</SafeAreaView>
</SafeAreaProvider>
</BottomSheetModalProvider>
</GestureHandlerRootView>
</ChatContext.Provider>
Expand Down
5 changes: 0 additions & 5 deletions samples/react-native-group-chat/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ module.exports = function (api) {
api.cache(true)
return {
presets: ["babel-preset-expo"],
env: {
production: {
plugins: ["react-native-paper/babel"],
},
},
plugins: ["@babel/plugin-proposal-export-namespace-from", "react-native-reanimated/plugin"],
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useMemo, useRef, useState } from "react"
import { StyleSheet, View } from "react-native"
import { BottomSheetModal, BottomSheetBackdrop } from "@gorhom/bottom-sheet"
import { Gap, Text, usePNTheme, Button } from "../../ui-components"
import { Gap, Text, colorPalette as colors, Button } from "../../ui-components"
import Emoji1 from "../../assets/emojis/emoji1.svg"
import Emoji2 from "../../assets/emojis/emoji2.svg"
import Emoji3 from "../../assets/emojis/emoji3.svg"
Expand All @@ -12,17 +12,20 @@ import Emoji7 from "../../assets/emojis/emoji7.svg"
import { useNavigation } from "@react-navigation/native"
import { EnhancedIMessage } from "../../utils"
import { HomeStackNavigation } from "../../types"
import { Message } from "@pubnub/chat"
import { Message, ThreadMessage } from "@pubnub/chat"

type UseActionsMenuParams = {
onQuote: (message: Message) => void
removeThreadReply?: boolean
onPinMessage: (message: Message) => void
onPinMessage: (message: Message | ThreadMessage) => void
}

export function useActionsMenu({ onQuote, removeThreadReply = false, onPinMessage }: UseActionsMenuParams) {
export function useActionsMenu({
onQuote,
removeThreadReply = false,
onPinMessage,
}: UseActionsMenuParams) {
const bottomSheetModalRef = useRef<BottomSheetModal>(null)
const theme = usePNTheme()
const navigation = useNavigation<HomeStackNavigation>()
const [currentlyFocusedMessage, setCurrentlyFocusedMessage] = useState<EnhancedIMessage | null>(
null
Expand All @@ -48,7 +51,7 @@ export function useActionsMenu({ onQuote, removeThreadReply = false, onPinMessag
onChange={handleSheetChanges}
style={styles.container}
backdropComponent={BottomSheetBackdrop}
handleIndicatorStyle={[styles.handleIndicator, { backgroundColor: theme.colors.neutral500 }]}
handleIndicatorStyle={[styles.handleIndicator, { backgroundColor: colors.neutral500 }]}
>
<View style={styles.emojisRow}>
<Emoji1 width={48} height={48} />
Expand Down
96 changes: 96 additions & 0 deletions samples/react-native-group-chat/components/avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Channel, User } from "@pubnub/chat"
import { View, Image, StyleSheet, ViewStyle } from "react-native"

import Avatar1 from "../../assets/avatars/avatar1.png"
import Avatar2 from "../../assets/avatars/avatar2.png"
import Avatar3 from "../../assets/avatars/avatar3.png"
import Avatar4 from "../../assets/avatars/avatar4.png"
import Avatar5 from "../../assets/avatars/avatar5.png"
import Avatar6 from "../../assets/avatars/avatar6.png"
import Avatar7 from "../../assets/avatars/avatar7.png"
import Avatar8 from "../../assets/avatars/avatar8.png"
import Avatar9 from "../../assets/avatars/avatar9.png"
import Avatar10 from "../../assets/avatars/avatar10.png"
import Avatar11 from "../../assets/avatars/avatar11.png"
import Avatar12 from "../../assets/avatars/avatar12.png"
import Avatar13 from "../../assets/avatars/avatar13.png"
import Avatar14 from "../../assets/avatars/avatar14.png"
import Avatar15 from "../../assets/avatars/avatar15.png"
import Avatar16 from "../../assets/avatars/avatar16.png"
import { colorPalette as colors } from "../../ui-components"

const avatars = [
Avatar1,
Avatar2,
Avatar3,
Avatar4,
Avatar5,
Avatar6,
Avatar7,
Avatar8,
Avatar9,
Avatar10,
Avatar11,
Avatar12,
Avatar13,
Avatar14,
Avatar15,
Avatar16,
]

type AvatarProps = {
source: User | Channel
showIndicator?: boolean
size?: "sm" | "md" | "lg" | "xl"
style?: ViewStyle
}

export function Avatar({ source, showIndicator = false, size = "sm", style }: AvatarProps) {
const isUser = source && "active" in source
const hash = source.id
.split("")
.map((c) => c.charCodeAt(0))
.reduce((a, b) => a + b)
const styles = createStyles({ size })

return (
<View style={style}>
<Image
style={styles.image}
source={
isUser
? avatars[hash % avatars.length]
: { uri: `https://api.dicebear.com/7.x/rings/png?seed=${source.id}` }
}
/>
{isUser && showIndicator && (
<View
style={[
styles.indicator,
{ backgroundColor: source.active ? colors.success : colors.neutral300 },
]}
/>
)}
</View>
)
}

function createStyles({ size }: Required<Pick<AvatarProps, "size">>) {
return StyleSheet.create({
image: {
width: { sm: 27, md: 36, lg: 64, xl: 88 }[size],
height: { sm: 27, md: 36, lg: 64, xl: 88 }[size],
borderRadius: { sm: 27, md: 36, lg: 64, xl: 88 }[size],
},
indicator: {
borderColor: colors.neutral0,
borderRadius: { sm: 12, md: 18, lg: 22, xl: 64 }[size],
borderWidth: { sm: 2, md: 3, lg: 3, xl: 4 }[size],
top: { sm: 16, md: 20, lg: 42, xl: 64 }[size],
height: { sm: 12, md: 18, lg: 22, xl: 28 }[size],
position: "absolute",
left: { sm: 16, md: 20, lg: 42, xl: 64 }[size],
width: { sm: 12, md: 18, lg: 22, xl: 28 }[size],
},
})
}
1 change: 1 addition & 0 deletions samples/react-native-group-chat/components/avatar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Avatar"

This file was deleted.

This file was deleted.

Loading

0 comments on commit af306e2

Please sign in to comment.