All checks were successful
Trigger cinny-mobile / dispatch (push) Successful in 2s
Use stripBase for update static copy paths, show Settings detail on mobile when nav is hidden, pause Settings focus trap during release notes, and guard extractMemberColorPreference when room is not a Matrix Room instance.
382 lines
14 KiB
TypeScript
382 lines
14 KiB
TypeScript
import React, { useCallback, useEffect, useState } from 'react';
|
|
import { getAppVersion } from '../../../utils/appVersion';
|
|
import { useOpenReleaseNotesDialog } from '../../../components/updates-dialog/UpdatesDialogHost';
|
|
import { getCurrentUpdatePreview } from '../../../data/updateNotes';
|
|
import { Box, Text, IconButton, Scroll, Button, config, toRem } from 'folds';
|
|
import { Icon, Icons } from '../../../components/icons';
|
|
import { Page, PageContent, PageHeader } from '../../../components/page';
|
|
import { SequenceCard } from '../../../components/sequence-card';
|
|
import { SequenceCardStyle } from '../styles.css';
|
|
import { SettingTile } from '../../../components/setting-tile';
|
|
import PaarrotSVG from '../../../../../public/res/svg/paarrot.svg';
|
|
import { clearCacheAndReload } from '../../../../client/initMatrix';
|
|
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
|
|
|
type AboutProps = {
|
|
requestClose: () => void;
|
|
};
|
|
export function About({ requestClose }: AboutProps) {
|
|
const mx = useMatrixClient();
|
|
const openReleaseNotes = useOpenReleaseNotesDialog();
|
|
const [version, setVersion] = useState<string>('');
|
|
const [protocolStatus, setProtocolStatus] = useState<string>('Checking desktop protocol integration...');
|
|
const [protocolBusy, setProtocolBusy] = useState<boolean>(false);
|
|
const [updatePreview, setUpdatePreview] = useState<{ title: string; description: string } | null>(
|
|
null
|
|
);
|
|
|
|
const formatProtocolStatus = useCallback((data: {
|
|
scheme: string;
|
|
platform: string;
|
|
isDefault: boolean;
|
|
registerCall: boolean;
|
|
error: string | null;
|
|
windows: null | {
|
|
userChoiceProgId: string | null;
|
|
userChoiceCommand: string | null;
|
|
hkcuCommand: string | null;
|
|
hklmCommand: string | null;
|
|
};
|
|
}): string => {
|
|
const state = data.isDefault ? 'registered' : 'not registered';
|
|
const startupState = data.registerCall ? 'ok' : 'no-change';
|
|
const owner =
|
|
data.platform === 'win32' && data.windows?.userChoiceProgId
|
|
? ` Current default ProgId=${data.windows.userChoiceProgId}.`
|
|
: '';
|
|
const missingHint =
|
|
data.platform === 'win32' && !data.windows?.hkcuCommand && !data.windows?.hklmCommand
|
|
? ` ${data.scheme} link type is missing from Windows registry. Use Repair to create it.`
|
|
: '';
|
|
const errorPart = data.error ? ` Error: ${data.error}` : '';
|
|
|
|
return `${data.scheme} is ${state} (startup=${startupState}) on ${data.platform}.${owner}${missingHint}${errorPart}`;
|
|
}, []);
|
|
|
|
const refreshProtocolStatus = useCallback(() => {
|
|
if (!window.electron?.protocol?.getStatus) {
|
|
setProtocolStatus('Desktop protocol diagnostics are unavailable in this environment.');
|
|
return;
|
|
}
|
|
|
|
window.electron.protocol
|
|
.getStatus()
|
|
.then((result) => {
|
|
if (!result?.success || !result.data) {
|
|
setProtocolStatus(result?.error ?? 'Unable to query protocol state.');
|
|
return;
|
|
}
|
|
|
|
setProtocolStatus(formatProtocolStatus(result.data));
|
|
})
|
|
.catch((error) => {
|
|
setProtocolStatus(`Protocol diagnostics failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
});
|
|
}, [formatProtocolStatus]);
|
|
|
|
const repairProtocolHandler = () => {
|
|
if (!window.electron?.protocol?.repair) {
|
|
setProtocolStatus('Protocol repair is unavailable in this environment.');
|
|
return;
|
|
}
|
|
|
|
setProtocolBusy(true);
|
|
window.electron.protocol
|
|
.repair()
|
|
.then((result) => {
|
|
if (!result?.success || !result.data) {
|
|
setProtocolStatus(result?.error ?? 'Protocol repair failed.');
|
|
return;
|
|
}
|
|
|
|
setProtocolStatus(formatProtocolStatus(result.data));
|
|
setProtocolBusy(false);
|
|
})
|
|
.catch((error) => {
|
|
setProtocolStatus(`Protocol repair failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
setProtocolBusy(false);
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
getAppVersion().then(setVersion).catch(() => setVersion('unknown'));
|
|
refreshProtocolStatus();
|
|
getCurrentUpdatePreview().then(setUpdatePreview).catch(() => setUpdatePreview(null));
|
|
}, [refreshProtocolStatus]);
|
|
|
|
return (
|
|
<Page>
|
|
<PageHeader outlined={false}>
|
|
<Box grow="Yes" gap="200">
|
|
<Box grow="Yes" alignItems="Center" gap="200">
|
|
<Text size="H3" truncate>
|
|
About
|
|
</Text>
|
|
</Box>
|
|
<Box shrink="No">
|
|
<IconButton onClick={requestClose} variant="Surface">
|
|
<Icon src={Icons.Cross} />
|
|
</IconButton>
|
|
</Box>
|
|
</Box>
|
|
</PageHeader>
|
|
<Box grow="Yes">
|
|
<Scroll hideTrack visibility="Hover">
|
|
<PageContent>
|
|
<Box direction="Column" gap="700">
|
|
<Box gap="400">
|
|
<Box shrink="No">
|
|
<img
|
|
style={{ width: toRem(60), height: toRem(60) }}
|
|
src={PaarrotSVG}
|
|
alt="Paarrot logo"
|
|
/>
|
|
</Box>
|
|
<Box direction="Column" gap="300">
|
|
<Box direction="Column" gap="100">
|
|
<Box gap="100" alignItems="End">
|
|
<Text size="H3">Paarrot</Text>
|
|
<Text size="T200">v{version}</Text>
|
|
</Box>
|
|
<Text>Squawk to yer mateys!</Text>
|
|
</Box>
|
|
|
|
<Box gap="200" wrap="Wrap">
|
|
<Button
|
|
as="a"
|
|
href="https://github.com/Paarrot/Paarrot-Desktop"
|
|
rel="noreferrer noopener"
|
|
target="_blank"
|
|
variant="Secondary"
|
|
fill="Soft"
|
|
size="300"
|
|
radii="300"
|
|
before={<Icon src={Icons.Code} size="100" filled />}
|
|
>
|
|
<Text size="B300">Source Code</Text>
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
<Box direction="Column" gap="100">
|
|
<Text size="L400">What's new</Text>
|
|
<SequenceCard
|
|
className={SequenceCardStyle}
|
|
variant="SurfaceVariant"
|
|
direction="Column"
|
|
gap="300"
|
|
>
|
|
<SettingTile
|
|
title={updatePreview?.title ?? 'Release notes'}
|
|
description={
|
|
updatePreview
|
|
? `Paarrot ${version} — ${updatePreview.description}`
|
|
: 'View highlights from the latest Paarrot update.'
|
|
}
|
|
after={
|
|
<Button
|
|
onClick={openReleaseNotes}
|
|
variant="Secondary"
|
|
fill="Soft"
|
|
size="300"
|
|
radii="300"
|
|
outlined
|
|
>
|
|
<Text size="B300">View updates</Text>
|
|
</Button>
|
|
}
|
|
/>
|
|
</SequenceCard>
|
|
</Box>
|
|
<Box direction="Column" gap="100">
|
|
<Text size="L400">Options</Text>
|
|
<SequenceCard
|
|
className={SequenceCardStyle}
|
|
variant="SurfaceVariant"
|
|
direction="Column"
|
|
gap="400"
|
|
>
|
|
<SettingTile
|
|
title="Clear Cache & Reload"
|
|
description="Clear all your locally stored data and reload from server."
|
|
after={
|
|
<Button
|
|
onClick={() => clearCacheAndReload(mx)}
|
|
variant="Secondary"
|
|
fill="Soft"
|
|
size="300"
|
|
radii="300"
|
|
outlined
|
|
>
|
|
<Text size="B300">Clear Cache</Text>
|
|
</Button>
|
|
}
|
|
/>
|
|
<SettingTile
|
|
title="Protocol Handler (paarrot://)"
|
|
description={protocolStatus}
|
|
after={
|
|
<Box gap="100">
|
|
<Button
|
|
onClick={repairProtocolHandler}
|
|
variant="Secondary"
|
|
fill="Soft"
|
|
size="300"
|
|
radii="300"
|
|
outlined
|
|
disabled={protocolBusy}
|
|
>
|
|
<Text size="B300">Repair</Text>
|
|
</Button>
|
|
<Button
|
|
onClick={refreshProtocolStatus}
|
|
variant="Secondary"
|
|
fill="Soft"
|
|
size="300"
|
|
radii="300"
|
|
outlined
|
|
disabled={protocolBusy}
|
|
>
|
|
<Text size="B300">Refresh</Text>
|
|
</Button>
|
|
</Box>
|
|
}
|
|
/>
|
|
</SequenceCard>
|
|
</Box>
|
|
<Box direction="Column" gap="100">
|
|
<Text size="L400">Credits</Text>
|
|
<SequenceCard
|
|
className={SequenceCardStyle}
|
|
variant="SurfaceVariant"
|
|
direction="Column"
|
|
gap="400"
|
|
>
|
|
<Box
|
|
as="ul"
|
|
direction="Column"
|
|
gap="200"
|
|
style={{
|
|
margin: 0,
|
|
paddingLeft: config.space.S400,
|
|
}}
|
|
>
|
|
<li>
|
|
<Text size="T300">
|
|
The{' '}
|
|
<a
|
|
href="https://github.com/matrix-org/matrix-js-sdk"
|
|
rel="noreferrer noopener"
|
|
target="_blank"
|
|
>
|
|
matrix-js-sdk
|
|
</a>{' '}
|
|
is ©{' '}
|
|
<a
|
|
href="https://matrix.org/foundation"
|
|
rel="noreferrer noopener"
|
|
target="_blank"
|
|
>
|
|
The Matrix.org Foundation C.I.C
|
|
</a>{' '}
|
|
used under the terms of{' '}
|
|
<a
|
|
href="http://www.apache.org/licenses/LICENSE-2.0"
|
|
rel="noreferrer noopener"
|
|
target="_blank"
|
|
>
|
|
Apache 2.0
|
|
</a>
|
|
.
|
|
</Text>
|
|
</li>
|
|
<li>
|
|
<Text size="T300">
|
|
The{' '}
|
|
<a
|
|
href="https://github.com/mozilla/twemoji-colr"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
twemoji-colr
|
|
</a>{' '}
|
|
font is ©{' '}
|
|
<a href="https://mozilla.org/" target="_blank" rel="noreferrer noopener">
|
|
Mozilla Foundation
|
|
</a>{' '}
|
|
used under the terms of{' '}
|
|
<a
|
|
href="http://www.apache.org/licenses/LICENSE-2.0"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
Apache 2.0
|
|
</a>
|
|
.
|
|
</Text>
|
|
</li>
|
|
<li>
|
|
<Text size="T300">
|
|
The{' '}
|
|
<a
|
|
href="https://twemoji.twitter.com"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
Twemoji
|
|
</a>{' '}
|
|
emoji art is ©{' '}
|
|
<a
|
|
href="https://twemoji.twitter.com"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
Twitter, Inc and other contributors
|
|
</a>{' '}
|
|
used under the terms of{' '}
|
|
<a
|
|
href="https://creativecommons.org/licenses/by/4.0/"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
CC-BY 4.0
|
|
</a>
|
|
.
|
|
</Text>
|
|
</li>
|
|
<li>
|
|
<Text size="T300">
|
|
The{' '}
|
|
<a
|
|
href="https://material.io/design/sound/sound-resources.html"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
Material sound resources
|
|
</a>{' '}
|
|
are ©{' '}
|
|
<a href="https://google.com" target="_blank" rel="noreferrer noopener">
|
|
Google
|
|
</a>{' '}
|
|
used under the terms of{' '}
|
|
<a
|
|
href="https://creativecommons.org/licenses/by/4.0/"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
>
|
|
CC-BY 4.0
|
|
</a>
|
|
.
|
|
</Text>
|
|
</li>
|
|
</Box>
|
|
</SequenceCard>
|
|
</Box>
|
|
</Box>
|
|
</PageContent>
|
|
</Scroll>
|
|
</Box>
|
|
</Page>
|
|
);
|
|
}
|