diff --git a/keyserver/addons/rust-node-addon/build.rs b/keyserver/addons/rust-node-addon/build.rs index 6c6dfd52a..2d381c6f4 100644 --- a/keyserver/addons/rust-node-addon/build.rs +++ b/keyserver/addons/rust-node-addon/build.rs @@ -1,49 +1,49 @@ extern crate napi_build; use regex::Regex; use std::env; use std::fs; use std::path::Path; fn main() { napi_build::setup(); - const VERSIONS_JS_PATH: &str = "../../../lib/facts/version.js"; + const VERSIONS_JS_PATH: &str = "../../src/version.js"; println!("cargo:rerun-if-changed={}", VERSIONS_JS_PATH); let js_path = Path::new(VERSIONS_JS_PATH); let content = fs::read_to_string(js_path).expect("Failed to read version.js"); let version_line = content .lines() - .find(|line| line.contains("webAndKeyserverCodeVersion")) - .expect("Failed to find webAndKeyserverCodeVersion line"); + .find(|line| line.contains("keyserverCodeVersion")) + .expect("Failed to find keyserverCodeVersion line"); // Find a sequence in the input string that starts with - // 'webAndKeyserverCodeVersion', followed by any number of whitespace + // 'keyserverCodeVersion', followed by any number of whitespace // characters, an equals sign, any number of additional whitespace characters, // a series of one or more digits (and capture these digits), and finally a // semicolon. - let re = Regex::new(r"webAndKeyserverCodeVersion\s*=\s*(\d+);").unwrap(); + let re = Regex::new(r"keyserverCodeVersion\s*=\s*(\d+);").unwrap(); let version: u64 = re .captures(version_line) .and_then(|cap| cap.get(1)) .map_or_else( || panic!("Failed to capture version number"), |m| { m.as_str() .parse::() .expect("Failed to parse version number") }, ); let out_dir = env::var("OUT_DIR").unwrap(); let rust_path = Path::new(&out_dir).join("version.rs"); fs::write( rust_path, format!("pub const CODE_VERSION: u64 = {};", version), ) .expect("Failed to write version.rs"); } diff --git a/keyserver/src/responders/version-responders.js b/keyserver/src/responders/version-responders.js index e6e9554b6..852a3f14b 100644 --- a/keyserver/src/responders/version-responders.js +++ b/keyserver/src/responders/version-responders.js @@ -1,18 +1,19 @@ // @flow import t, { type TInterface } from 'tcomb'; -import { webAndKeyserverCodeVersion } from 'lib/facts/version.js'; import type { VersionResponse } from 'lib/types/device-types.js'; import { tShape } from 'lib/utils/validation-utils.js'; +import { keyserverCodeVersion } from '../version.js'; + export const versionResponseValidator: TInterface = tShape({ codeVersion: t.Number }); -const versionResponse = { codeVersion: webAndKeyserverCodeVersion }; +const versionResponse = { codeVersion: keyserverCodeVersion }; async function versionResponder(): Promise { return versionResponse; } export { versionResponder }; diff --git a/keyserver/src/version.js b/keyserver/src/version.js new file mode 100644 index 000000000..f691fc38f --- /dev/null +++ b/keyserver/src/version.js @@ -0,0 +1,3 @@ +// @flow + +export const keyserverCodeVersion = 38; diff --git a/lib/facts/version.js b/lib/facts/version.js deleted file mode 100644 index d1f1f7b0c..000000000 --- a/lib/facts/version.js +++ /dev/null @@ -1,5 +0,0 @@ -// @flow - -// This file defines the version of both web and keyserver. Today they are -// deployed together, so their version are sourced from the same place. -export const webAndKeyserverCodeVersion = 38; diff --git a/web/app.react.js b/web/app.react.js index 0f3e1a9d8..609b8f5bb 100644 --- a/web/app.react.js +++ b/web/app.react.js @@ -1,391 +1,390 @@ // @flow import 'basscss/css/basscss.min.css'; import './theme.css'; import { config as faConfig } from '@fortawesome/fontawesome-svg-core'; import classnames from 'classnames'; import _isEqual from 'lodash/fp/isEqual.js'; import * as React from 'react'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { useDispatch } from 'react-redux'; import { WagmiConfig } from 'wagmi'; import { fetchEntriesActionTypes, updateCalendarQueryActionTypes, } from 'lib/actions/entry-actions.js'; import { ChatMentionContextProvider } from 'lib/components/chat-mention-provider.react.js'; import { EditUserAvatarProvider } from 'lib/components/edit-user-avatar-provider.react.js'; import { ModalProvider, useModalContext, } from 'lib/components/modal-provider.react.js'; -import { webAndKeyserverCodeVersion } from 'lib/facts/version.js'; import { createLoadingStatusSelector, combineLoadingStatuses, } from 'lib/selectors/loading-selectors.js'; import { isLoggedIn } from 'lib/selectors/user-selectors.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; import type { Dispatch } from 'lib/types/redux-types.js'; import { registerConfig } from 'lib/utils/config.js'; import { infoFromURL } from 'lib/utils/url-utils.js'; import { AlchemyENSCacheProvider, wagmiConfig } from 'lib/utils/wagmi-utils.js'; import QrCodeLogin from './account/qr-code-login.react.js'; import AppThemeWrapper from './app-theme-wrapper.react.js'; import WebEditThreadAvatarProvider from './avatars/web-edit-thread-avatar-provider.react.js'; import Calendar from './calendar/calendar.react.js'; import Chat from './chat/chat.react.js'; import { EditModalProvider } from './chat/edit-message-provider.js'; import { TooltipProvider } from './chat/tooltip-provider.js'; import NavigationArrows from './components/navigation-arrows.react.js'; import { initOpaque } from './crypto/opaque-utils.js'; import electron from './electron.js'; import InputStateContainer from './input/input-state-container.react.js'; import InviteLinkHandler from './invite-links/invite-link-handler.react.js'; import InviteLinksRefresher from './invite-links/invite-links-refresher.react.js'; import LoadingIndicator from './loading-indicator.react.js'; import { MenuProvider } from './menu-provider.react.js'; import UpdateModalHandler from './modals/update-modal.react.js'; import SettingsSwitcher from './navigation-panels/settings-switcher.react.js'; import Topbar from './navigation-panels/topbar.react.js'; import useBadgeHandler from './push-notif/badge-handler.react.js'; import { PushNotificationsHandler } from './push-notif/push-notifs-handler.js'; import { updateNavInfoActionType } from './redux/action-types.js'; import DisconnectedBarVisibilityHandler from './redux/disconnected-bar-visibility-handler.js'; import DisconnectedBar from './redux/disconnected-bar.js'; import FocusHandler from './redux/focus-handler.react.js'; import { persistConfig } from './redux/persist.js'; import PolicyAcknowledgmentHandler from './redux/policy-acknowledgment-handler.js'; import { useSelector } from './redux/redux-utils.js'; import VisibilityHandler from './redux/visibility-handler.react.js'; import history from './router-history.js'; import { MessageSearchStateProvider } from './search/message-search-state-provider.react.js'; import AccountSettings from './settings/account-settings.react.js'; import DangerZone from './settings/danger-zone.react.js'; import CommunityPicker from './sidebar/community-picker.react.js'; import Splash from './splash/splash.react.js'; import './typography.css'; import css from './style.css'; import { type NavInfo } from './types/nav-types.js'; import { canonicalURLFromReduxState, navInfoFromURL } from './url-utils.js'; initOpaque(); // We want Webpack's css-loader and style-loader to handle the Fontawesome CSS, // so we disable the autoAddCss logic and import the CSS file. Otherwise every // icon flashes huge for a second before the CSS is loaded. import '@fortawesome/fontawesome-svg-core/styles.css'; faConfig.autoAddCss = false; registerConfig({ // We can't securely cache credentials on web, so we have no way to recover // from a cookie invalidation resolveInvalidatedCookie: null, setSessionIDOnRequest: true, // Never reset the calendar range calendarRangeInactivityLimit: null, platformDetails: { platform: electron?.platform ?? 'web', - codeVersion: webAndKeyserverCodeVersion, + codeVersion: 38, stateVersion: persistConfig.version, }, }); type BaseProps = { +location: { +pathname: string, ... }, }; type Props = { ...BaseProps, // Redux state +navInfo: NavInfo, +entriesLoadingStatus: LoadingStatus, +loggedIn: boolean, +activeThreadCurrentlyUnread: boolean, // Redux dispatch functions +dispatch: Dispatch, +modals: $ReadOnlyArray, }; class App extends React.PureComponent { componentDidMount() { const { navInfo, location: { pathname }, loggedIn, } = this.props; const newURL = canonicalURLFromReduxState(navInfo, pathname, loggedIn); if (pathname !== newURL) { history.replace(newURL); } } componentDidUpdate(prevProps: Props) { const { navInfo, location: { pathname }, loggedIn, } = this.props; if (!_isEqual(navInfo)(prevProps.navInfo)) { const newURL = canonicalURLFromReduxState(navInfo, pathname, loggedIn); if (newURL !== pathname) { history.push(newURL); } } else if (pathname !== prevProps.location.pathname) { const urlInfo = infoFromURL(pathname); const newNavInfo = navInfoFromURL(urlInfo, { navInfo }); if (!_isEqual(newNavInfo)(navInfo)) { this.props.dispatch({ type: updateNavInfoActionType, payload: newNavInfo, }); } } else if (loggedIn !== prevProps.loggedIn) { const newURL = canonicalURLFromReduxState(navInfo, pathname, loggedIn); if (newURL !== pathname) { history.replace(newURL); } } if (loggedIn !== prevProps.loggedIn) { electron?.clearHistory(); } } onWordmarkClicked = () => { this.props.dispatch({ type: updateNavInfoActionType, payload: { tab: 'chat' }, }); }; render() { let content; if (this.props.loggedIn) { content = ( <> {this.renderMainContent()} {this.props.modals} ); } else { content = ( <> {this.renderLoginPage()} {this.props.modals} ); } return ( {content} ); } onHeaderDoubleClick = () => electron?.doubleClickTopBar(); stopDoubleClickPropagation = electron ? e => e.stopPropagation() : null; renderLoginPage() { const { loginMethod } = this.props.navInfo; if (loginMethod === 'qr-code') { return ; } return ; } renderMainContent() { const mainContent = this.getMainContentWithSwitcher(); let navigationArrows = null; if (electron) { navigationArrows = ; } const headerClasses = classnames({ [css.header]: true, [css['electron-draggable']]: electron, }); const wordmarkClasses = classnames({ [css.wordmark]: true, [css['electron-non-draggable']]: electron, [css['wordmark-macos']]: electron?.platform === 'macos', }); return (

Comm

{navigationArrows}
{mainContent}
); } getMainContentWithSwitcher() { const { tab, settingsSection } = this.props.navInfo; let mainContent; if (tab === 'settings') { if (settingsSection === 'account') { mainContent = ; } else if (settingsSection === 'danger-zone') { mainContent = ; } return (
{mainContent}
); } if (tab === 'calendar') { mainContent = ; } else if (tab === 'chat') { mainContent = ; } const mainContentClass = classnames( css['main-content-container'], css['main-content-container-column'], ); return (
{mainContent}
); } } const fetchEntriesLoadingStatusSelector = createLoadingStatusSelector( fetchEntriesActionTypes, ); const updateCalendarQueryLoadingStatusSelector = createLoadingStatusSelector( updateCalendarQueryActionTypes, ); const ConnectedApp: React.ComponentType = React.memo( function ConnectedApp(props) { const activeChatThreadID = useSelector( state => state.navInfo.activeChatThreadID, ); const navInfo = useSelector(state => state.navInfo); const fetchEntriesLoadingStatus = useSelector( fetchEntriesLoadingStatusSelector, ); const updateCalendarQueryLoadingStatus = useSelector( updateCalendarQueryLoadingStatusSelector, ); const entriesLoadingStatus = combineLoadingStatuses( fetchEntriesLoadingStatus, updateCalendarQueryLoadingStatus, ); const loggedIn = useSelector(isLoggedIn); const activeThreadCurrentlyUnread = useSelector( state => !activeChatThreadID || !!state.threadStore.threadInfos[activeChatThreadID]?.currentUser.unread, ); useBadgeHandler(); const dispatch = useDispatch(); const modalContext = useModalContext(); const modals = React.useMemo( () => modalContext.modals.map(([modal, key]) => ( {modal} )), [modalContext.modals], ); return ( ); }, ); function AppWithProvider(props: BaseProps): React.Node { return ( ); } export default AppWithProvider;