mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-26 18:58:21 +02:00
Co-authored-by: Georg Grab <talkdirty@users.noreply.github.com>
This commit is contained in:
parent
6d56bb8afd
commit
6b8e84332d
@ -622,7 +622,7 @@ class Application extends BaseApplication {
|
||||
|
||||
// the following menu items will be available for all OS under Tools
|
||||
const toolsItemsAll = [{
|
||||
label: _('Resources'),
|
||||
label: _('Note attachments...'),
|
||||
click: () => {
|
||||
this.dispatch({
|
||||
type: 'NAV_GO',
|
||||
|
@ -267,6 +267,7 @@ class HeaderComponent extends React.Component {
|
||||
boxSizing: 'border-box',
|
||||
cursor: 'default',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'none',
|
||||
};
|
||||
|
||||
if (showBackButton) {
|
||||
|
@ -8,9 +8,14 @@ const { Header } = require('./Header.min.js');
|
||||
const prettyBytes = require('pretty-bytes');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
|
||||
interface Style {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
style: any;
|
||||
theme: any;
|
||||
style: Style
|
||||
}
|
||||
|
||||
interface Resource {
|
||||
@ -32,6 +37,8 @@ interface ResourceTable {
|
||||
onResourceClick: (resource: Resource) => any
|
||||
onResourceDelete: (resource: Resource) => any
|
||||
onToggleSorting: (order: SortingOrder) => any
|
||||
theme: any
|
||||
style: Style
|
||||
}
|
||||
|
||||
type SortingOrder = 'size' | 'name'
|
||||
@ -45,29 +52,58 @@ interface ActiveSorting {
|
||||
const ResourceTable: React.FC<ResourceTable> = (props: ResourceTable) => {
|
||||
const sortOrderEngagedMarker = (s: SortingOrder) => {
|
||||
return (
|
||||
<a href="#" onClick={ () => props.onToggleSorting(s) }>{
|
||||
(props.sorting.order === s && props.sorting.type === 'desc') ? '▾' : '▴' }</a>
|
||||
<a href="#"
|
||||
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>
|
||||
<tr>
|
||||
<th>{_('Title')} { sortOrderEngagedMarker('name') }</th>
|
||||
<th>{_('Size')} { sortOrderEngagedMarker('size') }</th>
|
||||
<th>{_('ID')}</th>
|
||||
<th>{_('Action')}</th>
|
||||
<th style={headerStyle}>{_('Title')} {sortOrderEngagedMarker('name')}</th>
|
||||
<th style={headerStyle}>{_('Size')} {sortOrderEngagedMarker('size')}</th>
|
||||
<th style={headerStyle}>{_('ID')}</th>
|
||||
<th style={headerStyle}>{_('Action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.resources.map((resource: Resource, index: number) =>
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<a href="#" onClick={() => props.onResourceClick(resource)}>{resource.title}</a>
|
||||
<td style={titleCellStyle} className="titleCell">
|
||||
<a
|
||||
style={{ color: props.theme.htmlLinkColor }}
|
||||
href="#"
|
||||
onClick={() => props.onResourceClick(resource)}>{resource.title || `(${_('Untitled')})`}
|
||||
</a>
|
||||
</td>
|
||||
<td>{prettyBytes(resource.size)}</td>
|
||||
<td>{resource.id}</td>
|
||||
<td>
|
||||
<button onClick={ () => props.onResourceDelete(resource) }>{_('Delete')}</button>
|
||||
<td style={cellStyle} className="dataCell">{prettyBytes(resource.size)}</td>
|
||||
<td style={cellStyle} className="dataCell">{resource.id}</td>
|
||||
<td style={cellStyle} className="dataCell">
|
||||
<button style={props.theme.buttonStyle} onClick={() => props.onResourceDelete(resource)}>{_('Delete')}</button>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
@ -123,6 +159,13 @@ class ResourceScreenComponent extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
onResourceDelete(resource: Resource) {
|
||||
const ok = bridge().showConfirmMessageBox(_('Delete attachment "%s"?', resource.title), {
|
||||
buttons: [_('Delete'), _('Cancel')],
|
||||
defaultId: 1,
|
||||
});
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
Resource.delete(resource.id)
|
||||
.catch((error: Error) => {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
@ -158,9 +201,23 @@ class ResourceScreenComponent extends React.Component<Props, State> {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
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} />
|
||||
<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>
|
||||
{!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>
|
||||
}
|
||||
{this.state.resources && <ResourceTable
|
||||
resources={ this.state.resources }
|
||||
sorting={ this.state.sorting }
|
||||
onToggleSorting={ (order) => this.onToggleSortOrder(order) }
|
||||
onResourceClick={ (resource) => this.openResource(resource) }
|
||||
onResourceDelete={ (resource) => this.onResourceDelete(resource) }
|
||||
theme={theme}
|
||||
style={style}
|
||||
resources={this.state.resources}
|
||||
sorting={this.state.sorting}
|
||||
onToggleSorting={(order) => this.onToggleSortOrder(order)}
|
||||
onResourceClick={(resource) => this.openResource(resource)}
|
||||
onResourceDelete={(resource) => this.onResourceDelete(resource)}
|
||||
/>}
|
||||
</div>
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ class RootComponent extends React.Component {
|
||||
DropboxLogin: { screen: DropboxLoginScreen, title: () => _('Dropbox Login') },
|
||||
Import: { screen: ImportScreen, title: () => _('Import') },
|
||||
Config: { screen: ConfigScreen, title: () => _('Options') },
|
||||
Resources: { screen: ResourceScreen, title: () => _('Resources') },
|
||||
Resources: { screen: ResourceScreen, title: () => _('Note attachments') },
|
||||
Status: { screen: StatusScreen, title: () => _('Synchronisation Status') },
|
||||
};
|
||||
|
||||
|
@ -131,6 +131,7 @@ class SideBarComponent extends React.Component {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
userSelect: 'none',
|
||||
},
|
||||
listItemSelected: {
|
||||
backgroundColor: theme.selectedColor2,
|
||||
@ -160,6 +161,7 @@ class SideBarComponent extends React.Component {
|
||||
paddingLeft: 8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
userSelect: 'none',
|
||||
},
|
||||
button: {
|
||||
padding: 6,
|
||||
@ -176,6 +178,7 @@ class SideBarComponent extends React.Component {
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
cursor: 'default',
|
||||
userSelect: 'none',
|
||||
},
|
||||
syncReport: {
|
||||
fontFamily: theme.fontFamily,
|
||||
@ -195,6 +198,7 @@ class SideBarComponent extends React.Component {
|
||||
noteCount: {
|
||||
paddingLeft: 5,
|
||||
opacity: 0.5,
|
||||
userSelect: 'none',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,7 @@ const style = createSelector(
|
||||
alignItems: 'center',
|
||||
padding: 9,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
userSelect: 'none',
|
||||
},
|
||||
buttonIcon: {
|
||||
fontSize: 24,
|
||||
|
@ -5,7 +5,7 @@ const darkStyle = {
|
||||
color: '#dddddd',
|
||||
colorError: 'red',
|
||||
colorWarn: '#9A5B00',
|
||||
colorFaded: '#777777', // For less important text
|
||||
colorFaded: '#999999', // For less important text
|
||||
colorBright: '#ffffff', // For important text
|
||||
dividerColor: '#555555',
|
||||
selectedColor: '#333333',
|
||||
|
@ -201,6 +201,14 @@ function addExtraStyles(style) {
|
||||
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.dropdownList = Object.assign({}, style.inputStyle);
|
||||
@ -299,6 +307,7 @@ function themeStyle(theme) {
|
||||
color: output.color,
|
||||
backgroundColor: output.backgroundColor,
|
||||
borderColor: output.dividerColor,
|
||||
userSelect: 'none',
|
||||
}
|
||||
);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user