1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-02-04 19:16:07 +02:00

Desktop: Resolves #2639: Resource screen improvements (#2850)

Co-authored-by: Georg Grab <talkdirty@users.noreply.github.com>
This commit is contained in:
Laurent Cozic 2020-04-08 11:12:25 +01:00
parent 6d56bb8afd
commit 6b8e84332d
8 changed files with 98 additions and 24 deletions

View File

@ -622,7 +622,7 @@ class Application extends BaseApplication {
// the following menu items will be available for all OS under Tools // the following menu items will be available for all OS under Tools
const toolsItemsAll = [{ const toolsItemsAll = [{
label: _('Resources'), label: _('Note attachments...'),
click: () => { click: () => {
this.dispatch({ this.dispatch({
type: 'NAV_GO', type: 'NAV_GO',

View File

@ -267,6 +267,7 @@ class HeaderComponent extends React.Component {
boxSizing: 'border-box', boxSizing: 'border-box',
cursor: 'default', cursor: 'default',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
userSelect: 'none',
}; };
if (showBackButton) { if (showBackButton) {

View File

@ -8,9 +8,14 @@ const { Header } = require('./Header.min.js');
const prettyBytes = require('pretty-bytes'); const prettyBytes = require('pretty-bytes');
const Resource = require('lib/models/Resource.js'); const Resource = require('lib/models/Resource.js');
interface Style {
width: number
height: number
}
interface Props { interface Props {
style: any;
theme: any; theme: any;
style: Style
} }
interface Resource { interface Resource {
@ -32,6 +37,8 @@ interface ResourceTable {
onResourceClick: (resource: Resource) => any onResourceClick: (resource: Resource) => any
onResourceDelete: (resource: Resource) => any onResourceDelete: (resource: Resource) => any
onToggleSorting: (order: SortingOrder) => any onToggleSorting: (order: SortingOrder) => any
theme: any
style: Style
} }
type SortingOrder = 'size' | 'name' type SortingOrder = 'size' | 'name'
@ -45,29 +52,58 @@ interface ActiveSorting {
const ResourceTable: React.FC<ResourceTable> = (props: ResourceTable) => { const ResourceTable: React.FC<ResourceTable> = (props: ResourceTable) => {
const sortOrderEngagedMarker = (s: SortingOrder) => { const sortOrderEngagedMarker = (s: SortingOrder) => {
return ( return (
<a href="#" onClick={ () => props.onToggleSorting(s) }>{ <a href="#"
(props.sorting.order === s && props.sorting.type === 'desc') ? '▾' : '▴' }</a> style={{ color: props.theme.htmlLinkColor }}
onClick={() => props.onToggleSorting(s)}>{
(props.sorting.order === s && props.sorting.type === 'desc') ? '▾' : '▴'}</a>
); );
}; };
return <table style={{ width: '90%' }}>
const titleCellStyle = {
...props.theme.textStyle,
textOverflow: 'ellipsis',
overflowX: 'hidden',
maxWidth: 1,
width: '100%',
whiteSpace: 'nowrap',
};
const cellStyle = {
...props.theme.textStyleMinor,
whiteSpace: 'nowrap',
width: 1,
};
const headerStyle = {
...props.theme.textStyle,
whiteSpace: 'nowrap',
width: 1,
fontWeight: 'bold',
};
return <table style={{ width: '100%' }}>
<thead> <thead>
<tr> <tr>
<th>{_('Title')} { sortOrderEngagedMarker('name') }</th> <th style={headerStyle}>{_('Title')} {sortOrderEngagedMarker('name')}</th>
<th>{_('Size')} { sortOrderEngagedMarker('size') }</th> <th style={headerStyle}>{_('Size')} {sortOrderEngagedMarker('size')}</th>
<th>{_('ID')}</th> <th style={headerStyle}>{_('ID')}</th>
<th>{_('Action')}</th> <th style={headerStyle}>{_('Action')}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{props.resources.map((resource: Resource, index: number) => {props.resources.map((resource: Resource, index: number) =>
<tr key={index}> <tr key={index}>
<td> <td style={titleCellStyle} className="titleCell">
<a href="#" onClick={() => props.onResourceClick(resource)}>{resource.title}</a> <a
style={{ color: props.theme.htmlLinkColor }}
href="#"
onClick={() => props.onResourceClick(resource)}>{resource.title || `(${_('Untitled')})`}
</a>
</td> </td>
<td>{prettyBytes(resource.size)}</td> <td style={cellStyle} className="dataCell">{prettyBytes(resource.size)}</td>
<td>{resource.id}</td> <td style={cellStyle} className="dataCell">{resource.id}</td>
<td> <td style={cellStyle} className="dataCell">
<button onClick={ () => props.onResourceDelete(resource) }>{_('Delete')}</button> <button style={props.theme.buttonStyle} onClick={() => props.onResourceDelete(resource)}>{_('Delete')}</button>
</td> </td>
</tr> </tr>
)} )}
@ -123,6 +159,13 @@ class ResourceScreenComponent extends React.Component<Props, State> {
} }
onResourceDelete(resource: Resource) { onResourceDelete(resource: Resource) {
const ok = bridge().showConfirmMessageBox(_('Delete attachment "%s"?', resource.title), {
buttons: [_('Delete'), _('Cancel')],
defaultId: 1,
});
if (!ok) {
return;
}
Resource.delete(resource.id) Resource.delete(resource.id)
.catch((error: Error) => { .catch((error: Error) => {
bridge().showErrorMessageBox(error.message); bridge().showErrorMessageBox(error.message);
@ -158,9 +201,23 @@ class ResourceScreenComponent extends React.Component<Props, State> {
const style = this.props.style; const style = this.props.style;
const theme = themeStyle(this.props.theme); const theme = themeStyle(this.props.theme);
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width }); const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
return <div>
const rootStyle = {
...style,
overflowY: 'scroll',
color: theme.color,
padding: 20,
boxSizing: 'border-box',
};
rootStyle.height = style.height - 35; // Minus the header height
delete rootStyle.width;
return <div style={{ ...theme.containerStyle, fontFamily: theme.fontFamily }}>
<Header style={headerStyle} /> <Header style={headerStyle} />
<div style={{ ...style, margin: '20px', overflow: 'scroll' }}> <div style={rootStyle}>
<div style={{ ...theme.notificationBox, marginBottom: 10 }}>{
_('This is an advanced tool to show the attachments that are linked to your notes. Please be careful when deleting one of them as they cannot be restored afterwards.')
}</div>
{this.state.isLoading && <div>{_('Please wait...')}</div>} {this.state.isLoading && <div>{_('Please wait...')}</div>}
{!this.state.isLoading && <div> {!this.state.isLoading && <div>
{!this.state.resources && <div> {!this.state.resources && <div>
@ -171,11 +228,13 @@ class ResourceScreenComponent extends React.Component<Props, State> {
<div>{_('Warning: not all resources shown for performance reasons (limit: %s).', MAX_RESOURCES)}</div> <div>{_('Warning: not all resources shown for performance reasons (limit: %s).', MAX_RESOURCES)}</div>
} }
{this.state.resources && <ResourceTable {this.state.resources && <ResourceTable
resources={ this.state.resources } theme={theme}
sorting={ this.state.sorting } style={style}
onToggleSorting={ (order) => this.onToggleSortOrder(order) } resources={this.state.resources}
onResourceClick={ (resource) => this.openResource(resource) } sorting={this.state.sorting}
onResourceDelete={ (resource) => this.onResourceDelete(resource) } onToggleSorting={(order) => this.onToggleSortOrder(order)}
onResourceClick={(resource) => this.openResource(resource)}
onResourceDelete={(resource) => this.onResourceDelete(resource)}
/>} />}
</div> </div>
} }

View File

@ -92,7 +92,7 @@ class RootComponent extends React.Component {
DropboxLogin: { screen: DropboxLoginScreen, title: () => _('Dropbox Login') }, DropboxLogin: { screen: DropboxLoginScreen, title: () => _('Dropbox Login') },
Import: { screen: ImportScreen, title: () => _('Import') }, Import: { screen: ImportScreen, title: () => _('Import') },
Config: { screen: ConfigScreen, title: () => _('Options') }, Config: { screen: ConfigScreen, title: () => _('Options') },
Resources: { screen: ResourceScreen, title: () => _('Resources') }, Resources: { screen: ResourceScreen, title: () => _('Note attachments') },
Status: { screen: StatusScreen, title: () => _('Synchronisation Status') }, Status: { screen: StatusScreen, title: () => _('Synchronisation Status') },
}; };

View File

@ -131,6 +131,7 @@ class SideBarComponent extends React.Component {
display: 'flex', display: 'flex',
flex: 1, flex: 1,
alignItems: 'center', alignItems: 'center',
userSelect: 'none',
}, },
listItemSelected: { listItemSelected: {
backgroundColor: theme.selectedColor2, backgroundColor: theme.selectedColor2,
@ -160,6 +161,7 @@ class SideBarComponent extends React.Component {
paddingLeft: 8, paddingLeft: 8,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
userSelect: 'none',
}, },
button: { button: {
padding: 6, padding: 6,
@ -176,6 +178,7 @@ class SideBarComponent extends React.Component {
marginLeft: 5, marginLeft: 5,
marginRight: 5, marginRight: 5,
cursor: 'default', cursor: 'default',
userSelect: 'none',
}, },
syncReport: { syncReport: {
fontFamily: theme.fontFamily, fontFamily: theme.fontFamily,
@ -195,6 +198,7 @@ class SideBarComponent extends React.Component {
noteCount: { noteCount: {
paddingLeft: 5, paddingLeft: 5,
opacity: 0.5, opacity: 0.5,
userSelect: 'none',
}, },
}; };

View File

@ -16,6 +16,7 @@ const style = createSelector(
alignItems: 'center', alignItems: 'center',
padding: 9, padding: 9,
backgroundColor: theme.backgroundColor, backgroundColor: theme.backgroundColor,
userSelect: 'none',
}, },
buttonIcon: { buttonIcon: {
fontSize: 24, fontSize: 24,

View File

@ -5,7 +5,7 @@ const darkStyle = {
color: '#dddddd', color: '#dddddd',
colorError: 'red', colorError: 'red',
colorWarn: '#9A5B00', colorWarn: '#9A5B00',
colorFaded: '#777777', // For less important text colorFaded: '#999999', // For less important text
colorBright: '#ffffff', // For important text colorBright: '#ffffff', // For important text
dividerColor: '#555555', dividerColor: '#555555',
selectedColor: '#333333', selectedColor: '#333333',

View File

@ -201,6 +201,14 @@ function addExtraStyles(style) {
marginRight: 6, marginRight: 6,
}; };
style.notificationBox = {
backgroundColor: style.warningBackgroundColor,
display: 'flex',
alignItems: 'center',
padding: 10,
fontSize: style.fontSize,
};
style.dialogTitle = Object.assign({}, style.h1Style, { marginBottom: '1.2em' }); style.dialogTitle = Object.assign({}, style.h1Style, { marginBottom: '1.2em' });
style.dropdownList = Object.assign({}, style.inputStyle); style.dropdownList = Object.assign({}, style.inputStyle);
@ -299,6 +307,7 @@ function themeStyle(theme) {
color: output.color, color: output.color,
backgroundColor: output.backgroundColor, backgroundColor: output.backgroundColor,
borderColor: output.dividerColor, borderColor: output.dividerColor,
userSelect: 'none',
} }
); );