diff --git a/packages/app-mobile/components/screens/status.tsx b/packages/app-mobile/components/screens/status.tsx index a164ce1697..fcde858ed6 100644 --- a/packages/app-mobile/components/screens/status.tsx +++ b/packages/app-mobile/components/screens/status.tsx @@ -1,16 +1,17 @@ import * as React from 'react'; -import { View, Text, Button, FlatList, TextStyle, StyleSheet } from 'react-native'; +import { View, Text, Button, FlatList, TextStyle, StyleSheet, Role } from 'react-native'; import Setting from '@joplin/lib/models/Setting'; import { connect } from 'react-redux'; import { ScreenHeader } from '../ScreenHeader'; -import ReportService, { ReportSection } from '@joplin/lib/services/ReportService'; +import ReportService, { ReportItemType, ReportSection } from '@joplin/lib/services/ReportService'; import { _ } from '@joplin/lib/locale'; import { BaseScreenComponent } from '../base-screen'; import { themeStyle } from '../global-style'; import { AppState } from '../../utils/types'; import checkDisabledSyncItemsNotification from '@joplin/lib/services/synchronizer/utils/checkDisabledSyncItemsNotification'; import { Dispatch } from 'redux'; +import Icon from '../Icon'; interface Props { themeId: number; @@ -21,6 +22,86 @@ interface State { report: ReportSection[]; } +interface ProcessedLine { + key: string; + text?: string; + isSection?: boolean; + isDivider?: boolean; + retryAllHandler?: ()=> void; + retryHandler?: ()=> void; + ignoreHandler?: ()=> void; + listItems?: ProcessedLine[]; +} + +type OnRefreshScreen = ()=> Promise; + +const processReport = (report: ReportSection[], refreshScreen: OnRefreshScreen, dispatch: Dispatch, baseStyle: TextStyle) => { + const lines: ProcessedLine[] = []; + let currentList: ProcessedLine[]|null = null; + + for (let i = 0; i < report.length; i++) { + const section = report[i]; + + let style: TextStyle = { ...baseStyle }; + style.fontWeight = 'bold'; + if (i > 0) style.paddingTop = 20; + lines.push({ key: `section_${i}`, isSection: true, text: section.title }); + if (section.canRetryAll) { + lines.push({ key: `retry_all_${i}`, text: '', retryAllHandler: section.retryAllHandler }); + } + + for (const n in section.body) { + if (!section.body.hasOwnProperty(n)) continue; + style = { ...baseStyle }; + const item = section.body[n]; + + let text = ''; + + let retryHandler = null; + let ignoreHandler = null; + if (typeof item === 'object') { + if (item.canRetry) { + retryHandler = async () => { + await item.retryHandler(); + await refreshScreen(); + }; + } + if (item.canIgnore) { + ignoreHandler = async () => { + await item.ignoreHandler(); + await refreshScreen(); + await checkDisabledSyncItemsNotification((action) => dispatch(action)); + }; + } + if (item.type === ReportItemType.OpenList) { + currentList = []; + } else if (item.type === ReportItemType.CloseList) { + lines.push({ key: `list_${i}_${n}`, listItems: currentList }); + currentList = null; + } + text = item.text; + } else { + text = item; + } + + const line = { key: `item_${i}_${n}`, text: text, retryHandler, ignoreHandler }; + if (currentList) { + // The OpenList item, for example, might be empty and should be skipped: + const hasContent = line.text || retryHandler || ignoreHandler; + if (hasContent) { + currentList.push(line); + } + } else { + lines.push(line); + } + } + + lines.push({ key: `divider2_${i}`, isDivider: true }); + } + + return lines; +}; + class StatusScreenComponent extends BaseScreenComponent { public constructor(props: Props) { super(props); @@ -52,15 +133,11 @@ class StatusScreenComponent extends BaseScreenComponent { marginLeft: 2, marginRight: 2, }, - }); - } - - public override render() { - const theme = themeStyle(this.props.themeId); - const styles = this.styles(); - - const renderBody = (report: ReportSection[]) => { - const baseStyle = { + retryAllButton: { + flexGrow: 0, + alignSelf: 'flex-start', + }, + baseStyle: { paddingLeft: 6, paddingRight: 6, paddingTop: 2, @@ -68,98 +145,95 @@ class StatusScreenComponent extends BaseScreenComponent { flex: 0, color: theme.color, fontSize: theme.fontSize, - }; + alignSelf: 'center', + }, + listWrapper: { + paddingBottom: 5, + }, + listBullet: { + fontSize: theme.fontSize / 3, + color: theme.color, + alignSelf: 'center', + justifyContent: 'center', + flexGrow: 0, + marginStart: 12, + marginEnd: 2, + }, + divider: { + borderBottomWidth: 1, + borderBottomColor: theme.dividerColor, + marginTop: 20, + marginBottom: 20, + }, + }); + } - const lines = []; + public override render() { + const styles = this.styles(); - for (let i = 0; i < report.length; i++) { - const section = report[i]; + const renderItem = (item: ProcessedLine, inList: boolean) => { + const style: TextStyle = { ...styles.baseStyle }; - let style: TextStyle = { ...baseStyle }; + let textRole: Role|null = undefined; + const text = item.text; + if (item.isSection === true) { style.fontWeight = 'bold'; - if (i > 0) style.paddingTop = 20; - lines.push({ key: `section_${i}`, isSection: true, text: section.title }); - if (section.canRetryAll) { - lines.push({ key: `retry_all_${i}`, text: '', retryAllHandler: section.retryAllHandler }); - } - - for (const n in section.body) { - if (!section.body.hasOwnProperty(n)) continue; - style = { ...baseStyle }; - const item = section.body[n]; - - let text = ''; - - let retryHandler = null; - let ignoreHandler = null; - if (typeof item === 'object') { - if (item.canRetry) { - retryHandler = async () => { - await item.retryHandler(); - await this.refreshScreen(); - }; - } - if (item.canIgnore) { - ignoreHandler = async () => { - await item.ignoreHandler(); - await this.refreshScreen(); - await checkDisabledSyncItemsNotification((action) => this.props.dispatch(action)); - }; - } - text = item.text; - } else { - text = item; - } - - lines.push({ key: `item_${i}_${n}`, text: text, retryHandler, ignoreHandler }); - } - - lines.push({ key: `divider2_${i}`, isDivider: true }); + style.marginBottom = 5; + textRole = 'heading'; + } else if (inList) { + textRole = 'listitem'; } + style.flex = 1; + + const retryAllButton = item.retryAllHandler ? ( + +