2021-12-08 07:44:24 +01:00
import React , { useEffect , useState } from 'react' ;
2021-06-24 07:10:38 +02:00
import { getConversationController } from '../../session/conversations' ;
2023-01-19 04:02:59 +01:00
import { syncConfigurationIfNeeded } from '../../session/utils/sync/syncUtils' ;
2021-05-25 05:19:34 +02:00
2023-01-25 07:46:30 +01:00
import { useDispatch , useSelector } from 'react-redux' ;
2021-03-01 07:43:45 +01:00
import {
2022-08-08 01:50:48 +02:00
Data ,
2021-03-01 07:43:45 +01:00
hasSyncedInitialConfigurationItem ,
2021-05-25 05:19:34 +02:00
lastAvatarUploadTimestamp ,
2021-03-01 07:43:45 +01:00
} from '../../data/data' ;
2021-02-22 01:07:58 +01:00
import { getMessageQueue } from '../../session/sending' ;
2021-09-13 09:07:53 +02:00
// tslint:disable: no-submodule-imports
import useInterval from 'react-use/lib/useInterval' ;
import useTimeoutFn from 'react-use/lib/useTimeoutFn' ;
2023-01-25 07:46:30 +01:00
import { clearSearch } from '../../state/ducks/search' ;
import { resetOverlayMode , SectionType , showLeftPaneSection } from '../../state/ducks/section' ;
2021-03-16 07:45:36 +01:00
import {
2023-03-16 00:49:06 +01:00
getGlobalUnreadMessageCount ,
2023-04-21 08:17:14 +02:00
getOurPrimaryConversation ,
2021-03-16 07:45:36 +01:00
} from '../../state/selectors/conversations' ;
import { getFocusedSection } from '../../state/selectors/section' ;
2023-01-25 07:46:30 +01:00
import { getOurNumber } from '../../state/selectors/user' ;
2021-04-22 10:10:10 +02:00
2021-06-24 07:35:42 +02:00
import { cleanUpOldDecryptedMedias } from '../../session/crypto/DecryptedAttachmentsManager' ;
2021-12-14 05:15:12 +01:00
2021-05-27 03:04:26 +02:00
import { DURATION } from '../../session/constants' ;
2022-08-08 01:50:48 +02:00
2022-03-15 02:13:33 +01:00
import { debounce , isEmpty , isString } from 'lodash' ;
2023-01-25 07:46:30 +01:00
import { uploadOurAvatar } from '../../interactions/conversationInteractions' ;
import { editProfileModal , onionPathModal } from '../../state/ducks/modalDialog' ;
2021-06-04 03:38:51 +02:00
2020-10-22 07:34:41 +02:00
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
2019-12-17 00:46:20 +01:00
2023-01-25 07:46:30 +01:00
import { ipcRenderer } from 'electron' ;
2021-12-14 05:15:12 +01:00
import { loadDefaultRooms } from '../../session/apis/open_group_api/opengroupV2/ApiUtil' ;
import { getOpenGroupManager } from '../../session/apis/open_group_api/opengroupV2/OpenGroupManagerV2' ;
import { getSwarmPollingInstance } from '../../session/apis/snode_api' ;
2023-01-25 07:46:30 +01:00
import { UserUtils } from '../../session/utils' ;
2021-12-14 05:15:12 +01:00
import { Avatar , AvatarSize } from '../avatar/Avatar' ;
2023-01-25 07:46:30 +01:00
import { ActionPanelOnionStatusLight } from '../dialog/OnionStatusPathDialog' ;
2021-12-14 05:15:12 +01:00
import { SessionIconButton } from '../icon' ;
2021-12-15 04:41:55 +01:00
import { LeftPaneSectionContainer } from './LeftPaneSectionContainer' ;
2021-12-08 07:44:24 +01:00
2022-08-08 01:50:48 +02:00
import { getLatestReleaseFromFileServer } from '../../session/apis/file_server_api/FileServerApi' ;
2022-11-07 01:17:42 +01:00
import { forceRefreshRandomSnodePool } from '../../session/apis/snode_api/snodePool' ;
2023-01-25 07:46:30 +01:00
import { isDarkTheme } from '../../state/selectors/theme' ;
import { ThemeStateType } from '../../themes/constants/colors' ;
import { switchThemeTo } from '../../themes/switchTheme' ;
2022-05-18 03:47:42 +02:00
2021-11-23 06:18:27 +01:00
const Section = ( props : { type : SectionType } ) = > {
2021-03-16 07:45:36 +01:00
const ourNumber = useSelector ( getOurNumber ) ;
2023-03-16 00:49:06 +01:00
const globalUnreadMessageCount = useSelector ( getGlobalUnreadMessageCount ) ;
2021-03-16 07:45:36 +01:00
const dispatch = useDispatch ( ) ;
2021-11-23 06:18:27 +01:00
const { type } = props ;
2021-03-16 07:45:36 +01:00
2022-10-14 06:08:46 +02:00
const isDarkMode = useSelector ( isDarkTheme ) ;
2021-03-16 07:45:36 +01:00
const focusedSection = useSelector ( getFocusedSection ) ;
const isSelected = focusedSection === props . type ;
2022-08-26 02:40:31 +02:00
const handleClick = async ( ) = > {
2021-03-16 07:45:36 +01:00
/* tslint:disable:no-void-expression */
if ( type === SectionType . Profile ) {
2023-01-25 07:46:30 +01:00
dispatch ( editProfileModal ( { } ) ) ;
2022-09-26 05:10:45 +02:00
} else if ( type === SectionType . ColorMode ) {
2022-09-26 04:04:53 +02:00
const currentTheme = String ( window . Events . getThemeSetting ( ) ) ;
2022-10-14 06:08:46 +02:00
const newTheme = ( isDarkMode
? currentTheme . replace ( 'dark' , 'light' )
: currentTheme . replace ( 'light' , 'dark' ) ) as ThemeStateType ;
2022-08-26 02:40:31 +02:00
2022-10-11 07:32:50 +02:00
// We want to persist the primary color when using the color mode button
2022-10-11 02:37:58 +02:00
await switchThemeTo ( {
theme : newTheme ,
mainWindow : true ,
2022-10-11 07:32:50 +02:00
usePrimaryColor : true ,
2022-10-11 02:37:58 +02:00
dispatch ,
} ) ;
2021-05-12 06:12:05 +02:00
} else if ( type === SectionType . PathIndicator ) {
// Show Path Indicator Modal
2021-06-18 08:41:35 +02:00
dispatch ( onionPathModal ( { } ) ) ;
2021-03-16 07:45:36 +01:00
} else {
2022-02-24 23:58:35 +01:00
// message section
2021-03-16 07:45:36 +01:00
dispatch ( clearSearch ( ) ) ;
dispatch ( showLeftPaneSection ( type ) ) ;
2022-08-16 03:11:52 +02:00
dispatch ( resetOverlayMode ( ) ) ;
2021-03-16 07:45:36 +01:00
}
} ;
if ( type === SectionType . Profile ) {
return (
< Avatar
2021-03-26 05:24:56 +01:00
size = { AvatarSize . XS }
2021-03-16 07:45:36 +01:00
onAvatarClick = { handleClick }
pubkey = { ourNumber }
2021-11-18 01:36:14 +01:00
dataTestId = "leftpane-primary-avatar"
2021-03-16 07:45:36 +01:00
/ >
) ;
}
2023-03-16 00:49:06 +01:00
const unreadToShow = type === SectionType . Message ? globalUnreadMessageCount : undefined ;
2021-05-20 03:27:21 +02:00
2021-03-16 07:45:36 +01:00
switch ( type ) {
case SectionType . Message :
2021-11-18 01:36:14 +01:00
return (
< SessionIconButton
iconSize = "medium"
dataTestId = "message-section"
iconType = { 'chatBubble' }
notificationCount = { unreadToShow }
onClick = { handleClick }
isSelected = { isSelected }
/ >
) ;
2021-03-16 07:45:36 +01:00
case SectionType . Settings :
2021-11-18 01:36:14 +01:00
return (
< SessionIconButton
iconSize = "medium"
dataTestId = "settings-section"
iconType = { 'gear' }
onClick = { handleClick }
isSelected = { isSelected }
/ >
) ;
case SectionType . PathIndicator :
return (
< ActionPanelOnionStatusLight
dataTestId = "onion-status-section"
handleClick = { handleClick }
isSelected = { isSelected }
2021-12-15 04:41:55 +01:00
id = { 'onion-path-indicator-led-id' }
2021-11-18 01:36:14 +01:00
/ >
) ;
2022-09-26 05:10:45 +02:00
case SectionType . ColorMode :
2021-03-16 07:45:36 +01:00
default :
2021-11-18 01:36:14 +01:00
return (
2021-06-09 02:06:23 +02:00
< SessionIconButton
2021-11-18 01:36:14 +01:00
iconSize = "medium"
2022-10-14 06:08:46 +02:00
iconType = { isDarkMode ? 'moon' : 'sun' }
2021-11-18 01:36:14 +01:00
dataTestId = "theme-section"
2021-06-09 02:06:23 +02:00
onClick = { handleClick }
isSelected = { isSelected }
/ >
2021-11-18 01:36:14 +01:00
) ;
}
2021-03-16 07:45:36 +01:00
} ;
2022-03-15 02:13:33 +01:00
const cleanUpMediasInterval = DURATION . MINUTES * 60 ;
2022-08-08 01:50:48 +02:00
// every 1 minute we fetch from the fileserver to check for a new release
2022-03-15 02:13:33 +01:00
// * if there is none, no request to github are made.
// * if there is a version on the fileserver more recent than our current, we fetch github to get the UpdateInfos and trigger an update as usual (asking user via dialog)
2022-03-21 06:29:07 +01:00
const fetchReleaseFromFileServerInterval = 1000 * 60 ; // try to fetch the latest release from the fileserver every minute
2021-04-13 06:10:31 +02:00
2022-08-26 02:40:31 +02:00
const setupTheme = async ( ) = > {
2021-04-30 05:26:33 +02:00
const theme = window . Events . getThemeSetting ( ) ;
2022-10-11 02:37:58 +02:00
// We don't want to reset the primary color on startup
await switchThemeTo ( {
theme ,
mainWindow : true ,
2022-10-11 02:58:18 +02:00
usePrimaryColor : true ,
2022-10-11 02:37:58 +02:00
dispatch : window?.inboxStore?.dispatch || undefined ,
} ) ;
2021-04-30 05:26:33 +02:00
} ;
// Do this only if we created a new Session ID, or if we already received the initial configuration message
2021-05-06 08:02:47 +02:00
const triggerSyncIfNeeded = async ( ) = > {
2023-02-21 07:09:08 +01:00
const us = UserUtils . getOurPubKeyStrFromCache ( ) ;
2022-03-28 07:22:20 +02:00
await getConversationController ( )
2023-02-21 07:09:08 +01:00
. get ( us )
2022-03-28 07:22:20 +02:00
. setDidApproveMe ( true , true ) ;
2022-08-08 01:50:48 +02:00
await getConversationController ( )
2023-02-21 07:09:08 +01:00
. get ( us )
2022-08-08 01:50:48 +02:00
. setIsApproved ( true , true ) ;
2021-04-30 05:26:33 +02:00
const didWeHandleAConfigurationMessageAlready =
2022-08-08 01:50:48 +02:00
( await Data . getItemById ( hasSyncedInitialConfigurationItem ) ) ? . value || false ;
2021-04-30 05:26:33 +02:00
if ( didWeHandleAConfigurationMessageAlready ) {
await syncConfigurationIfNeeded ( ) ;
}
} ;
2021-05-25 05:19:34 +02:00
const triggerAvatarReUploadIfNeeded = async ( ) = > {
2022-08-08 01:50:48 +02:00
const lastTimeStampAvatarUpload = ( await Data . getItemById ( lastAvatarUploadTimestamp ) ) ? . value || 0 ;
2021-05-25 05:19:34 +02:00
2021-05-27 03:04:26 +02:00
if ( Date . now ( ) - lastTimeStampAvatarUpload > DURATION . DAYS * 14 ) {
2021-05-25 05:19:34 +02:00
window . log . info ( 'Reuploading avatar...' ) ;
// reupload the avatar
2021-06-21 03:42:25 +02:00
await uploadOurAvatar ( ) ;
2021-05-25 05:19:34 +02:00
}
} ;
2021-04-30 05:19:46 +02:00
/ * *
* This function is called only once : on app startup with a logged in user
* /
2023-01-25 05:26:28 +01:00
const doAppStartUp = async ( ) = > {
2021-06-16 07:26:00 +02:00
void setupTheme ( ) ;
2021-04-30 05:26:33 +02:00
// this generates the key to encrypt attachments locally
2023-01-25 05:26:28 +01:00
await Data . generateAttachmentKeyIfEmpty ( ) ;
2021-04-30 05:26:33 +02:00
2022-08-12 06:48:20 +02:00
// trigger a sync message if needed for our other devices
void triggerSyncIfNeeded ( ) ;
2021-11-09 05:45:18 +01:00
void getSwarmPollingInstance ( ) . start ( ) ;
2021-04-30 05:19:46 +02:00
void loadDefaultRooms ( ) ;
2021-05-05 01:55:38 +02:00
2023-03-30 05:15:59 +02:00
// TODOLATER make this a job of the JobRunner
2021-05-25 05:19:34 +02:00
debounce ( triggerAvatarReUploadIfNeeded , 200 ) ;
2022-12-08 23:47:49 +01:00
2023-01-25 05:26:28 +01:00
/* Postpone a little bit of the polling of sogs messages to let the swarm messages come in first. */
global . setTimeout ( ( ) = > {
void getOpenGroupManager ( ) . startPolling ( ) ;
} , 10000 ) ;
2022-12-19 08:58:55 +01:00
2023-01-25 05:26:28 +01:00
global . setTimeout ( ( ) = > {
// init the messageQueue. In the constructor, we add all not send messages
// this call does nothing except calling the constructor, which will continue sending message in the pipeline
void getMessageQueue ( ) . processAllPending ( ) ;
2022-12-08 23:47:49 +01:00
} , 3000 ) ;
2021-04-30 05:19:46 +02:00
} ;
2022-03-15 02:13:33 +01:00
async function fetchReleaseFromFSAndUpdateMain() {
try {
2022-04-04 09:30:47 +02:00
window . log . info ( '[updater] about to fetchReleaseFromFSAndUpdateMain' ) ;
2022-03-21 06:29:07 +01:00
2022-08-08 01:50:48 +02:00
const latest = await getLatestReleaseFromFileServer ( ) ;
window . log . info ( '[updater] fetched latest release from fileserver: ' , latest ) ;
2022-03-15 02:13:33 +01:00
if ( isString ( latest ) && ! isEmpty ( latest ) ) {
ipcRenderer . send ( 'set-release-from-file-server' , latest ) ;
2022-03-21 06:29:07 +01:00
window . readyForUpdates ( ) ;
2022-03-15 02:13:33 +01:00
}
} catch ( e ) {
window . log . warn ( e ) ;
}
}
2021-03-02 23:29:36 +01:00
/ * *
* ActionsPanel is the far left banner ( not the left pane ) .
* The panel with buttons to switch between the message / contact / settings / theme views
* /
2021-03-16 07:45:36 +01:00
export const ActionsPanel = ( ) = > {
2021-04-13 06:10:31 +02:00
const [ startCleanUpMedia , setStartCleanUpMedia ] = useState ( false ) ;
2021-03-16 07:45:36 +01:00
const ourPrimaryConversation = useSelector ( getOurPrimaryConversation ) ;
2020-07-14 03:28:10 +02:00
2021-03-16 07:45:36 +01:00
// this maxi useEffect is called only once: when the component is mounted.
2021-04-13 06:10:31 +02:00
// For the action panel, it means this is called only one per app start/with a user loggedin
2021-03-16 07:45:36 +01:00
useEffect ( ( ) = > {
2021-12-08 07:44:24 +01:00
void doAppStartUp ( ) ;
2021-03-16 07:45:36 +01:00
} , [ ] ) ;
2021-02-05 06:29:37 +01:00
2021-04-13 06:10:31 +02:00
// wait for cleanUpMediasInterval and then start cleaning up medias
// this would be way easier to just be able to not trigger a call with the setInterval
useEffect ( ( ) = > {
2021-08-04 02:52:24 +02:00
const timeout = setTimeout ( ( ) = > setStartCleanUpMedia ( true ) , cleanUpMediasInterval ) ;
2021-04-13 06:10:31 +02:00
2021-08-04 02:52:24 +02:00
return ( ) = > clearTimeout ( timeout ) ;
2021-04-13 06:10:31 +02:00
} , [ ] ) ;
2021-11-23 06:18:27 +01:00
useInterval ( cleanUpOldDecryptedMedias , startCleanUpMedia ? cleanUpMediasInterval : null ) ;
2021-04-13 06:10:31 +02:00
2022-03-15 02:13:33 +01:00
useInterval ( ( ) = > {
void fetchReleaseFromFSAndUpdateMain ( ) ;
} , fetchReleaseFromFileServerInterval ) ;
2021-03-16 07:45:36 +01:00
if ( ! ourPrimaryConversation ) {
2021-05-19 07:54:31 +02:00
window ? . log ? . warn ( 'ActionsPanel: ourPrimaryConversation is not set' ) ;
2021-11-08 01:03:08 +01:00
return null ;
2021-02-05 06:29:37 +01:00
}
2021-03-16 07:45:36 +01:00
useInterval ( ( ) = > {
void syncConfigurationIfNeeded ( ) ;
2021-05-27 03:04:26 +02:00
} , DURATION . DAYS * 2 ) ;
2020-12-01 01:15:51 +01:00
2021-05-04 04:34:07 +02:00
useInterval ( ( ) = > {
2021-09-13 09:07:53 +02:00
// trigger an updates from the snodes every hour
2021-05-04 04:34:07 +02:00
void forceRefreshRandomSnodePool ( ) ;
2021-09-13 09:07:53 +02:00
} , DURATION . HOURS * 1 ) ;
useTimeoutFn ( ( ) = > {
// trigger an updates from the snodes after 5 minutes, once
void forceRefreshRandomSnodePool ( ) ;
} , DURATION . MINUTES * 5 ) ;
2021-05-04 04:34:07 +02:00
2021-05-25 05:19:34 +02:00
useInterval ( ( ) = > {
// this won't be run every days, but if the app stays open for more than 10 days
void triggerAvatarReUploadIfNeeded ( ) ;
2021-05-27 03:04:26 +02:00
} , DURATION . DAYS * 1 ) ;
2021-05-25 05:19:34 +02:00
2021-03-16 07:45:36 +01:00
return (
2021-06-09 02:06:23 +02:00
< >
2021-12-15 04:41:55 +01:00
< LeftPaneSectionContainer data - testid = "leftpane-section-container" >
2021-11-23 06:18:27 +01:00
< Section type = { SectionType . Profile } / >
2021-06-09 02:06:23 +02:00
< Section type = { SectionType . Message } / >
< Section type = { SectionType . Settings } / >
2021-06-18 08:41:35 +02:00
< Section type = { SectionType . PathIndicator } / >
2022-09-26 05:10:45 +02:00
< Section type = { SectionType . ColorMode } / >
2021-12-15 04:41:55 +01:00
< / LeftPaneSectionContainer >
2021-06-07 03:44:49 +02:00
< / >
2021-03-16 07:45:36 +01:00
) ;
} ;