From 9eca1e539a15c268e9228b80aca56226b7cb3ed3 Mon Sep 17 00:00:00 2001 From: "Andrew W. Hill" Date: Mon, 25 Mar 2019 16:09:03 -0700 Subject: [PATCH 1/2] Fixes Type.Message click from Notifications (#986) * removes old migration field * adds notification prompt --- .env.example | 1 - App/Containers/Groups.tsx | 58 +++++++++++++++++++++++++++++++-- App/Sagas/NotificationsSagas.ts | 1 + 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 652ad71bc..d0d09799e 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,6 @@ RN_MAILCHIMP_API_URL="https://something.api.mailchimp.com" RN_MAILCHIMP_API_KEY="a-key" RN_MAILCHIMP_LIST_ID="a-list-id" RN_MAILCHIMP_WAITLIST_ID="a-list-id" -RN_PEER_SWAP="https://" RN_RELEASE_TYPE="dev" RN_IOS_STORE_LINK="https://" RN_ANDROID_STORE_LINK="https://" \ No newline at end of file diff --git a/App/Containers/Groups.tsx b/App/Containers/Groups.tsx index 7a4ac7916..364da4679 100644 --- a/App/Containers/Groups.tsx +++ b/App/Containers/Groups.tsx @@ -2,7 +2,7 @@ import React from 'react' import {Dispatch} from 'redux' import { NavigationScreenProps } from 'react-navigation' import { connect } from 'react-redux' -import { FlatList, View, Text, TouchableOpacity } from 'react-native' +import { FlatList, View, Text, TouchableOpacity, Alert } from 'react-native' import {RootAction, RootState} from '../Redux/Types' @@ -10,6 +10,7 @@ import { getThreads } from '../Redux/PhotoViewingSelectors' import { contactsSelectors } from '../features/contacts' import UIActions from '../Redux/UIRedux' import TextileEventsActions from '../Redux/TextileEventsRedux' +import PreferencesActions from '../Redux/PreferencesRedux' import { pb } from '@textile/react-native-sdk' @@ -29,11 +30,14 @@ interface GroupAuthors { interface StateProps { threads: ReadonlyArray + showNotificationsPrompt: boolean } interface DispatchProps { refreshMessages: () => void navigateToThread: (id: string, name: string) => void + enableNotifications: () => void + completeNotifications: () => void } interface NavProps { @@ -136,6 +140,13 @@ class Groups extends React.Component { }) } + componentDidUpdate (prevProps, prevState) { + // ensure that it only gets called once by using the first update of the state or a new group add + if (this.props.threads.length && this.props.threads.length !== prevProps.threads.length && this.props.showNotificationsPrompt) { + this.notificationPrompt() + } + } + render () { return ( @@ -159,11 +170,40 @@ class Groups extends React.Component { ) } + + // Simple Alert based prompt to get Notification permissions + notificationPrompt () { + // never show it again + this.props.completeNotifications() + // give the user a prompt + Alert.alert( + 'Notifications', + 'Want to receive notifications when you receive new photos or invites?', + [ + { + text: 'Yes please', + onPress: () => { + this.props.enableNotifications() + } + }, + { text: 'Not now', style: 'cancel' }, + { + text: 'Show all options', + onPress: () => { + this.props.navigation.navigate('Settings') + } + } + ], + { cancelable: false } + ) + } + } const mapStateToProps = (state: RootState): StateProps => { const ownAddress = state.account.address.value const profile = state.account.profile.value + let memberCount = 0 const threads = getThreads(state, 'date') .map((thread) => { const selector = contactsSelectors.makeByThreadId(thread.id) @@ -172,6 +212,8 @@ const mapStateToProps = (state: RootState): StateProps => { members.unshift(profile) } const thumb = thread.photos.length ? thread.photos[0] : undefined + // just get a sense of how many group x members there are + memberCount += members.length return { id: thread.id, name: thread.name, @@ -181,8 +223,14 @@ const mapStateToProps = (state: RootState): StateProps => { thumb } }) + + const showNotificationsPrompt = state.preferences.tourScreens.notifications && + threads.length > 0 && + memberCount > threads.length + return { - threads + threads, + showNotificationsPrompt } } @@ -193,6 +241,12 @@ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => { }, navigateToThread: (id: string, name: string) => { dispatch(UIActions.navigateToThreadRequest(id, name)) + }, + enableNotifications: () => { + dispatch(PreferencesActions.toggleServicesRequest('notifications', true)) + }, + completeNotifications: () => { + dispatch(PreferencesActions.completeTourSuccess('notifications')) } } } diff --git a/App/Sagas/NotificationsSagas.ts b/App/Sagas/NotificationsSagas.ts index 352e91746..311c38099 100644 --- a/App/Sagas/NotificationsSagas.ts +++ b/App/Sagas/NotificationsSagas.ts @@ -110,6 +110,7 @@ export function * notificationView (action: ActionType Date: Mon, 25 Mar 2019 16:44:04 -0700 Subject: [PATCH 2/2] Only show one avatar if a block of messages in a row (#985) * removes old migration field * just stacks message type entries from the same user * fix avatar --- App/Components/message.tsx | 16 ++++++++++------ App/screens/group/group.tsx | 22 ++++++++++++++++++++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/App/Components/message.tsx b/App/Components/message.tsx index 95abd7e22..55c04d137 100644 --- a/App/Components/message.tsx +++ b/App/Components/message.tsx @@ -53,18 +53,22 @@ export interface Props { message?: string time: string containerStyle?: ViewStyle + isSameUser?: boolean } const Message = (props: Props) => { const alignItems = props.message ? 'flex-start' : 'center' return ( - - + + {props.isSameUser && } + {!props.isSameUser && } - - {props.username} - {props.time.toUpperCase()} - + {!props.isSameUser && + + {props.username} + {props.time.toUpperCase()} + + } {props.message && {props.message} } diff --git a/App/screens/group/group.tsx b/App/screens/group/group.tsx index 9237becf6..5dea57480 100644 --- a/App/screens/group/group.tsx +++ b/App/screens/group/group.tsx @@ -24,6 +24,7 @@ import UIActions from '../../Redux/UIRedux' import PhotoViewingActions from '../../Redux/PhotoViewingRedux' import { CommentData } from '../../Components/comments' import { color } from '../../styles' +import { pb } from '@textile/react-native-sdk' const momentSpec: moment.CalendarSpec = { sameDay: 'LT', @@ -137,7 +138,21 @@ class Group extends Component { ) } - renderRow = ({ item }: ListRenderItemInfo) => { + sameUserAgain = (user: pb.IUser, previous: Item): boolean => { + if (!previous || !previous.type) { + return false + } + switch (previous.type) { + case 'message': { + return user.address === previous.data.user.address + } + default: { + return false + } + } + } + + renderRow = ({ item, index }: ListRenderItemInfo) => { switch (item.type) { case 'photo': { const { user, caption, date, target, files, likes, comments, block } = item.data @@ -181,13 +196,16 @@ class Group extends Component { } case 'message': { const { user, body, date } = item.data + const isSameUser = this.sameUserAgain(user, this.props.items[(index + 1)]) + const avatar = isSameUser ? undefined : user.avatar return ( ) }