Skip to content

Commit

Permalink
[#163591901,#163591918,#163591935] Messages Inbox/Deadlines/Archive T…
Browse files Browse the repository at this point in the history
…abs (v2) (#823)
  • Loading branch information
francescopersico authored and cloudify committed Feb 15, 2019
1 parent 6e0422e commit aca9f05
Show file tree
Hide file tree
Showing 25 changed files with 965 additions and 173 deletions.
7 changes: 6 additions & 1 deletion locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
global:
localization:
decimalSeparator: "."
dateFormats:
dayAndMonth: "dddd D MMMM"
jserror:
title: Unexpected error occurred
message: We have reported this to our team! Please close the app and start again!
Expand Down Expand Up @@ -436,13 +438,16 @@ messages:
one: You have 1 message
other: You have {{count}} messages
tab:
all: All
inbox: Inbox
deadlines: Deadlines
archive: Archive
contentTitle: Messages
refresh: Pull down to refresh
loading: Loading Messages...
yesterday: yesterday
cta:
archive: Archive
unarchive: Unarchive
pay: Pay € {{amount}}
paid: Paid € {{amount}}
reminder: Reminder
Expand Down
7 changes: 6 additions & 1 deletion locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
global:
localization:
decimalSeparator: ","
dateFormats:
dayAndMonth: "dddd D MMMM"
jserror:
title: Si è verificato un errore imprevisto
message: Abbiamo segnalato questo al nostro team! Si prega di chiudere l'app e
Expand Down Expand Up @@ -446,13 +448,16 @@ messages:
one: Hai 1 messaggio
other: Hai {{count}} messaggi
tab:
all: Tutti
inbox: Ricevuti
deadlines: Scadenze
archive: Archivio
contentTitle: Messaggi
refresh: Trascina in basso per aggiornare
loading: Caricamento dei messaggi...
yesterday: ieri
cta:
archive: Archivia
unarchive: Disarchivia
pay: Paga € {{amount}}
paid: Pagato € {{amount}}
reminder: Promemoria
Expand Down
74 changes: 74 additions & 0 deletions ts/components/helpers/withMessagesSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { none, Option, some } from "fp-ts/lib/Option";
import hoistNonReactStatics from "hoist-non-react-statics";
import { Omit } from "italia-ts-commons/lib/types";
import React from "react";

type State = {
selectedMessageIds: Option<Set<string>>;
};

export type InjectedWithMessagesSelectionProps = {
selectedMessageIds: Option<Set<string>>;
toggleMessageSelection: (id: string) => void;
resetSelection: () => void;
};

/**
* An HOC to maintain and manipulate the messages selection.
*/
export function withMessagesSelection<
P extends InjectedWithMessagesSelectionProps
>(WrappedComponent: React.ComponentType<P>) {
class WithMessagesSelection extends React.PureComponent<
Omit<P, keyof InjectedWithMessagesSelectionProps>,
State
> {
constructor(props: Omit<P, keyof InjectedWithMessagesSelectionProps>) {
super(props);
this.state = {
selectedMessageIds: none
};
}

public render() {
const { selectedMessageIds } = this.state;

return (
<WrappedComponent
{...this.props as P}
selectedMessageIds={selectedMessageIds}
toggleMessageSelection={this.toggleMessageSelection}
resetSelection={this.resetSelection}
/>
);
}

// A function to add/remove an id from the selectedMessageIds Set.
private toggleMessageSelection = (id: string) => {
this.setState(({ selectedMessageIds }) => {
return selectedMessageIds
.map(_ => {
const newSelectedMessageIds = new Set(_);
newSelectedMessageIds.has(id)
? newSelectedMessageIds.delete(id)
: newSelectedMessageIds.add(id);

return {
selectedMessageIds: some(newSelectedMessageIds)
};
})
.getOrElse({ selectedMessageIds: some(new Set().add(id)) });
});
};

private resetSelection = () => {
this.setState({
selectedMessageIds: none
});
};
}

hoistNonReactStatics(WithMessagesSelection, WrappedComponent);

return WithMessagesSelection;
}
94 changes: 94 additions & 0 deletions ts/components/messages/MessageAgenda.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { format } from "date-fns";
import { View } from "native-base";
import React from "react";
import {
SectionList,
SectionListData,
SectionListRenderItem,
StyleSheet
} from "react-native";

import I18n from "../../i18n";
import customVariables from "../../theme/variables";
import { MessageWithContentAndDueDatePO } from "../../types/MessageWithContentAndDueDatePO";
import H5 from "../ui/H5";
import MessageAgendaItem from "./MessageAgendaItem";

const styles = StyleSheet.create({
sectionHeader: {
paddingHorizontal: customVariables.contentPadding,
paddingVertical: customVariables.contentPadding / 2,
backgroundColor: customVariables.brandLightGray
},
itemSeparator: {
height: 1,
backgroundColor: customVariables.brandLightGray
}
});

const keyExtractor = (_: MessageWithContentAndDueDatePO) => _.id;

const ItemSeparatorComponent = () => <View style={styles.itemSeparator} />;

export type MessageAgendaSection = SectionListData<
MessageWithContentAndDueDatePO
>;

type Props = {
// Can't use ReadonlyArray because of the SectionList section prop
// typescript definition.
// tslint:disable-next-line:readonly-array
sections: MessageAgendaSection[];
isRefreshing: boolean;
onRefresh: () => void;
onPressItem: (id: string) => void;
};

/**
* A component to render messages with due_date in a agenda like form.
*/
class MessageAgenda extends React.PureComponent<Props> {
public render() {
const { sections, isRefreshing, onRefresh } = this.props;
return (
<SectionList
sections={sections}
keyExtractor={keyExtractor}
stickySectionHeadersEnabled={true}
alwaysBounceVertical={false}
ItemSeparatorComponent={ItemSeparatorComponent}
refreshing={isRefreshing}
onRefresh={onRefresh}
renderSectionHeader={this.renderSectionHeader}
renderItem={this.renderItem}
/>
);
}

private renderSectionHeader = (info: { section: MessageAgendaSection }) => {
return (
<H5 style={styles.sectionHeader}>
{format(
info.section.title,
I18n.t("global.dateFormats.dayAndMonth")
).toUpperCase()}
</H5>
);
};

private renderItem: SectionListRenderItem<
MessageWithContentAndDueDatePO
> = info => {
const message = info.item;
return (
<MessageAgendaItem
id={message.id}
subject={message.content.subject}
due_date={message.content.due_date}
onPress={this.props.onPressItem}
/>
);
};
}

export default MessageAgenda;
50 changes: 50 additions & 0 deletions ts/components/messages/MessageAgendaItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { format } from "date-fns";
import { Text, View } from "native-base";
import React from "react";
import { StyleSheet, TouchableOpacity } from "react-native";

import customVariables from "../../theme/variables";
import { MessageWithContentPO } from "../../types/MessageWithContentPO";

const styles = StyleSheet.create({
container: {
padding: customVariables.contentPadding,
flexDirection: "row"
},
subject: {
flex: 1
},
hour: {
flex: 0
}
});

type Props = {
id: string;
subject: string;
due_date: NonNullable<MessageWithContentPO["content"]["due_date"]>;
onPress: (id: string) => void;
};

/**
* A component to render a single Agenda item.
* Extends PureComponent to avoid unnecessary re-renders.
*/
class MessageAgendaItem extends React.PureComponent<Props> {
public render() {
const { subject, due_date } = this.props;

return (
<TouchableOpacity onPress={this.handlePress}>
<View style={styles.container}>
<Text style={styles.subject}>{subject}</Text>
<Text style={styles.hour}>{format(due_date, "HH:mm")}</Text>
</View>
</TouchableOpacity>
);
}

private handlePress = () => this.props.onPress(this.props.id);
}

export default MessageAgendaItem;
13 changes: 10 additions & 3 deletions ts/components/messages/MessageListComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Option } from "fp-ts/lib/Option";
import * as pot from "italia-ts-commons/lib/pot";
import * as React from "react";
import { FlatList, ListRenderItemInfo, RefreshControl } from "react-native";

import { MessageState } from "../../store/reducers/entities/messages/messagesById";

import { PaymentByRptIdState } from "../../store/reducers/entities/payments";
import { ServicesByIdState } from "../../store/reducers/entities/services/servicesById";
import { MessageListItemComponent } from "./MessageListItemComponent";
Expand All @@ -14,7 +14,9 @@ type OwnProps = {
paymentByRptId: PaymentByRptIdState;
refreshing: boolean;
onRefresh: () => void;
onListItemPress?: (messageId: string) => void;
onPressItem: (id: string) => void;
onLongPressItem: (id: string) => void;
selectedMessageIds: Option<Set<string>>;
};

type Props = OwnProps;
Expand All @@ -32,7 +34,12 @@ class MessageListComponent extends React.Component<Props> {
messageState={info.item}
paymentByRptId={this.props.paymentByRptId}
service={service !== undefined ? service : pot.none}
onItemPress={this.props.onListItemPress}
onPress={this.props.onPressItem}
onLongPress={this.props.onLongPressItem}
isSelectionModeEnabled={this.props.selectedMessageIds.isSome()}
isSelected={this.props.selectedMessageIds
.map(_ => _.has(info.item.meta.id))
.getOrElse(false)}
/>
);
};
Expand Down
Loading

0 comments on commit aca9f05

Please sign in to comment.