Remove loading screen
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M116 64c0 28.719-23.281 52-52 52-9.11 0-17.672-2.342-25.117-6.457a3.3 3.3 0 0 0-2.351-.341L13.4 114.599l5.397-23.13a3.3 3.3 0 0 0-.34-2.352C14.341 81.671 12 73.109 12 64c0-28.719 23.281-52 52-52s52 23.281 52 52"/></svg>
|
||||
|
Before Width: | Height: | Size: 293 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m22.488 117.818 1.363 5.843-10.411 2.43C6.51 127.707.293 121.489 1.91 114.56l2.429-10.411 5.843 1.363-2.43 10.412c-.606 2.598 1.726 4.93 4.324 4.324z"/></svg>
|
||||
|
Before Width: | Height: | Size: 232 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m118.708 30.77-5.129 3.115a57.7 57.7 0 0 1 6.778 16.351l5.83-1.423a63.6 63.6 0 0 0-7.479-18.044Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 179 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m127.26 54.235-5.931.915A58.4 58.4 0 0 1 122 64c0 3.01-.229 5.965-.671 8.85l5.931.916c.487-3.184.74-6.446.74-9.766s-.253-6.581-.74-9.765"/></svg>
|
||||
|
Before Width: | Height: | Size: 219 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M113.579 94.116a57.7 57.7 0 0 0 6.778-16.352l5.83 1.424a63.6 63.6 0 0 1-7.48 18.043z"/></svg>
|
||||
|
Before Width: | Height: | Size: 167 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m110.792 98.278 4.841 3.546a64.4 64.4 0 0 1-13.81 13.809l-3.546-4.841a58.3 58.3 0 0 0 12.515-12.514"/></svg>
|
||||
|
Before Width: | Height: | Size: 182 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m94.115 113.579 3.116 5.129a63.6 63.6 0 0 1-18.044 7.479l-1.423-5.83a57.7 57.7 0 0 0 16.351-6.778"/></svg>
|
||||
|
Before Width: | Height: | Size: 180 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m72.85 121.329.915 5.931c-3.184.487-6.445.74-9.765.74s-6.582-.253-9.766-.74l.916-5.93c2.884.441 5.84.67 8.85.67a58.4 58.4 0 0 0 8.85-.671"/></svg>
|
||||
|
Before Width: | Height: | Size: 220 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m50.236 120.358-1.424 5.829a63.5 63.5 0 0 1-13.823-5.125l-6.074 1.418-1.363-5.843 8.208-1.916 1.953.995a57.5 57.5 0 0 0 12.523 4.642"/></svg>
|
||||
|
Before Width: | Height: | Size: 215 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M11.363 100.448 5.52 99.085l1.418-6.074a63.6 63.6 0 0 1-5.125-13.824l5.829-1.423a57.6 57.6 0 0 0 4.642 12.523l.994 1.953z"/></svg>
|
||||
|
Before Width: | Height: | Size: 204 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m6.67 72.85-5.93.915A64.5 64.5 0 0 1 0 64c0-3.32.253-6.582.74-9.766l5.931.916A58.5 58.5 0 0 0 6 64c0 3.01.229 5.966.67 8.85"/></svg>
|
||||
|
Before Width: | Height: | Size: 206 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m7.643 50.236-5.83-1.424a63.6 63.6 0 0 1 7.48-18.043l5.129 3.115a57.7 57.7 0 0 0-6.779 16.352"/></svg>
|
||||
|
Before Width: | Height: | Size: 176 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m17.208 29.723-4.84-3.546a64.4 64.4 0 0 1 13.809-13.81l3.546 4.84a58.3 58.3 0 0 0-12.515 12.515Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 179 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m33.885 14.421-3.116-5.129a63.6 63.6 0 0 1 18.044-7.48l1.423 5.83a57.7 57.7 0 0 0-16.351 6.78Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 177 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="M64 0c3.32 0 6.582.253 9.766.74l-.916 5.931A58.5 58.5 0 0 0 64 6c-3.009 0-5.964.23-8.85.67L54.235.74A64.5 64.5 0 0 1 64 0"/></svg>
|
||||
|
Before Width: | Height: | Size: 204 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m79.188 1.813-1.424 5.83a57.7 57.7 0 0 1 16.352 6.779l3.115-5.13a63.6 63.6 0 0 0-18.043-7.479"/></svg>
|
||||
|
Before Width: | Height: | Size: 176 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><path d="m101.823 12.367-3.545 4.84a58.3 58.3 0 0 1 12.514 12.516l4.841-3.546a64.4 64.4 0 0 0-13.81-13.81"/></svg>
|
||||
|
Before Width: | Height: | Size: 179 B |
@@ -42,67 +42,6 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.Inbox__logo {
|
||||
flex-shrink: 0;
|
||||
|
||||
display: block;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
margin-block: 24px;
|
||||
position: relative;
|
||||
|
||||
@include mixins.light-theme() {
|
||||
--Inbox__logo__bg: #{variables.$color-ultramarine-logo};
|
||||
}
|
||||
@include mixins.dark-theme() {
|
||||
--Inbox__logo__bg: #{variables.$color-white};
|
||||
}
|
||||
|
||||
.Inbox__logo__part {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.Inbox__logo__part--base {
|
||||
@include mixins.color-svg(
|
||||
'../images/logo-parts/base.svg',
|
||||
var(--Inbox__logo__bg)
|
||||
);
|
||||
& {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.Inbox__logo__part--segment {
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.Inbox__logo__part--animated {
|
||||
transition:
|
||||
opacity 250ms,
|
||||
transform 250ms;
|
||||
}
|
||||
|
||||
@for $i from 1 through 16 {
|
||||
.Inbox__logo__part--segment:nth-child(#{$i + 1}) {
|
||||
@include mixins.color-svg(
|
||||
'../images/logo-parts/p#{$i}.svg',
|
||||
var(--Inbox__logo__bg)
|
||||
);
|
||||
& {
|
||||
transform: rotate(#{(16 - $i) * 22.5}deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Inbox__logo__part--segment:nth-child(n).Inbox__logo__part--visible {
|
||||
transform: rotate(0deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.Inbox__welcome {
|
||||
margin-block: 20px 6px;
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import type { Meta, StoryFn } from '@storybook/react';
|
||||
import lodash from 'lodash';
|
||||
|
||||
import { Inbox } from './Inbox.dom.tsx';
|
||||
import type { PropsType } from './Inbox.dom.tsx';
|
||||
import { DAY, SECOND } from '../util/durations/index.std.ts';
|
||||
|
||||
const { noop } = lodash;
|
||||
|
||||
const { i18n } = window.SignalContext;
|
||||
|
||||
export default {
|
||||
title: 'Components/Inbox',
|
||||
args: {
|
||||
i18n,
|
||||
hasInitialLoadCompleted: false,
|
||||
isNightly: false,
|
||||
isCustomizingPreferredReactions: false,
|
||||
},
|
||||
argTypes: {
|
||||
daysAgo: { control: { type: 'number' } },
|
||||
isNightly: { control: { type: 'boolean' } },
|
||||
},
|
||||
} satisfies Meta<PropsType & { daysAgo?: number }>;
|
||||
|
||||
const Template: StoryFn<PropsType & { daysAgo?: number }> = ({
|
||||
daysAgo,
|
||||
...args
|
||||
}) => {
|
||||
const now = useMemo(() => Date.now(), []);
|
||||
const [dayOffset, setDayOffset] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!daysAgo) {
|
||||
setDayOffset(0);
|
||||
return noop;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
// Increment day offset by 1 / 24 of a day (an hour), and wrap it when it
|
||||
// reaches `daysAgo` value.
|
||||
setDayOffset(prevValue => (prevValue + 1 / 24) % daysAgo);
|
||||
}, SECOND / 10);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [now, daysAgo]);
|
||||
|
||||
const firstEnvelopeTimestamp =
|
||||
daysAgo === undefined ? undefined : now - daysAgo * DAY;
|
||||
const envelopeTimestamp =
|
||||
firstEnvelopeTimestamp === undefined
|
||||
? undefined
|
||||
: firstEnvelopeTimestamp + dayOffset * DAY;
|
||||
|
||||
return (
|
||||
<Inbox
|
||||
{...args}
|
||||
firstEnvelopeTimestamp={firstEnvelopeTimestamp}
|
||||
envelopeTimestamp={envelopeTimestamp}
|
||||
renderCustomizingPreferredReactionsModal={() => <div />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const FourDaysAgo = Template.bind({});
|
||||
FourDaysAgo.args = {
|
||||
daysAgo: 4,
|
||||
};
|
||||
@@ -2,22 +2,11 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { LocalizerType } from '../types/Util.std.ts';
|
||||
import { createLogger } from '../logging/log.std.ts';
|
||||
import { SECOND, DAY } from '../util/durations/index.std.ts';
|
||||
import React from 'react';
|
||||
import type { SmartNavTabsProps } from '../state/smart/NavTabs.preload.tsx';
|
||||
import { TitlebarDragArea } from './TitlebarDragArea.dom.tsx';
|
||||
|
||||
const log = createLogger('Inbox');
|
||||
|
||||
export type PropsType = {
|
||||
firstEnvelopeTimestamp: number | undefined;
|
||||
envelopeTimestamp: number | undefined;
|
||||
hasInitialLoadCompleted: boolean;
|
||||
i18n: LocalizerType;
|
||||
isNightly: boolean;
|
||||
isCustomizingPreferredReactions: boolean;
|
||||
navTabsCollapsed: boolean;
|
||||
onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => unknown;
|
||||
@@ -29,14 +18,7 @@ export type PropsType = {
|
||||
renderSettingsTab: () => React.JSX.Element;
|
||||
};
|
||||
|
||||
const PART_COUNT = 16;
|
||||
|
||||
export function Inbox({
|
||||
firstEnvelopeTimestamp,
|
||||
envelopeTimestamp,
|
||||
hasInitialLoadCompleted,
|
||||
i18n,
|
||||
isNightly,
|
||||
isCustomizingPreferredReactions,
|
||||
navTabsCollapsed,
|
||||
onToggleNavTabsCollapse,
|
||||
@@ -47,149 +29,6 @@ export function Inbox({
|
||||
renderStoriesTab,
|
||||
renderSettingsTab,
|
||||
}: PropsType): React.JSX.Element {
|
||||
const [internalHasInitialLoadCompleted, setInternalHasInitialLoadCompleted] =
|
||||
useState(hasInitialLoadCompleted);
|
||||
|
||||
const now = useMemo(() => Date.now(), []);
|
||||
const midnight = useMemo(() => {
|
||||
const date = new Date(now);
|
||||
date.setHours(0);
|
||||
date.setMinutes(0);
|
||||
date.setSeconds(0);
|
||||
date.setMilliseconds(0);
|
||||
return date.getTime();
|
||||
}, [now]);
|
||||
|
||||
useEffect(() => {
|
||||
if (internalHasInitialLoadCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const { status } = window.getSocketStatus().authenticated;
|
||||
switch (status) {
|
||||
case 'CONNECTING':
|
||||
break;
|
||||
case 'OPEN':
|
||||
// if we've connected, we can wait for real empty event
|
||||
clearInterval(interval);
|
||||
break;
|
||||
case 'CLOSING':
|
||||
case 'CLOSED':
|
||||
clearInterval(interval);
|
||||
// if we failed to connect, we pretend we loaded
|
||||
setInternalHasInitialLoadCompleted(true);
|
||||
break;
|
||||
default:
|
||||
log.warn(
|
||||
`startConnectionListener: Found unexpected socket status ${status}; setting load to done manually.`
|
||||
);
|
||||
setInternalHasInitialLoadCompleted(true);
|
||||
break;
|
||||
}
|
||||
}, SECOND);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [internalHasInitialLoadCompleted]);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalHasInitialLoadCompleted(hasInitialLoadCompleted);
|
||||
}, [hasInitialLoadCompleted]);
|
||||
|
||||
if (!internalHasInitialLoadCompleted) {
|
||||
let loadingProgress = 100;
|
||||
if (
|
||||
firstEnvelopeTimestamp !== undefined &&
|
||||
envelopeTimestamp !== undefined
|
||||
) {
|
||||
loadingProgress =
|
||||
Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
1,
|
||||
Math.max(0, envelopeTimestamp - firstEnvelopeTimestamp) /
|
||||
Math.max(1e-23, now - firstEnvelopeTimestamp)
|
||||
)
|
||||
) * 100;
|
||||
}
|
||||
|
||||
let message: string | undefined;
|
||||
if (envelopeTimestamp !== undefined) {
|
||||
const daysBeforeMidnight = Math.ceil(
|
||||
(midnight - envelopeTimestamp) / DAY
|
||||
);
|
||||
|
||||
if (daysBeforeMidnight <= 0) {
|
||||
message = i18n('icu:loadingMessages--today');
|
||||
} else if (daysBeforeMidnight === 1) {
|
||||
message = i18n('icu:loadingMessages--yesterday');
|
||||
} else {
|
||||
message = i18n('icu:loadingMessages--other', {
|
||||
daysAgo: daysBeforeMidnight,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let logo: React.JSX.Element;
|
||||
if (isNightly) {
|
||||
const parts = new Array<React.JSX.Element>();
|
||||
parts.push(
|
||||
<i key="base" className="Inbox__logo__part Inbox__logo__part--base" />
|
||||
);
|
||||
for (let i = 0; i < PART_COUNT; i += 1) {
|
||||
const isVisible = i <= (loadingProgress * PART_COUNT) / 100;
|
||||
parts.push(
|
||||
<i
|
||||
key={i}
|
||||
className={classNames({
|
||||
Inbox__logo__part: true,
|
||||
'Inbox__logo__part--animated':
|
||||
firstEnvelopeTimestamp !== undefined && loadingProgress !== 0,
|
||||
'Inbox__logo__part--segment': true,
|
||||
'Inbox__logo__part--visible': isVisible,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
logo = <div className="Inbox__logo">{parts}</div>;
|
||||
} else {
|
||||
logo = (
|
||||
<div className="module-splash-screen__logo module-splash-screen__logo--128" />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app-loading-screen">
|
||||
<TitlebarDragArea />
|
||||
|
||||
{logo}
|
||||
|
||||
{envelopeTimestamp === undefined ? (
|
||||
<div className="dot-container">
|
||||
<span className="dot" />
|
||||
<span className="dot" />
|
||||
<span className="dot" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="app-loading-screen__progress--container">
|
||||
<div
|
||||
className="app-loading-screen__progress--bar"
|
||||
style={{ transform: `translateX(${loadingProgress - 100}%)` }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{message === undefined ? (
|
||||
<div className="message-placeholder" />
|
||||
) : (
|
||||
<div className="message">{message}</div>
|
||||
)}
|
||||
<div id="toast" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let activeModal: ReactNode;
|
||||
if (isCustomizingPreferredReactions) {
|
||||
activeModal = renderCustomizingPreferredReactionsModal();
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { createSelector } from 'reselect';
|
||||
import type { StateType } from '../reducer.preload.ts';
|
||||
|
||||
const getInboxState = (state: StateType) => state.inbox;
|
||||
|
||||
export const getInboxEnvelopeTimestamp = createSelector(
|
||||
getInboxState,
|
||||
({ envelopeTimestamp }) => envelopeTimestamp
|
||||
);
|
||||
|
||||
export const getInboxFirstEnvelopeTimestamp = createSelector(
|
||||
getInboxState,
|
||||
({ firstEnvelopeTimestamp }) => firstEnvelopeTimestamp
|
||||
);
|
||||
@@ -4,8 +4,6 @@
|
||||
import React, { memo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Inbox } from '../../components/Inbox.dom.tsx';
|
||||
import { isNightly } from '../../util/version.std.ts';
|
||||
import { getIntl } from '../selectors/user.std.ts';
|
||||
import { SmartCustomizingPreferredReactionsModal } from './CustomizingPreferredReactionsModal.preload.tsx';
|
||||
import { getIsCustomizingPreferredReactions } from '../selectors/preferredReactions.std.ts';
|
||||
import type { SmartNavTabsProps } from './NavTabs.preload.tsx';
|
||||
@@ -15,11 +13,6 @@ import { SmartCallsTab } from './CallsTab.preload.tsx';
|
||||
import { useItemsActions } from '../ducks/items.preload.ts';
|
||||
import { getNavTabsCollapsed } from '../selectors/items.dom.ts';
|
||||
import { SmartChatsTab } from './ChatsTab.preload.tsx';
|
||||
import { getHasInitialLoadCompleted } from '../selectors/app.std.ts';
|
||||
import {
|
||||
getInboxEnvelopeTimestamp,
|
||||
getInboxFirstEnvelopeTimestamp,
|
||||
} from '../selectors/inbox.std.ts';
|
||||
import { SmartPreferences } from './Preferences.preload.tsx';
|
||||
|
||||
function renderChatsTab() {
|
||||
@@ -47,24 +40,15 @@ function renderSettingsTab() {
|
||||
}
|
||||
|
||||
export const SmartInbox = memo(function SmartInbox(): React.JSX.Element {
|
||||
const i18n = useSelector(getIntl);
|
||||
const isCustomizingPreferredReactions = useSelector(
|
||||
getIsCustomizingPreferredReactions
|
||||
);
|
||||
const envelopeTimestamp = useSelector(getInboxEnvelopeTimestamp);
|
||||
const firstEnvelopeTimestamp = useSelector(getInboxFirstEnvelopeTimestamp);
|
||||
const hasInitialLoadCompleted = useSelector(getHasInitialLoadCompleted);
|
||||
const navTabsCollapsed = useSelector(getNavTabsCollapsed);
|
||||
|
||||
const { toggleNavTabsCollapse } = useItemsActions();
|
||||
|
||||
return (
|
||||
<Inbox
|
||||
envelopeTimestamp={envelopeTimestamp}
|
||||
firstEnvelopeTimestamp={firstEnvelopeTimestamp}
|
||||
hasInitialLoadCompleted={hasInitialLoadCompleted}
|
||||
i18n={i18n}
|
||||
isNightly={isNightly(window.getVersion())}
|
||||
isCustomizingPreferredReactions={isCustomizingPreferredReactions}
|
||||
navTabsCollapsed={navTabsCollapsed}
|
||||
onToggleNavTabsCollapse={toggleNavTabsCollapse}
|
||||
|
||||