mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-12-27 19:07:15 +08:00
Add setting to disable email content max width (#251)
commit 84d0997cdca895d321ed6e70f1ab40cf03b14aa0 Author: Ben Gotow <ben@foundry376.com> Date: Sun Jun 9 17:36:38 2019 -0500 A bit of polish commit 066963a8111d510cab2d87caaa547bdabe581461 Merge:06d2e4546
8ed229a7c
Author: Ben Gotow <ben@foundry376.com> Date: Sun Jun 9 17:07:20 2019 -0500 Merge branch 'emailWidthFix' of https://github.com/mattlyons0/Mailspring into mattlyons0-emailWidthFix # Conflicts: # app/internal_packages/message-list/lib/message-list.tsx # app/internal_packages/message-list/styles/message-list.less # app/src/config-schema.es6 commit8ed229a7c7
Author: Matt Lyons <matt@mattlyons.net> Date: Wed Oct 25 03:02:03 2017 -0700 Add setting to disable email content max width Closes #228
This commit is contained in:
parent
06d2e4546c
commit
f20e9c3f39
10 changed files with 189 additions and 134 deletions
|
@ -165,8 +165,10 @@ export default class ComposerView extends React.Component<ComposerViewProps, Com
|
|||
}
|
||||
|
||||
_renderContent() {
|
||||
const restrictWidth = AppEnv.config.get('core.reading.restrictMaxWidth');
|
||||
|
||||
return (
|
||||
<div className="composer-centered">
|
||||
<div className={`composer-centered ${restrictWidth && 'restrict-width'}`}>
|
||||
<ComposerHeader
|
||||
ref={el => {
|
||||
if (el) {
|
||||
|
@ -333,8 +335,9 @@ export default class ComposerView extends React.Component<ComposerViewProps, Com
|
|||
}
|
||||
|
||||
_renderActionsRegion() {
|
||||
const restrictWidth = AppEnv.config.get('core.reading.restrictMaxWidth');
|
||||
return (
|
||||
<div className="composer-action-bar-content">
|
||||
<div className={`composer-action-bar-content ${restrictWidth && 'restrict-width'}`}>
|
||||
<ActionBarPlugins
|
||||
draft={this.props.draft}
|
||||
session={this.props.session}
|
||||
|
|
|
@ -286,9 +286,12 @@ body.platform-win32 {
|
|||
display: flex;
|
||||
margin: 0 auto;
|
||||
flex-direction: row;
|
||||
max-width: @compose-width;
|
||||
padding: 9px 22.5px;
|
||||
|
||||
&.restrict-width {
|
||||
max-width: @compose-width;
|
||||
}
|
||||
|
||||
.composer-action-bar-plugins {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
@ -338,9 +341,12 @@ body.platform-win32 {
|
|||
flex-direction: column;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: @compose-width;
|
||||
margin: 0 auto;
|
||||
padding-top: @spacing-standard * 0.7;
|
||||
|
||||
&.restrict-width {
|
||||
max-width: @compose-width;
|
||||
}
|
||||
}
|
||||
|
||||
.composer-body-wrap {
|
||||
|
|
|
@ -84,6 +84,8 @@ export default class EmailFrame extends React.Component<EmailFrameProps> {
|
|||
// the `border-collapse: collapse` css property while setting a
|
||||
// `padding`.
|
||||
const styles = EmailFrameStylesStore.styles();
|
||||
const restrictWidth = AppEnv.config.get('core.reading.restrictMaxWidth');
|
||||
|
||||
doc.open();
|
||||
doc.write(
|
||||
`<!DOCTYPE html>` +
|
||||
|
@ -92,6 +94,10 @@ export default class EmailFrame extends React.Component<EmailFrameProps> {
|
|||
);
|
||||
doc.close();
|
||||
|
||||
if (doc.body && restrictWidth) {
|
||||
doc.body.classList.add('restrict-width');
|
||||
}
|
||||
|
||||
// Notify the EventedIFrame that we've replaced it's document (with `open`)
|
||||
// so it can attach event listeners again.
|
||||
this._iframeComponent.didReplaceDocument();
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import {
|
||||
PropTypes,
|
||||
Utils,
|
||||
DraftStore,
|
||||
ComponentRegistry,
|
||||
Thread,
|
||||
Message,
|
||||
} from 'mailspring-exports';
|
||||
import { Utils, DraftStore, ComponentRegistry, Thread, Message } from 'mailspring-exports';
|
||||
|
||||
import MessageItem from './message-item';
|
||||
|
||||
|
@ -21,9 +14,13 @@ interface MessageItemContainerProps {
|
|||
scrollTo: () => void;
|
||||
}
|
||||
|
||||
interface MessageItemContainerState {
|
||||
isSending: boolean;
|
||||
}
|
||||
|
||||
export default class MessageItemContainer extends React.Component<
|
||||
MessageItemContainerProps,
|
||||
{ isSending: boolean }
|
||||
MessageItemContainerState
|
||||
> {
|
||||
static displayName = 'MessageItemContainer';
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import React from 'react';
|
||||
import { localized, PropTypes, Utils } from 'mailspring-exports';
|
||||
|
||||
export class MessageListScrollTooltip extends React.Component<
|
||||
{ viewportCenter: number; totalHeight: number },
|
||||
{ idx: number; count: number }
|
||||
> {
|
||||
static displayName = 'MessageListScrollTooltip';
|
||||
static propTypes = {
|
||||
viewportCenter: PropTypes.number.isRequired,
|
||||
totalHeight: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.setupForProps(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
this.setupForProps(newProps);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(newProps, newState) {
|
||||
return !Utils.isEqualReact(this.state, newState);
|
||||
}
|
||||
|
||||
setupForProps(props) {
|
||||
// Technically, we could have MessageList provide the currently visible
|
||||
// item index, but the DOM approach is simple and self-contained.
|
||||
//
|
||||
const els = document.querySelectorAll('.message-item-wrap');
|
||||
let idx = Array.from(els).findIndex(el => (el as HTMLElement).offsetTop > props.viewportCenter);
|
||||
if (idx === -1) {
|
||||
idx = els.length;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
idx: idx,
|
||||
count: els.length,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="scroll-tooltip">
|
||||
{localized('%1$@ of %2$@', this.state.idx, this.state.count)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import {
|
||||
localized,
|
||||
PropTypes,
|
||||
Utils,
|
||||
Actions,
|
||||
MessageStore,
|
||||
|
@ -27,53 +25,8 @@ import {
|
|||
|
||||
import FindInThread from './find-in-thread';
|
||||
import MessageItemContainer from './message-item-container';
|
||||
|
||||
class MessageListScrollTooltip extends React.Component<
|
||||
{ viewportCenter: number; totalHeight: number },
|
||||
{ idx: number; count: number }
|
||||
> {
|
||||
static displayName = 'MessageListScrollTooltip';
|
||||
static propTypes = {
|
||||
viewportCenter: PropTypes.number.isRequired,
|
||||
totalHeight: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.setupForProps(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
this.setupForProps(newProps);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(newProps, newState) {
|
||||
return !Utils.isEqualReact(this.state, newState);
|
||||
}
|
||||
|
||||
setupForProps(props) {
|
||||
// Technically, we could have MessageList provide the currently visible
|
||||
// item index, but the DOM approach is simple and self-contained.
|
||||
//
|
||||
const els = document.querySelectorAll('.message-item-wrap');
|
||||
let idx = Array.from(els).findIndex(el => (el as HTMLElement).offsetTop > props.viewportCenter);
|
||||
if (idx === -1) {
|
||||
idx = els.length;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
idx: idx,
|
||||
count: els.length,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="scroll-tooltip">
|
||||
{localized('%1$@ of %2$@', this.state.idx, this.state.count)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
import { MessageListScrollTooltip } from './message-list-scroll-tooltip';
|
||||
import { SubjectLineIcons } from './subject-line-icons';
|
||||
|
||||
interface MessageListState {
|
||||
messages: Message[];
|
||||
|
@ -87,6 +40,10 @@ interface MessageListState {
|
|||
minified: boolean;
|
||||
}
|
||||
|
||||
const PREF_REPLY_TYPE = 'core.sending.defaultReplyType';
|
||||
const PREF_RESTRICT_WIDTH = 'core.reading.restrictMaxWidth';
|
||||
const PREF_DESCENDING_ORDER = 'core.reading.descendingOrderMessageList';
|
||||
|
||||
class MessageList extends React.Component<{}, MessageListState> {
|
||||
static displayName = 'MessageList';
|
||||
static containerStyles = {
|
||||
|
@ -172,7 +129,7 @@ class MessageList extends React.Component<{}, MessageListState> {
|
|||
|
||||
// Returns either "reply" or "reply-all"
|
||||
_replyType() {
|
||||
const defaultReplyType = AppEnv.config.get('core.sending.defaultReplyType');
|
||||
const defaultReplyType = AppEnv.config.get(PREF_REPLY_TYPE);
|
||||
const lastMessage = this._lastMessage();
|
||||
if (!lastMessage) {
|
||||
return 'reply';
|
||||
|
@ -232,7 +189,7 @@ class MessageList extends React.Component<{}, MessageListState> {
|
|||
const hasReplyArea = mostRecentMessage && !mostRecentMessage.draft;
|
||||
|
||||
// Invert the message list if the descending option is set
|
||||
if (AppEnv.config.get('core.reading.descendingOrderMessageList')) {
|
||||
if (AppEnv.config.get(PREF_DESCENDING_ORDER)) {
|
||||
messages = messages.reverse();
|
||||
}
|
||||
|
||||
|
@ -396,61 +353,13 @@ class MessageList extends React.Component<{}, MessageListState> {
|
|||
thread={this.state.currentThread}
|
||||
/>
|
||||
</div>
|
||||
{this._renderIcons()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderIcons() {
|
||||
return (
|
||||
<div className="message-icons-wrap">
|
||||
{this._renderExpandToggle()}
|
||||
<div onClick={this._onPrintThread}>
|
||||
<RetinaImg
|
||||
name="print.png"
|
||||
title={localized('Print Thread')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
{this._renderPopoutToggle()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderExpandToggle() {
|
||||
if (!this.state.canCollapse) {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div onClick={this._onToggleAllMessagesExpanded}>
|
||||
<RetinaImg
|
||||
name={this.state.hasCollapsedItems ? 'expand.png' : 'collapse.png'}
|
||||
title={this.state.hasCollapsedItems ? localized('Expand All') : localized('Collapse All')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPopoutToggle() {
|
||||
if (AppEnv.isThreadWindow()) {
|
||||
return (
|
||||
<div onClick={this._onPopThreadIn}>
|
||||
<RetinaImg
|
||||
name="thread-popin.png"
|
||||
title={localized('Pop thread in')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div onClick={this._onPopoutThread}>
|
||||
<RetinaImg
|
||||
name="thread-popout.png"
|
||||
title={localized('Popout thread')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
<SubjectLineIcons
|
||||
canCollapse={this.state.canCollapse}
|
||||
hasCollapsedItems={this.state.hasCollapsedItems}
|
||||
onPrint={this._onPrintThread}
|
||||
onPopIn={this._onPopThreadIn}
|
||||
onPopOut={this._onPopoutThread}
|
||||
onToggleAllExpanded={this._onToggleAllMessagesExpanded}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -500,15 +409,17 @@ class MessageList extends React.Component<{}, MessageListState> {
|
|||
ready: !this.state.loading,
|
||||
});
|
||||
|
||||
const messageListClass = classNames({
|
||||
'message-list': true,
|
||||
'height-fix': SearchableComponentStore.searchTerm !== null,
|
||||
});
|
||||
|
||||
return (
|
||||
<KeyCommandsRegion globalHandlers={this._globalKeymapHandlers()}>
|
||||
<FindInThread />
|
||||
<div className={messageListClass} id="message-list">
|
||||
<div
|
||||
id="message-list"
|
||||
className={classNames({
|
||||
'message-list': true,
|
||||
'restrict-width': AppEnv.config.get(PREF_RESTRICT_WIDTH),
|
||||
'height-fix': SearchableComponentStore.searchTerm !== null,
|
||||
})}
|
||||
>
|
||||
<ScrollRegion
|
||||
tabIndex={-1}
|
||||
className={wrapClass}
|
||||
|
@ -523,7 +434,10 @@ class MessageList extends React.Component<{}, MessageListState> {
|
|||
<InjectedComponentSet
|
||||
className="message-list-headers"
|
||||
matching={{ role: 'MessageListHeaders' }}
|
||||
exposedProps={{ thread: this.state.currentThread, messages: this.state.messages }}
|
||||
exposedProps={{
|
||||
thread: this.state.currentThread,
|
||||
messages: this.state.messages,
|
||||
}}
|
||||
direction="column"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import React from 'react';
|
||||
import { RetinaImg } from 'mailspring-component-kit';
|
||||
import { localized } from 'mailspring-exports';
|
||||
|
||||
interface SubjectLineIconsProps {
|
||||
canCollapse: boolean;
|
||||
hasCollapsedItems: boolean;
|
||||
|
||||
onPrint: () => void;
|
||||
onPopIn: () => void;
|
||||
onPopOut: () => void;
|
||||
onToggleAllExpanded: () => void;
|
||||
}
|
||||
|
||||
export const SubjectLineIcons: React.FunctionComponent<SubjectLineIconsProps> = props => (
|
||||
<div className="message-icons-wrap">
|
||||
{props.canCollapse && (
|
||||
<div onClick={props.onToggleAllExpanded}>
|
||||
<RetinaImg
|
||||
name={props.hasCollapsedItems ? 'expand.png' : 'collapse.png'}
|
||||
title={props.hasCollapsedItems ? localized('Expand All') : localized('Collapse All')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div onClick={props.onPrint}>
|
||||
<RetinaImg
|
||||
name="print.png"
|
||||
title={localized('Print Thread')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
{AppEnv.isThreadWindow() ? (
|
||||
<div onClick={props.onPopIn}>
|
||||
<RetinaImg
|
||||
name="thread-popin.png"
|
||||
title={localized('Pop thread in')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div onClick={props.onPopOut}>
|
||||
<RetinaImg
|
||||
name="thread-popout.png"
|
||||
title={localized('Popout thread')}
|
||||
mode={RetinaImg.Mode.ContentIsMask}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
|
@ -136,6 +136,30 @@ body.platform-win32 {
|
|||
padding: 0;
|
||||
order: 2;
|
||||
|
||||
&.restrict-width {
|
||||
.message-subject-wrap {
|
||||
max-width: @message-max-width;
|
||||
}
|
||||
.message-list-headers {
|
||||
max-width: @message-max-width;
|
||||
}
|
||||
.message-item-wrap {
|
||||
max-width: @message-max-width;
|
||||
}
|
||||
.msg-lines {
|
||||
max-width: @message-max-width;
|
||||
}
|
||||
.message-item-area {
|
||||
max-width: @message-max-width;
|
||||
}
|
||||
.footer-reply-area-wrap {
|
||||
max-width: @message-max-width;
|
||||
}
|
||||
.footer-reply-area {
|
||||
max-width: @message-max-width;
|
||||
}
|
||||
}
|
||||
|
||||
search-match,
|
||||
.search-match {
|
||||
background: @text-color-search-match;
|
||||
|
@ -179,7 +203,6 @@ body.platform-win32 {
|
|||
}
|
||||
|
||||
.message-subject-wrap {
|
||||
max-width: @message-max-width;
|
||||
margin: 5px auto 10px auto;
|
||||
-webkit-user-select: text;
|
||||
line-height: @font-size-large * 1.8;
|
||||
|
@ -215,10 +238,11 @@ body.platform-win32 {
|
|||
.thread-injected-mail-labels {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.message-list-headers {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: @message-max-width;
|
||||
|
||||
display: block;
|
||||
|
||||
.participants {
|
||||
|
@ -250,7 +274,7 @@ body.platform-win32 {
|
|||
.message-item-wrap {
|
||||
transition: height 0.1s;
|
||||
position: relative;
|
||||
max-width: @message-max-width;
|
||||
|
||||
margin: 0 auto;
|
||||
|
||||
.message-item-white-wrap {
|
||||
|
@ -355,7 +379,6 @@ body.platform-win32 {
|
|||
}
|
||||
}
|
||||
.msg-lines {
|
||||
max-width: @message-max-width;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
margin-top: -13px;
|
||||
|
@ -479,7 +502,7 @@ body.platform-win32 {
|
|||
|
||||
.message-item-area {
|
||||
width: 100%;
|
||||
max-width: @message-max-width;
|
||||
|
||||
margin: 0 auto;
|
||||
padding: 0 20px @spacing-standard 20px;
|
||||
|
||||
|
@ -520,7 +543,6 @@ body.platform-win32 {
|
|||
.footer-reply-area-wrap {
|
||||
overflow: hidden;
|
||||
|
||||
max-width: @message-max-width;
|
||||
margin: -3px auto 10px auto;
|
||||
|
||||
position: relative;
|
||||
|
@ -543,7 +565,7 @@ body.platform-win32 {
|
|||
|
||||
.footer-reply-area {
|
||||
width: 100%;
|
||||
max-width: @message-max-width;
|
||||
|
||||
margin: 0 auto;
|
||||
padding: 12px @spacing-standard * 1.5;
|
||||
}
|
||||
|
|
|
@ -114,6 +114,11 @@ export default {
|
|||
default: true,
|
||||
title: localized('Automatically load images in viewed messages'),
|
||||
},
|
||||
restrictMaxWidth: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
title: localized('Restrict width of messages to maximize readability'),
|
||||
},
|
||||
backspaceDelete: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
|
|
|
@ -85,8 +85,10 @@
|
|||
body {
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
max-width: 840px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
&.restrict-width {
|
||||
max-width: 840px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
Loading…
Reference in a new issue