Compare commits

...

2 Commits

Author SHA1 Message Date
Danish Ahmed Mirza 7df61a8621 Change time format of RoomItems 2022-06-03 03:15:41 +05:30
Danish Ahmed Mirza 8b4cc16548 [NEW] Add an option to change message time format 2022-06-03 01:19:12 +05:30
17 changed files with 100 additions and 22 deletions

View File

@ -46,23 +46,22 @@ const styles = StyleSheet.create({
interface IMessageBoxReplyPreview { interface IMessageBoxReplyPreview {
replying: boolean; replying: boolean;
message: IMessage; message: IMessage;
Message_TimeFormat: string;
close(): void; close(): void;
baseUrl: string; baseUrl: string;
username: string; username: string;
getCustomEmoji: Function; getCustomEmoji: Function;
useRealName: boolean; useRealName: boolean;
timeFormat: string;
} }
const ReplyPreview = React.memo( const ReplyPreview = React.memo(
({ message, Message_TimeFormat, replying, close, useRealName }: IMessageBoxReplyPreview) => { ({ message, replying, close, useRealName, timeFormat }: IMessageBoxReplyPreview) => {
const { theme } = useTheme(); const { theme } = useTheme();
if (!replying) { if (!replying) {
return null; return null;
} }
const time = moment(message.ts).format(timeFormat);
const time = moment(message.ts).format(Message_TimeFormat);
return ( return (
<View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}> <View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}>
<View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}> <View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}>
@ -83,7 +82,6 @@ const ReplyPreview = React.memo(
); );
const mapStateToProps = (state: IApplicationState) => ({ const mapStateToProps = (state: IApplicationState) => ({
Message_TimeFormat: state.settings.Message_TimeFormat as string,
baseUrl: state.server.server, baseUrl: state.server.server,
useRealName: state.settings.UI_Use_Real_Name as boolean useRealName: state.settings.UI_Use_Real_Name as boolean
}); });

View File

@ -1077,6 +1077,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
username={user.username} username={user.username}
replying={replying} replying={replying}
getCustomEmoji={getCustomEmoji} getCustomEmoji={getCustomEmoji}
timeFormat={user.timeFormat}
/> />
) : null; ) : null;

View File

@ -120,13 +120,14 @@ class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
swipeEnabled, swipeEnabled,
autoJoin, autoJoin,
showAvatar, showAvatar,
displayMode displayMode,
timeFormat
} = this.props; } = this.props;
const name = getRoomTitle(item); const name = getRoomTitle(item);
const testID = `rooms-list-view-item-${name}`; const testID = `rooms-list-view-item-${name}`;
const avatar = getRoomAvatar(item); const avatar = getRoomAvatar(item);
const isRead = getIsRead(item); const isRead = getIsRead(item);
const date = item.roomUpdatedAt && formatDate(item.roomUpdatedAt); const date = item.roomUpdatedAt && formatDate(item.roomUpdatedAt, timeFormat);
const alert = item.alert || item.tunread?.length; const alert = item.alert || item.tunread?.length;
let accessibilityLabel = name; let accessibilityLabel = name;

View File

@ -92,6 +92,7 @@ export interface IRoomItemContainerProps {
autoJoin: boolean; autoJoin: boolean;
showAvatar: boolean; showAvatar: boolean;
displayMode: string; displayMode: string;
timeFormat: string;
} }
export interface IRoomItemProps { export interface IRoomItemProps {

View File

@ -21,6 +21,7 @@ export interface ILoggedUser {
showMessageInMainThread?: boolean; showMessageInMainThread?: boolean;
isFromWebView?: boolean; isFromWebView?: boolean;
enableMessageParserEarlyAdoption: boolean; enableMessageParserEarlyAdoption: boolean;
timeFormat: string;
} }
export interface ILoggedUserResultFromServer export interface ILoggedUserResultFromServer

View File

@ -112,6 +112,7 @@ export interface INotificationPreferences {
pushNotifications: TNotifications; pushNotifications: TNotifications;
emailNotificationMode?: 'mentions' | 'nothing'; emailNotificationMode?: 'mentions' | 'nothing';
language?: string; language?: string;
clockMode?: number;
} }
export interface IUserPreferences { export interface IUserPreferences {

View File

@ -1,6 +1,8 @@
{ {
"1_person_reacted": "1 person reacted", "1_person_reacted": "1 person reacted",
"1_user": "1 user", "1_user": "1 user",
"12_Hour": "12-hour clock",
"24_Hour": "24-hour clock",
"error-action-not-allowed": "{{action}} is not allowed", "error-action-not-allowed": "{{action}} is not allowed",
"error-application-not-found": "Application not found", "error-application-not-found": "Application not found",
"error-archived-duplicate-name": "There's an archived channel with name {{room_name}}", "error-archived-duplicate-name": "There's an archived channel with name {{room_name}}",
@ -327,6 +329,7 @@
"Message": "Message", "Message": "Message",
"Messages": "Messages", "Messages": "Messages",
"Message_Reported": "Message reported", "Message_Reported": "Message reported",
"Message_time_format": "Time format",
"Microphone_Permission_Message": "Rocket.Chat needs access to your microphone so you can send audio message.", "Microphone_Permission_Message": "Rocket.Chat needs access to your microphone so you can send audio message.",
"Microphone_Permission": "Microphone Permission", "Microphone_Permission": "Microphone Permission",
"Mute": "Mute", "Mute": "Mute",

View File

@ -29,4 +29,6 @@ export default class User extends Model {
@field('is_from_webview') isFromWebView; @field('is_from_webview') isFromWebView;
@field('enable_message_parser_early_adoption') enableMessageParserEarlyAdoption; @field('enable_message_parser_early_adoption') enableMessageParserEarlyAdoption;
@field('time_format') timeFormat;
} }

View File

@ -103,6 +103,15 @@ export default schemaMigrations({
columns: [{ name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true }] columns: [{ name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true }]
}) })
] ]
},
{
toVersion: 13,
steps: [
addColumns({
table: 'users',
columns: [{ name: 'time_format', type: 'string', isOptional: true }]
})
]
} }
] ]
}); });

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb'; import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({ export default appSchema({
version: 12, version: 13,
tables: [ tables: [
tableSchema({ tableSchema({
name: 'users', name: 'users',
@ -17,7 +17,8 @@ export default appSchema({
{ name: 'show_message_in_main_thread', type: 'boolean', isOptional: true }, { name: 'show_message_in_main_thread', type: 'boolean', isOptional: true },
{ name: 'avatar_etag', type: 'string', isOptional: true }, { name: 'avatar_etag', type: 'string', isOptional: true },
{ name: 'is_from_webview', type: 'boolean', isOptional: true }, { name: 'is_from_webview', type: 'boolean', isOptional: true },
{ name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true } { name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true },
{ name: 'time_format', type: 'string', isOptional: true }
] ]
}), }),
tableSchema({ tableSchema({

View File

@ -295,6 +295,9 @@ export default function subscribeRooms() {
if ((['settings.preferences.showMessageInMainThread'] as any) in diff) { if ((['settings.preferences.showMessageInMainThread'] as any) in diff) {
store.dispatch(setUser({ showMessageInMainThread: diff['settings.preferences.showMessageInMainThread'] })); store.dispatch(setUser({ showMessageInMainThread: diff['settings.preferences.showMessageInMainThread'] }));
} }
if ((['settings.preferences.clockMode'] as any) in diff) {
store.dispatch(setUser({ timeFormat: diff['settings.preferences.clockMode'] === 2 ? 'H:mm' : 'h:mm A' }));
}
} }
if (/subscriptions/.test(ev)) { if (/subscriptions/.test(ev)) {
if (type === 'removed') { if (type === 'removed') {

View File

@ -285,7 +285,8 @@ async function login(credentials: ICredentials, isFromWebView = false): Promise<
avatarETag: result.me.avatarETag, avatarETag: result.me.avatarETag,
isFromWebView, isFromWebView,
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true, showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true,
enableMessageParserEarlyAdoption: result.me.settings?.preferences?.enableMessageParserEarlyAdoption ?? true enableMessageParserEarlyAdoption: result.me.settings?.preferences?.enableMessageParserEarlyAdoption ?? true,
timeFormat: result.me.settings?.preferences?.clockMode === 2 ? 'H:mm' : 'h:mm A'
}; };
return user; return user;
} }

View File

@ -23,10 +23,10 @@ export const capitalize = (s: string): string => {
return s.charAt(0).toUpperCase() + s.slice(1); return s.charAt(0).toUpperCase() + s.slice(1);
}; };
export const formatDate = (date: string | Date): string => export const formatDate = (date: string | Date, timeFormat: string | undefined): string =>
moment(date).calendar(null, { moment(date).calendar(null, {
lastDay: `[${I18n.t('Yesterday')}]`, lastDay: `[${I18n.t('Yesterday')}]`,
sameDay: 'LT', sameDay: timeFormat || 'LT',
lastWeek: 'dddd', lastWeek: 'dddd',
sameElse: 'L' sameElse: 'L'
}); });

View File

@ -139,12 +139,11 @@ const roomAttrsUpdate = [
] as TRoomUpdate[]; ] as TRoomUpdate[];
interface IRoomViewProps extends IBaseScreen<ChatsStackParamList, 'RoomView'> { interface IRoomViewProps extends IBaseScreen<ChatsStackParamList, 'RoomView'> {
user: Pick<ILoggedUser, 'id' | 'username' | 'token' | 'showMessageInMainThread'>; user: Pick<ILoggedUser, 'id' | 'username' | 'token' | 'showMessageInMainThread' | 'timeFormat'>;
appState: string; appState: string;
useRealName?: boolean; useRealName?: boolean;
isAuthenticated: boolean; isAuthenticated: boolean;
Message_GroupingPeriod?: number; Message_GroupingPeriod?: number;
Message_TimeFormat?: string;
Message_Read_Receipt_Enabled?: boolean; Message_Read_Receipt_Enabled?: boolean;
Hide_System_Messages?: string[]; Hide_System_Messages?: string[];
baseUrl: string; baseUrl: string;
@ -1185,8 +1184,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
renderItem = (item: TAnyMessageModel, previousItem: TAnyMessageModel, highlightedMessage?: string) => { renderItem = (item: TAnyMessageModel, previousItem: TAnyMessageModel, highlightedMessage?: string) => {
const { room, lastOpen, canAutoTranslate } = this.state; const { room, lastOpen, canAutoTranslate } = this.state;
const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } = const { user, Message_GroupingPeriod, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } = this.props;
this.props;
let dateSeparator = null; let dateSeparator = null;
let showUnreadSeparator = false; let showUnreadSeparator = false;
@ -1237,7 +1235,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
isSystemMessage={room.sysMes as boolean} isSystemMessage={room.sysMes as boolean}
baseUrl={baseUrl} baseUrl={baseUrl}
Message_GroupingPeriod={Message_GroupingPeriod} Message_GroupingPeriod={Message_GroupingPeriod}
timeFormat={Message_TimeFormat} timeFormat={user.timeFormat}
useRealName={useRealName} useRealName={useRealName}
isReadReceiptEnabled={Message_Read_Receipt_Enabled} isReadReceiptEnabled={Message_Read_Receipt_Enabled}
autoTranslateRoom={canAutoTranslate && 'id' in room && room.autoTranslate} autoTranslateRoom={canAutoTranslate && 'id' in room && room.autoTranslate}
@ -1446,7 +1444,6 @@ const mapStateToProps = (state: IApplicationState) => ({
useRealName: state.settings.UI_Use_Real_Name as boolean, useRealName: state.settings.UI_Use_Real_Name as boolean,
isAuthenticated: state.login.isAuthenticated, isAuthenticated: state.login.isAuthenticated,
Message_GroupingPeriod: state.settings.Message_GroupingPeriod as number, Message_GroupingPeriod: state.settings.Message_GroupingPeriod as number,
Message_TimeFormat: state.settings.Message_TimeFormat as string,
customEmojis: state.customEmojis, customEmojis: state.customEmojis,
baseUrl: state.server.server, baseUrl: state.server.server,
serverVersion: state.server.version, serverVersion: state.server.version,

View File

@ -936,7 +936,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
const { item: currentItem } = this.state; const { item: currentItem } = this.state;
const { const {
user: { username }, user: { username, timeFormat },
StoreLastMessage, StoreLastMessage,
useRealName, useRealName,
theme, theme,
@ -972,6 +972,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
swipeEnabled={swipeEnabled} swipeEnabled={swipeEnabled}
showAvatar={showAvatar} showAvatar={showAvatar}
displayMode={displayMode} displayMode={displayMode}
timeFormat={timeFormat}
/> />
); );
}; };

View File

@ -17,7 +17,7 @@ import RoomHeader from '../containers/RoomHeader';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import SearchHeader from '../containers/SearchHeader'; import SearchHeader from '../containers/SearchHeader';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { IApplicationState, IBaseScreen, TSubscriptionModel } from '../definitions'; import { IApplicationState, IBaseScreen, TSubscriptionModel, IUser } from '../definitions';
import { ERoomType } from '../definitions/ERoomType'; import { ERoomType } from '../definitions/ERoomType';
import { withDimensions } from '../dimensions'; import { withDimensions } from '../dimensions';
import I18n from '../i18n'; import I18n from '../i18n';
@ -88,6 +88,7 @@ interface ITeamChannelsViewProps extends IBaseScreen<ChatsStackParamList, 'TeamC
showActionSheet: (options: TActionSheetOptions) => void; showActionSheet: (options: TActionSheetOptions) => void;
showAvatar: boolean; showAvatar: boolean;
displayMode: DisplayMode; displayMode: DisplayMode;
user: IUser;
} }
class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChannelsViewState> { class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChannelsViewState> {
private teamId: string; private teamId: string;
@ -496,7 +497,15 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
}; };
renderItem = ({ item }: { item: IItem }) => { renderItem = ({ item }: { item: IItem }) => {
const { StoreLastMessage, useRealName, theme, width, showAvatar, displayMode } = this.props; const {
user: { timeFormat },
StoreLastMessage,
useRealName,
theme,
width,
showAvatar,
displayMode
} = this.props;
return ( return (
<RoomItem <RoomItem
item={item} item={item}
@ -513,6 +522,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
autoJoin={item.teamDefault} autoJoin={item.teamDefault}
showAvatar={showAvatar} showAvatar={showAvatar}
displayMode={displayMode} displayMode={displayMode}
timeFormat={timeFormat}
/> />
); );
}; };

View File

@ -14,14 +14,36 @@ import { getUserSelector } from '../../selectors/login';
import { ProfileStackParamList } from '../../stacks/types'; import { ProfileStackParamList } from '../../stacks/types';
import { Services } from '../../lib/services'; import { Services } from '../../lib/services';
import { useAppSelector } from '../../lib/hooks'; import { useAppSelector } from '../../lib/hooks';
import { useTheme } from '../../theme';
interface IUserPreferencesViewProps { interface IUserPreferencesViewProps {
navigation: StackNavigationProp<ProfileStackParamList, 'UserPreferencesView'>; navigation: StackNavigationProp<ProfileStackParamList, 'UserPreferencesView'>;
theme: string;
} }
const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Element => { const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Element => {
const { enableMessageParserEarlyAdoption, id } = useAppSelector(state => getUserSelector(state)); const { enableMessageParserEarlyAdoption, timeFormat, id } = useAppSelector(state => getUserSelector(state));
const dispatch = useDispatch(); const dispatch = useDispatch();
const { colors } = useTheme();
interface ITimeFormats {
label: string;
value: number;
format: string;
}
const timeFormats: ITimeFormats[] = [
{
label: '12_Hour',
value: 1,
format: 'h:mm A'
},
{
label: '24_Hour',
value: 2,
format: 'H:mm'
}
];
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation.setOptions({
@ -47,6 +69,17 @@ const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Ele
<Switch value={value} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleMessageParser} /> <Switch value={value} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleMessageParser} />
); );
const renderIcon = () => <List.Icon name='check' color={colors.tintColor} />;
const onChangeTimeFormat = async (item: ITimeFormats) => {
try {
dispatch(setUser({ timeFormat: item.format }));
await Services.saveUserPreferences({ id, clockMode: item.value });
} catch (e) {
log(e);
}
};
return ( return (
<SafeAreaView testID='preferences-view'> <SafeAreaView testID='preferences-view'>
<StatusBar /> <StatusBar />
@ -70,6 +103,21 @@ const UserPreferencesView = ({ navigation }: IUserPreferencesViewProps): JSX.Ele
/> />
<List.Separator /> <List.Separator />
</List.Section> </List.Section>
<List.Section title='Message_time_format'>
<List.Separator />
<List.Item
title={timeFormats[0].label}
onPress={() => onChangeTimeFormat(timeFormats[0])}
right={() => (timeFormat === timeFormats[0].format ? renderIcon() : null)}
/>
<List.Separator />
<List.Item
title={timeFormats[1].label}
onPress={() => onChangeTimeFormat(timeFormats[1])}
right={() => (timeFormat === timeFormats[1].format ? renderIcon() : null)}
/>
<List.Separator />
</List.Section>
</List.Container> </List.Container>
</SafeAreaView> </SafeAreaView>
); );