From 67dca172db7c96f373ec9f623d4497787589c51c Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 5 Nov 2021 21:57:44 +0300 Subject: [PATCH] Auto-detect time format by nearest country --- src/api/gramjs/apiBuilders/misc.ts | 17 +++++---- src/api/gramjs/methods/index.ts | 5 +-- src/api/gramjs/methods/settings.ts | 17 +++++++-- src/api/gramjs/methods/users.ts | 13 ------- .../left/settings/SettingsGeneral.tsx | 9 +++-- src/components/main/Main.tsx | 29 ++++++++++---- src/config.ts | 3 ++ src/global/initial.ts | 1 + src/global/types.ts | 6 ++- src/modules/actions/api/initial.ts | 15 -------- src/modules/actions/api/settings.ts | 38 +++++++++++++++++++ src/types/index.ts | 1 + src/util/langProvider.ts | 10 +---- src/util/phoneNumber.ts | 2 +- 14 files changed, 101 insertions(+), 65 deletions(-) diff --git a/src/api/gramjs/apiBuilders/misc.ts b/src/api/gramjs/apiBuilders/misc.ts index 63942ce2a..9c1ceb32b 100644 --- a/src/api/gramjs/apiBuilders/misc.ts +++ b/src/api/gramjs/apiBuilders/misc.ts @@ -132,13 +132,16 @@ function buildApiCountry(country: GramJs.help.Country, code?: GramJs.help.Countr } export function buildApiCountryList(countries: GramJs.help.Country[]) { - const listByCode = flatten(countries - .filter((country) => !country.hidden) - .map((country) => ( - country.countryCodes.map((code) => buildApiCountry(country, code)) - ))).sort((a: ApiCountry, b: ApiCountry) => ( - a.name ? a.name.localeCompare(b.name!) : a.defaultName.localeCompare(b.defaultName) - )); + const listByCode = flatten( + countries + .filter((country) => !country.hidden) + .map((country) => ( + country.countryCodes.map((code) => buildApiCountry(country, code)) + )), + ) + .sort((a: ApiCountry, b: ApiCountry) => ( + a.name ? a.name.localeCompare(b.name!) : a.defaultName.localeCompare(b.defaultName) + )); const generalList = countries .filter((country) => !country.hidden) diff --git a/src/api/gramjs/methods/index.ts b/src/api/gramjs/methods/index.ts index 79d3f4ddb..1bf4c460c 100644 --- a/src/api/gramjs/methods/index.ts +++ b/src/api/gramjs/methods/index.ts @@ -26,8 +26,7 @@ export { } from './messages'; export { - fetchFullUser, fetchNearestCountry, fetchCountryList, - fetchTopUsers, fetchContactList, fetchUsers, + fetchFullUser, fetchNearestCountry, fetchTopUsers, fetchContactList, fetchUsers, addContact, updateContact, deleteUser, fetchProfilePhotos, } from './users'; @@ -47,7 +46,7 @@ export { fetchAuthorizations, terminateAuthorization, terminateAllAuthorizations, fetchNotificationExceptions, fetchNotificationSettings, updateContactSignUpNotification, updateNotificationSettings, fetchLanguages, fetchLangPack, fetchPrivacySettings, setPrivacySettings, registerDevice, unregisterDevice, - updateIsOnline, fetchContentSettings, updateContentSettings, fetchLangStrings, + updateIsOnline, fetchContentSettings, updateContentSettings, fetchLangStrings, fetchCountryList, } from './settings'; export { diff --git a/src/api/gramjs/methods/settings.ts b/src/api/gramjs/methods/settings.ts index a741e87f5..d69b12b03 100644 --- a/src/api/gramjs/methods/settings.ts +++ b/src/api/gramjs/methods/settings.ts @@ -4,11 +4,11 @@ import { Api as GramJs } from '../../../lib/gramjs'; import { ApiChat, ApiLangString, ApiLanguage, ApiNotifyException, ApiUser, ApiWallpaper, } from '../../types'; -import { ApiPrivacyKey, InputPrivacyRules } from '../../../types'; +import { ApiPrivacyKey, InputPrivacyRules, LangCode } from '../../../types'; import { BLOCKED_LIST_LIMIT, DEFAULT_LANG_PACK, LANG_PACKS } from '../../../config'; import { - buildApiWallpaper, buildApiSession, buildPrivacyRules, buildApiNotifyException, + buildApiWallpaper, buildApiSession, buildPrivacyRules, buildApiNotifyException, buildApiCountryList, } from '../apiBuilders/misc'; import { buildApiUser } from '../apiBuilders/users'; @@ -17,9 +17,9 @@ import { buildInputPrivacyKey, buildInputPeer, buildInputEntity } from '../gramj import { invokeRequest, uploadFile, getClient } from './client'; import { omitVirtualClassFields } from '../apiBuilders/helpers'; import { buildCollectionByKey } from '../../../util/iteratees'; -import localDb from '../localDb'; import { getServerTime } from '../../../util/serverTime'; import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers'; +import localDb from '../localDb'; const MAX_INT_32 = 2 ** 31 - 1; const BETA_LANG_CODES = ['ar', 'fa', 'id', 'ko', 'uz']; @@ -447,3 +447,14 @@ function updateLocalDb( } }); } + +export async function fetchCountryList({ langCode = 'en' }: { langCode?: LangCode }) { + const countryList = await invokeRequest(new GramJs.help.GetCountriesList({ + langCode, + })); + + if (!(countryList instanceof GramJs.help.CountriesList)) { + return undefined; + } + return buildApiCountryList(countryList.countries); +} diff --git a/src/api/gramjs/methods/users.ts b/src/api/gramjs/methods/users.ts index 5bc44b6e3..f0f69e2a1 100644 --- a/src/api/gramjs/methods/users.ts +++ b/src/api/gramjs/methods/users.ts @@ -3,7 +3,6 @@ import { Api as GramJs } from '../../../lib/gramjs'; import { OnApiUpdate, ApiUser, ApiChat, ApiPhoto, } from '../../types'; -import { LangCode } from '../../../types'; import { PROFILE_PHOTOS_LIMIT } from '../../../config'; import { invokeRequest } from './client'; @@ -18,7 +17,6 @@ import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiPhoto } from '../apiBuilders/common'; import localDb from '../localDb'; import { addPhotoToLocalDb } from '../helpers'; -import { buildApiCountryList } from '../apiBuilders/misc'; import { buildApiPeerId } from '../apiBuilders/peers'; let onUpdate: OnApiUpdate; @@ -62,17 +60,6 @@ export async function fetchNearestCountry() { return dcInfo?.country; } -export async function fetchCountryList({ langCode = 'en' }: { langCode?: LangCode }) { - const countryList = await invokeRequest(new GramJs.help.GetCountriesList({ - langCode, - })); - - if (!(countryList instanceof GramJs.help.CountriesList)) { - return undefined; - } - return buildApiCountryList(countryList.countries); -} - export async function fetchTopUsers() { const topPeers = await invokeRequest(new GramJs.contacts.GetTopPeers({ correspondents: true, diff --git a/src/components/left/settings/SettingsGeneral.tsx b/src/components/left/settings/SettingsGeneral.tsx index 9ae234fde..e6b559070 100644 --- a/src/components/left/settings/SettingsGeneral.tsx +++ b/src/components/left/settings/SettingsGeneral.tsx @@ -132,10 +132,11 @@ const SettingsGeneral: FC = ({ setSettingOption({ messageTextSize: newSize }); }, [setSettingOption]); - const handleTimeFormatChange = useCallback((value: string) => { - setTimeFormat(value as TimeFormat, () => { - setSettingOption({ timeFormat: value }); - }); + const handleTimeFormatChange = useCallback((newTimeFormat: string) => { + setSettingOption({ timeFormat: newTimeFormat }); + setSettingOption({ wasTimeFormatSetManually: true }); + + setTimeFormat(newTimeFormat as TimeFormat); }, [setSettingOption]); const handleStickerSetClick = useCallback((value: ApiSticker) => { diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 9874bf3b0..afeec7d90 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -48,7 +48,6 @@ import StickerSetModal from '../common/StickerSetModal.async'; import './Main.scss'; type StateProps = { - animationLevel: number; lastSyncTime?: number; isLeftColumnShown: boolean; isRightColumnShown: boolean; @@ -61,14 +60,17 @@ type StateProps = { safeLinkModalUrl?: string; isHistoryCalendarOpen: boolean; shouldSkipHistoryAnimations?: boolean; - language?: LangCode; openedStickerSetShortName?: string; isServiceChatReady?: boolean; + animationLevel: number; + language?: LangCode; + wasTimeFormatSetManually?: boolean; }; type DispatchProps = Pick; const NOTIFICATION_INTERVAL = 1000; @@ -84,7 +86,6 @@ const Main: FC = ({ isRightColumnShown, isMediaViewerOpen, isForwardModalOpen, - animationLevel, hasNotifications, hasDialogs, audioMessage, @@ -92,9 +93,11 @@ const Main: FC = ({ safeLinkModalUrl, isHistoryCalendarOpen, shouldSkipHistoryAnimations, - language, openedStickerSetShortName, isServiceChatReady, + animationLevel, + language, + wasTimeFormatSetManually, loadAnimatedEmojis, loadNotificationSettings, loadNotificationExceptions, @@ -102,6 +105,7 @@ const Main: FC = ({ loadTopInlineBots, loadEmojiKeywords, loadCountryList, + ensureTimeFormat, openStickerSetShortName, checkVersionNotification, }) => { @@ -138,6 +142,12 @@ const Main: FC = ({ } }, [lastSyncTime, isServiceChatReady, checkVersionNotification]); + useEffect(() => { + if (lastSyncTime && !wasTimeFormatSetManually) { + ensureTimeFormat(); + } + }, [lastSyncTime, wasTimeFormatSetManually, ensureTimeFormat]); + useEffect(() => { if (lastSyncTime && LOCATION_HASH.startsWith('#?tgaddr=')) { processDeepLink(decodeURIComponent(LOCATION_HASH.substr('#?tgaddr='.length))); @@ -291,13 +301,13 @@ function updatePageTitle(nextTitle: string) { export default memo(withGlobal( (global): StateProps => { + const { settings: { byKey: { animationLevel, language, wasTimeFormatSetManually } } } = global; const { chatId: audioChatId, messageId: audioMessageId, origin } = global.audioPlayer; const audioMessage = audioChatId && audioMessageId ? selectChatMessage(global, audioChatId, audioMessageId) : undefined; return { - animationLevel: global.settings.byKey.animationLevel, lastSyncTime: global.lastSyncTime, isLeftColumnShown: global.isLeftColumnShown, isRightColumnShown: selectIsRightColumnShown(global), @@ -310,13 +320,16 @@ export default memo(withGlobal( safeLinkModalUrl: global.safeLinkModalUrl, isHistoryCalendarOpen: Boolean(global.historyCalendarSelectedAt), shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations, - language: global.settings.byKey.language, openedStickerSetShortName: global.openedStickerSetShortName, isServiceChatReady: selectIsServiceChatReady(global), + animationLevel, + language, + wasTimeFormatSetManually, }; }, (setGlobal, actions): DispatchProps => pick(actions, [ 'loadAnimatedEmojis', 'loadNotificationSettings', 'loadNotificationExceptions', 'updateIsOnline', - 'loadTopInlineBots', 'loadEmojiKeywords', 'openStickerSetShortName', 'loadCountryList', 'checkVersionNotification', + 'loadTopInlineBots', 'loadEmojiKeywords', 'openStickerSetShortName', 'loadCountryList', 'ensureTimeFormat', + 'checkVersionNotification', ]), )(Main)); diff --git a/src/config.ts b/src/config.ts index 4cab66207..fe4c370ca 100644 --- a/src/config.ts +++ b/src/config.ts @@ -145,6 +145,9 @@ export const RE_MENTION_TEMPLATE = '(@[\\w\\d_-]+)'; export const RE_TG_LINK = /^tg:(\/\/)?([?=&\d\w_-]+)?/gm; export const RE_TME_LINK = /^(?:https?:\/\/)?(?:t\.me\/)/gm; +// eslint-disable-next-line max-len +export const COUNTRIES_WITH_12H_TIME_FORMAT = new Set(['AU', 'BD', 'CA', 'CO', 'EG', 'HN', 'IE', 'IN', 'JO', 'MX', 'MY', 'NI', 'NZ', 'PH', 'PK', 'SA', 'SV', 'US']); + // MTProto constants export const SERVICE_NOTIFICATIONS_USER_ID = '777000'; export const REPLIES_USER_ID = '1271266957'; // TODO For Test connection ID must be equal to 708513 diff --git a/src/global/initial.ts b/src/global/initial.ts index e6f56a162..32d29c178 100644 --- a/src/global/initial.ts +++ b/src/global/initial.ts @@ -149,6 +149,7 @@ export const INITIAL_STATE: GlobalState = { shouldLoopStickers: true, language: 'en', timeFormat: '24h', + wasTimeFormatSetManually: false, }, themes: { light: { diff --git a/src/global/types.ts b/src/global/types.ts index 160c52f75..30bcf6e1a 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -500,7 +500,7 @@ export type ActionTypes = ( 'togglePreHistoryHidden' | 'updateChatDefaultBannedRights' | 'updateChatMemberBannedRights' | 'updateChatAdmin' | 'acceptInviteConfirmation' | // users - 'loadFullUser' | 'openUserInfo' | 'loadNearestCountry' | 'loadCountryList' | 'loadTopUsers' | 'loadContactList' | + 'loadFullUser' | 'openUserInfo' | 'loadNearestCountry' | 'loadTopUsers' | 'loadContactList' | 'loadCurrentUser' | 'updateProfile' | 'checkUsername' | 'addContact' | 'updateContact' | 'deleteUser' | 'loadUser' | 'setUserSearchQuery' | // Channel / groups creation @@ -523,11 +523,13 @@ export type ActionTypes = ( // bots 'clickInlineButton' | 'sendBotCommand' | 'loadTopInlineBots' | 'queryInlineBot' | 'sendInlineBotResult' | 'resetInlineBot' | 'restartBot' | 'startBot' | - // misc + // media viewer & audio player 'openMediaViewer' | 'closeMediaViewer' | 'openAudioPlayer' | 'setAudioPlayerVolume' | 'closeAudioPlayer' | + // misc 'openPollModal' | 'closePollModal' | 'loadWebPagePreview' | 'clearWebPagePreview' | 'loadWallpapers' | 'uploadWallpaper' | 'setDeviceToken' | 'deleteDeviceToken' | 'checkVersionNotification' | 'createServiceNotification' | + 'loadCountryList' | 'ensureTimeFormat' | // payment 'openPaymentModal' | 'closePaymentModal' | 'addPaymentError' | 'validateRequestedInfo' | 'setPaymentStep' | 'sendPaymentForm' | 'getPaymentForm' | 'getReceipt' | diff --git a/src/modules/actions/api/initial.ts b/src/modules/actions/api/initial.ts index 719ce42e4..ca98d6d3c 100644 --- a/src/modules/actions/api/initial.ts +++ b/src/modules/actions/api/initial.ts @@ -182,21 +182,6 @@ addReducer('loadNearestCountry', (global) => { })(); }); -addReducer('loadCountryList', (global, actions, payload = {}) => { - let { langCode } = payload; - if (!langCode) langCode = global.settings.byKey.language; - - (async () => { - const countryList = await callApi('fetchCountryList', { langCode }); - if (!countryList) return; - - setGlobal({ - ...getGlobal(), - countryList, - }); - })(); -}); - addReducer('setDeviceToken', (global, actions, deviceToken) => { setGlobal({ ...global, diff --git a/src/modules/actions/api/settings.ts b/src/modules/actions/api/settings.ts index 0cc0ac617..3c44cc758 100644 --- a/src/modules/actions/api/settings.ts +++ b/src/modules/actions/api/settings.ts @@ -6,9 +6,11 @@ import { UPLOADING_WALLPAPER_SLUG, } from '../../../types'; +import { COUNTRIES_WITH_12H_TIME_FORMAT } from '../../../config'; import { callApi } from '../../../api/gramjs'; import { buildCollectionByKey } from '../../../util/iteratees'; import { subscribe, unsubscribe } from '../../../util/notifications'; +import { setTimeFormat } from '../../../util/langProvider'; import { selectUser } from '../../selectors'; import { addUsers, addBlockedContact, updateChats, updateUser, removeBlockedContact, replaceSettings, updateNotifySettings, @@ -564,3 +566,39 @@ addReducer('updateContentSettings', (global, actions, payload) => { } })(); }); + +addReducer('loadCountryList', (global, actions, payload = {}) => { + let { langCode } = payload; + if (!langCode) langCode = global.settings.byKey.language; + + (async () => { + const countryList = await callApi('fetchCountryList', { langCode }); + if (!countryList) return; + + setGlobal({ + ...getGlobal(), + countryList, + }); + })(); +}); + +addReducer('ensureTimeFormat', (global, actions) => { + if (global.authNearestCountry) { + const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(global.authNearestCountry.toUpperCase()) ? '12h' : '24h'; + actions.setSettingOption({ timeFormat }); + setTimeFormat(timeFormat); + } + + (async () => { + if (getGlobal().settings.byKey.wasTimeFormatSetManually) { + return; + } + + const nearestCountryCode = await callApi('fetchNearestCountry'); + if (nearestCountryCode) { + const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(nearestCountryCode.toUpperCase()) ? '12h' : '24h'; + actions.setSettingOption({ timeFormat }); + setTimeFormat(timeFormat); + } + })(); +}); diff --git a/src/types/index.ts b/src/types/index.ts index dbb6ee32d..b2f0b1a17 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -70,6 +70,7 @@ export interface ISettings extends NotifySettings, Record { isSensitiveEnabled?: boolean; canChangeSensitive?: boolean; timeFormat: TimeFormat; + wasTimeFormatSetManually: boolean; } export interface ApiPrivacySettings { diff --git a/src/util/langProvider.ts b/src/util/langProvider.ts index cb4807c71..4323e1f09 100644 --- a/src/util/langProvider.ts +++ b/src/util/langProvider.ts @@ -139,22 +139,14 @@ export async function setLanguage(langCode: LangCode, callback?: NoneToVoidFunct runCallbacks(); } -export function setTimeFormat(timeFormat: TimeFormat, callback?: NoneToVoidFunction) { +export function setTimeFormat(timeFormat: TimeFormat) { if (timeFormat && timeFormat === currentTimeFormat) { - if (callback) { - callback(); - } - return; } currentTimeFormat = timeFormat; getTranslation.timeFormat = timeFormat; - if (callback) { - callback(); - } - runCallbacks(); } diff --git a/src/util/phoneNumber.ts b/src/util/phoneNumber.ts index b979a7c99..c822ebbaa 100644 --- a/src/util/phoneNumber.ts +++ b/src/util/phoneNumber.ts @@ -8,7 +8,7 @@ export function getCountryCodesByIso(phoneCodeList: ApiCountryCode[], iso: strin return phoneCodeList.filter((country) => country.iso2 === iso); } -export function getCountryFromPhoneNumber(phoneCodeList: ApiCountryCode[], input: string = '') { +export function getCountryFromPhoneNumber(phoneCodeList: ApiCountryCode[], input = '') { let phoneNumber = input.replace(/[^\d+]+/g, ''); if (phoneNumber.startsWith('+')) { phoneNumber = phoneNumber.substr(1);