mirror of
https://github.com/laurent22/joplin.git
synced 2026-06-18 20:16:34 +02:00
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { StyledSyncReportText, StyledSyncReport, StyledSynchronizeButton, StyledRoot } from './styles';
|
||||
import { ButtonLevel } from '../Button/Button';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import Synchronizer 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';
|
||||
@@ -11,8 +11,6 @@ import { connect } from 'react-redux';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { Dispatch } from 'redux';
|
||||
import FolderAndTagList from './FolderAndTagList';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import time from '@joplin/lib/time';
|
||||
|
||||
|
||||
interface Props {
|
||||
@@ -23,18 +21,26 @@ interface Props {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
syncReport: any;
|
||||
syncStarted: boolean;
|
||||
syncReportLogExpanded: boolean;
|
||||
syncPending: boolean;
|
||||
syncReportIsVisible: boolean;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- The generated report does not currently have a type
|
||||
const syncCompletedWithoutError = (syncReport: any) => {
|
||||
return syncReport.completedTime && (!syncReport.errors || !syncReport.errors.length);
|
||||
};
|
||||
|
||||
const SidebarComponent = (props: Props) => {
|
||||
const renderSynchronizeButton = (type: string) => {
|
||||
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
|
||||
const nothingToSync = type === 'sync' && !props.syncPending && syncCompletedWithoutError(props.syncReport);
|
||||
const iconName = nothingToSync ? 'fas fa-check' : 'icon-sync';
|
||||
|
||||
return (
|
||||
<StyledSynchronizeButton
|
||||
level={ButtonLevel.SidebarSecondary}
|
||||
className={`sidebar-sync-button ${type === 'sync' ? '' : '-syncing'}`}
|
||||
iconName="icon-sync"
|
||||
className={`sidebar-sync-button ${type === 'sync' ? '' : '-syncing'} ${nothingToSync ? '-synced' : ''}`}
|
||||
iconName={iconName}
|
||||
key="sync_button"
|
||||
title={label}
|
||||
onClick={() => {
|
||||
@@ -56,56 +62,46 @@ const SidebarComponent = (props: Props) => {
|
||||
resourceFetcherText = _('Fetching resources: %d/%d', props.resourceFetcher.fetchingCount, props.resourceFetcher.toFetchCount);
|
||||
}
|
||||
|
||||
const syncReportExpanded = props.syncReportLogExpanded;
|
||||
|
||||
const toggleSyncReport = useCallback(() => {
|
||||
Setting.setValue('syncReportLogExpanded', !syncReportExpanded);
|
||||
}, [syncReportExpanded]);
|
||||
|
||||
const lines = Synchronizer.reportToLines(props.syncReport);
|
||||
if (resourceFetcherText) lines.push(resourceFetcherText);
|
||||
if (decryptionReportText) lines.push(decryptionReportText);
|
||||
|
||||
const completedTime = props.syncReport && props.syncReport.completedTime
|
||||
? time.formatMsToLocal(props.syncReport.completedTime)
|
||||
: null;
|
||||
const syncReportText = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
syncReportText.push(
|
||||
<StyledSyncReportText key={i}>
|
||||
{lines[i]}
|
||||
</StyledSyncReportText>,
|
||||
);
|
||||
}
|
||||
|
||||
const syncButton = renderSynchronizeButton(props.syncStarted ? 'cancel' : 'sync');
|
||||
|
||||
// Toggle to show/hide sync log output
|
||||
const toggleButton = (
|
||||
const hasSyncReport = syncReportText.length > 0;
|
||||
|
||||
const syncReportComp = !hasSyncReport || !props.syncReportIsVisible ? null : (
|
||||
<StyledSyncReport key="sync_report" id="sync-report">
|
||||
{syncReportText}
|
||||
</StyledSyncReport>
|
||||
);
|
||||
|
||||
const syncReportToggle = (
|
||||
<button
|
||||
className="sidebar-sync-toggle"
|
||||
onClick={toggleSyncReport}
|
||||
aria-expanded={syncReportExpanded}
|
||||
aria-label={syncReportExpanded ? _('Hide sync log') : _('Show sync log')}
|
||||
title={syncReportExpanded ? _('Hide sync log') : _('Show sync log')}
|
||||
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-caret-${syncReportExpanded ? 'down' : 'right'}`} />
|
||||
{(completedTime || props.syncStarted) ? (
|
||||
<span className="timestamp">
|
||||
{props.syncStarted ? _('Last sync: In progress...') : _('Last sync: %s', completedTime)}
|
||||
</span>
|
||||
) : ''}
|
||||
<i className={`fas fa-chevron-${props.syncReportIsVisible ? 'down' : 'up'}`}/>
|
||||
</button>
|
||||
);
|
||||
|
||||
// Sync log output, only visible when expanded
|
||||
const syncReportComp = (syncReportExpanded && lines.length > 0) ? (
|
||||
<StyledSyncReport key="sync_report">
|
||||
{lines.map((line, i) => (
|
||||
<StyledSyncReportText key={i}>
|
||||
{line}
|
||||
</StyledSyncReportText>
|
||||
))}
|
||||
</StyledSyncReport>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<StyledRoot className='sidebar _scrollbar2' role='navigation' aria-label={_('Sidebar')}>
|
||||
<div style={{ flex: 1 }}><FolderAndTagList /></div>
|
||||
<div style={{ flex: 1 }}><FolderAndTagList/></div>
|
||||
<div style={{ flex: 0, padding: theme.mainPadding }}>
|
||||
{(completedTime || props.syncStarted) ? toggleButton : null}
|
||||
{syncReportToggle}
|
||||
{syncReportComp}
|
||||
{syncButton}
|
||||
</div>
|
||||
@@ -117,6 +113,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
searches: state.searches,
|
||||
syncStarted: state.syncStarted,
|
||||
syncPending: state.syncPending,
|
||||
syncReport: state.syncReport,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
selectedSmartFilterId: state.selectedSmartFilterId,
|
||||
@@ -125,7 +122,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
collapsedFolderIds: state.collapsedFolderIds,
|
||||
decryptionWorker: state.decryptionWorker,
|
||||
resourceFetcher: state.resourceFetcher,
|
||||
syncReportLogExpanded: state.settings.syncReportLogExpanded,
|
||||
syncReportIsVisible: state.settings.syncReportIsVisible,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
@use 'styles/sidebar-header-container.scss';
|
||||
@use 'styles/sidebar-spacer-item.scss';
|
||||
@use 'styles/sidebar-header-button.scss';
|
||||
@use 'styles/sidebar-sync-button.scss';
|
||||
@use 'styles/sidebar-sync-toggle.scss';
|
||||
@use 'styles/sidebar-sync-button.scss';
|
||||
@@ -105,7 +105,7 @@ export const StyledSyncReport = styled.div`
|
||||
opacity: 0.5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 25px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
word-wrap: break-word;
|
||||
|
||||
@@ -5,6 +5,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes icon-fade-in-a {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes icon-fade-in-b {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.sidebar-sync-button > .icon {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px !important;
|
||||
height: 16px;
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
|
||||
.sidebar-sync-button {
|
||||
&.-syncing > .icon {
|
||||
animation: icon-infinite-rotation 1s linear infinite;
|
||||
@@ -13,4 +32,29 @@
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.-syncing).-synced > .icon {
|
||||
animation: icon-fade-in-a 300ms ease-in-out;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
&:not(.-syncing):not(.-synced) > .icon {
|
||||
animation: icon-fade-in-b 300ms ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.sync-report-toggle {
|
||||
display: block;
|
||||
width: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
.sidebar-sync-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 4px 5px 4px 5px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--joplin-color2);
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
font-size: calc(var(--joplin-font-size) * 1.6);
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
i {
|
||||
width: 16px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
font-size: calc(var(--joplin-toolbar-icon-size) * 0.8);
|
||||
}
|
||||
|
||||
>.timestamp {
|
||||
font-size: 0.6em;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
␍
|
||||
@@ -1479,7 +1479,7 @@ const builtInMetadata = (Setting: typeof SettingType) => {
|
||||
noteVisiblePanes: { value: ['editor', 'viewer'], type: SettingItemType.Array, storage: SettingStorage.File, isGlobal: true, public: false, appTypes: [AppType.Desktop] },
|
||||
tagHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: [AppType.Desktop] },
|
||||
folderHeaderIsExpanded: { value: true, type: SettingItemType.Bool, public: false, appTypes: [AppType.Desktop] },
|
||||
syncReportLogExpanded: { value: false, type: SettingItemType.Bool, public: false, appTypes: [AppType.Desktop] },
|
||||
syncReportIsVisible: { value: false, type: SettingItemType.Bool, public: false, appTypes: [AppType.Desktop] },
|
||||
editor: { value: '', type: SettingItemType.String, subType: 'file_path_and_args', storage: SettingStorage.File, isGlobal: true, public: true, appTypes: [AppType.Cli, AppType.Desktop], label: () => _('Text editor command'), description: () => _('The editor command (may include arguments) that will be used to open a note. If none is provided it will try to auto-detect the default editor.') },
|
||||
'export.pdfPageSize': { value: 'A4', type: SettingItemType.String, advanced: true, storage: SettingStorage.File, isGlobal: true, isEnum: true, public: true, appTypes: [AppType.Desktop], label: () => _('Page size for PDF export'), options: () => {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user