文件
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

116 行
3.7 KiB
TypeScript

import * as React from 'react';
import { StyledSyncReportText, StyledSyncReport, StyledRoot } from './styles';
import Synchronizer, { type ProgressReport } from '@joplin/lib/Synchronizer';
import Setting from '@joplin/lib/models/Setting';
import { _ } from '@joplin/lib/locale';
import { AppState } from '../../app.reducer';
import { StateDecryptionWorker, StateResourceFetcher } from '@joplin/lib/reducer';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { themeStyle } from '@joplin/lib/theme';
import FolderAndTagList from './FolderAndTagList';
import NavService from '@joplin/lib/services/NavService';
// 菜单栏隐藏后,侧边栏左下角的按钮是进入「选项」设置的入口。
const openOptions = () => {
void NavService.go('Config');
};
interface Props {
themeId: number;
dispatch: Dispatch;
decryptionWorker: StateDecryptionWorker;
resourceFetcher: StateResourceFetcher;
syncReport: ProgressReport;
syncReportIsVisible: boolean;
}
const SidebarComponent = (props: Props) => {
const theme = themeStyle(props.themeId);
let decryptionReportText = '';
if (props.decryptionWorker && props.decryptionWorker.state !== 'idle' && props.decryptionWorker.itemCount) {
decryptionReportText = _('Decrypting items: %d/%d', props.decryptionWorker.itemIndex + 1, props.decryptionWorker.itemCount);
}
let resourceFetcherText = '';
if (props.resourceFetcher && props.resourceFetcher.toFetchCount) {
resourceFetcherText = _('Fetching resources: %d/%d', props.resourceFetcher.fetchingCount, props.resourceFetcher.toFetchCount);
}
const lines = Synchronizer.reportToLines(props.syncReport);
if (resourceFetcherText) lines.push(resourceFetcherText);
if (decryptionReportText) lines.push(decryptionReportText);
const syncReportText = [];
for (let i = 0; i < lines.length; i++) {
syncReportText.push(
<StyledSyncReportText key={i}>
{lines[i]}
</StyledSyncReportText>,
);
}
const hasSyncReport = syncReportText.length > 0;
const syncReportComp = !hasSyncReport || !props.syncReportIsVisible ? null : (
<StyledSyncReport key="sync_report" id="sync-report">
{syncReportText}
</StyledSyncReport>
);
// 同步按钮已移到侧边栏顶部;底部只在确实有同步报告时才占用空间
const syncReportToggle = hasSyncReport ? (
<button
className="sync-report-toggle"
style={{ color: theme.color2 }}
onClick={() => Setting.toggle('syncReportIsVisible')}
aria-label={_('Sync report')}
aria-expanded={props.syncReportIsVisible}
aria-controls="sync-report"
>
<i className={`fas fa-chevron-${props.syncReportIsVisible ? 'down' : 'up'}`}/>
</button>
) : null;
return (
<StyledRoot className='sidebar _scrollbar2' role='navigation' aria-label={_('Sidebar')}>
<div style={{ flex: 1 }}><FolderAndTagList/></div>
{hasSyncReport && (
<div style={{ flex: 0, padding: theme.mainPadding }}>
{syncReportToggle}
{syncReportComp}
</div>
)}
<div className='sidebar-footer'>
<button
className='sidebar-footer-button'
onClick={openOptions}
title={_('Options')}
aria-label={_('Options')}
>
<i className='fas fa-cog' aria-hidden='true'/>
</button>
</div>
</StyledRoot>
);
};
const mapStateToProps = (state: AppState) => {
return {
searches: state.searches,
syncReport: state.syncReport,
selectedSearchId: state.selectedSearchId,
selectedSmartFilterId: state.selectedSmartFilterId,
locale: state.settings.locale,
themeId: state.settings.theme,
collapsedFolderIds: state.collapsedFolderIds,
decryptionWorker: state.decryptionWorker,
resourceFetcher: state.resourceFetcher,
syncReportIsVisible: state.settings.syncReportIsVisible,
};
};
export default connect(mapStateToProps)(SidebarComponent);