diff --git a/client/source/sass/base/_typography.scss b/client/source/sass/base/_typography.scss index a846cddc..373147a5 100644 --- a/client/source/sass/base/_typography.scss +++ b/client/source/sass/base/_typography.scss @@ -3,6 +3,7 @@ html { color: $foreground; font-family: $font; + font-size: 16px; font-smoothing: antialiased; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; diff --git a/client/source/sass/components/_badge.scss b/client/source/sass/components/_badge.scss index b7e110b3..54a23b57 100644 --- a/client/source/sass/components/_badge.scss +++ b/client/source/sass/components/_badge.scss @@ -7,11 +7,11 @@ $sidebar-filter--count--background--active: #3b586d; border-radius: 100px; color: $sidebar-filter--count--foreground; display: inline-block; - font-size: 0.8em; + font-size: 0.6rem; font-weight: 800; line-height: 1; margin-left: 10px; - padding: 1px 5px; + padding: 2px 5px; transition: background 0.25s; - vertical-align: baseline; + vertical-align: middle; } diff --git a/client/source/sass/components/_directory-tree.scss b/client/source/sass/components/_directory-tree.scss index a9a6390a..e3bd61eb 100644 --- a/client/source/sass/components/_directory-tree.scss +++ b/client/source/sass/components/_directory-tree.scss @@ -1,105 +1,50 @@ -$directory-tree--filename--foreground: #527893; -$directory-tree--directory--foreground: $directory-tree--filename--foreground; -$directory-tree--directory--foreground--open: #719bb7; - -$directory-tree--icon--file: #344b5b; -$directory-tree--icon--folder: rgba(#527893, 0.4); -$directory-tree--icon--folder--active: rgba(#82aac5, 0.5); -$directory-tree--icon--folder--open: rgba(#82aac5, 0.5); - -$directory-tree--file-details--foreground: #2b4456; -$directory-tree--file-details--hover--foreground: #527893; - -$torrent-details--directory-tree--file--hover--background: rgba(#1a2d3c, 0.8); -$torrent-details--directory-tree--file--hover--foreground: #79a7c7; -$torrent-details--directory-tree--file--hover--border: #1f3647; - -$torrent-details--directory-tree--parent-directory--foreground: #3a5c74; -$torrent-details--directory-tree--parent-directory--icon--fill: rgba(#3a5c74, 0.5); - .directory-tree { .icon { display: inline-block; - fill: $directory-tree--icon--folder; height: 14px; margin-right: 6px; + margin-top: -3px; + vertical-align: middle; width: 14px; } &__node { - color: $directory-tree--filename--foreground; - line-height: 1.75; + margin-left: -0px; + margin-right: -0px; + padding: 1px 4px 1px 8px; text-overflow: ellipsis; vertical-align: middle; white-space: nowrap; &--group { - padding-left: 10px; + margin-left: 0; + padding-left: 12px; position: relative; &:after { - background: rgba(#527893, 0.1); content: ''; height: 100%; - left: 5px; + left: 10px; position: absolute; - top: -5px; + top: 0; width: 2px; } } &--directory { - color: $directory-tree--directory--foreground; cursor: pointer; - transition: color 0.125s; - - &.is-expanded { - color: $directory-tree--directory--foreground--open; - font-weight: 500; - - .icon { - fill: $directory-tree--icon--folder--active; - } - } - - .icon { - fill: $directory-tree--icon--folder; - } } .file { align-items: center; display: flex; - line-height: 1.4; - margin-left: -9px; - padding: 2px 0 2px 9px; - transition: background 0.25s, box-shadow 0.25s, color 0.25s; - width: 100%; - - &:hover { - background: $torrent-details--directory-tree--file--hover--background; - box-shadow: - 0 1px 0 $torrent-details--directory-tree--file--hover--border, - 0 -1px 0 $torrent-details--directory-tree--file--hover--border; - color: $torrent-details--directory-tree--file--hover--foreground; - - .file { - - &__detail { - - &--size, - &--priority { - color: $directory-tree--file-details--hover--foreground; - } - } - } - } + width: auto; .icon { - fill: $directory-tree--icon--file; margin-top: -3px; vertical-align: middle; + transition: fill 0.25s; } &__detail { @@ -110,9 +55,8 @@ $torrent-details--directory-tree--parent-directory--icon--fill: rgba(#3a5c74, 0. &--size, &--priority { - color: $directory-tree--file-details--foreground; flex: 0 0 auto; - font-size: 0.95em; + font-size: 0.7rem; text-align: right; transition: color 0.25s; } @@ -134,6 +78,20 @@ $torrent-details--directory-tree--parent-directory--icon--fill: rgba(#3a5c74, 0. &--file-list { margin-bottom: 3px; + padding-left: 0; + } + } + + & > .directory-tree { + + &__tree { + + & > .directory-tree { + + &__node { + padding-left: 0; + } + } } } @@ -143,7 +101,6 @@ $torrent-details--directory-tree--parent-directory--icon--fill: rgba(#3a5c74, 0. text-overflow: ellipsis; .icon { - fill: $torrent-details--directory-tree--parent-directory--icon--fill; margin-right: 8px; vertical-align: middle; } @@ -152,7 +109,7 @@ $torrent-details--directory-tree--parent-directory--icon--fill: rgba(#3a5c74, 0. &__tree { .directory-tree__tree { - padding-left: 6px; + padding-left: 0; } } } diff --git a/client/source/sass/components/_icons.scss b/client/source/sass/components/_icons.scss index 0c3ebf52..38af9ad8 100644 --- a/client/source/sass/components/_icons.scss +++ b/client/source/sass/components/_icons.scss @@ -20,6 +20,34 @@ } } + &--information { + + &__fill { + fill-opacity: 0.05; + } + + &__ring { + fill-opacity: 0.6; + } + } + + &--limits { + + .limits { + + &__bars { + + &--top { + fill-opacity: 0.4; + } + + &--bottom { + fill-opacity: 0.6; + } + } + } + } + &--loading-indicator { .loading-indicator { @@ -41,21 +69,4 @@ } } } - - &--limits { - - .limits { - - &__bars { - - &--top { - fill-opacity: 0.4; - } - - &--bottom { - fill-opacity: 0.6; - } - } - } - } } diff --git a/client/source/sass/components/_modals.scss b/client/source/sass/components/_modals.scss index 8a09e578..23c9a423 100644 --- a/client/source/sass/components/_modals.scss +++ b/client/source/sass/components/_modals.scss @@ -1,5 +1,5 @@ $modal--background: #f7fafc; -$modal--heading--background: #fff; +$modal--heading--background: $white; $modal--heading--foreground: #5c6e80; $modal--heading--border: #dde7ed; @@ -36,11 +36,13 @@ $modal--tabs--padding--right: $spacing-unit * 1/5; $modal--tabs--padding--bottom: $spacing-unit * 2/5; $modal--tabs--padding--left: $spacing-unit * 1/5; -$modal--tabs--padding--vertical--top: $spacing-unit * 2/5; +$modal--tabs--padding--vertical--top: $spacing-unit * 1/5; $modal--tabs--padding--vertical--right: $spacing-unit * 2/5; -$modal--tabs--padding--vertical--bottom: $spacing-unit * 2/5; +$modal--tabs--padding--vertical--bottom: $spacing-unit * 1/5; $modal--tabs--padding--vertical--left: $modal--padding--horizontal; +$modal--tabs--in-body--background: rgba($white, 0.55); + .modal { background: $modal--overlay; height: 100%; @@ -57,7 +59,7 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; &__tabs { color: $modal--tab--foreground; - font-size: 0.65em; + font-size: 0.85rem; font-weight: 400; margin: $modal--tabs--margin--top $modal--tabs--margin--right $modal--tabs--margin--bottom $modal--tabs--margin--left; @@ -73,7 +75,7 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; &:after { bottom: 0; content: ''; - height: 1px; + height: 2px; left: 0; position: absolute; right: 0; @@ -114,16 +116,19 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; font-size: 1.25em; font-weight: 300; line-height: 1; + overflow: hidden; padding: $modal--padding--vertical $modal--padding--horizontal; &.has-tabs { - padding-bottom: 0; + + .modal--tabs-in-header & { + padding-bottom: 0; + } } } &__content { flex: 1 1 auto; - overflow: auto; padding: $modal--content--padding--top $modal--content--padding--right $modal--content--padding--bottom $modal--content--padding--left; &__wrapper { @@ -134,9 +139,11 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; 0 0 35px $modal--content--shadow; display: flex; flex-direction: column; + height: auto; left: 50%; max-height: 80%; max-width: 80%; + overflow: hidden; position: absolute; top: 10%; transform: translate(-50%, 0); @@ -146,7 +153,25 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; &__body { color: $modal--body--foreground; + flex: 1 1 auto; font-size: 0.9em; + overflow: auto; + overflow-x: hidden; + + .modal--tabs-in-body & { + overflow: hidden; + } + } + + &__footer { + padding: 0 $modal--padding--horizontal $modal--padding--vertical $modal--padding--horizontal; + + .modal { + + &__actions { + margin-top: 0; + } + } } &__actions { @@ -189,68 +214,113 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; opacity: 0; } - &--orientation { + &--vertical { - &--vertical { + &.modal { - &.modal__content__wrapper { + &--tabs-in-header { flex-direction: row; + + .modal { + + &__header { + border-radius: $modal--border-radius 0 0 $modal--border-radius; + box-shadow: inset -1px 0 0 $modal--heading--border; + flex-basis: 175px; + padding-bottom: $modal--padding--horizontal; + padding-right: 0; + max-width: 175px; + } + + &__content { + flex: 1 0 auto; + } + } } - .modal { + &--tabs-in-body { - &__header { - border-radius: $modal--border-radius 0 0 $modal--border-radius; - box-shadow: inset -1px 0 0 $modal--heading--border; - flex-basis: 175px; - padding-bottom: $modal--padding--horizontal; - padding-right: 0; - max-width: 175px; + .modal { + + &__body { + display: flex; + flex: 1 1 auto; + flex-direction: row; + } + + &__content { + overflow: auto; + overflow-x: hidden; + } + + &__tabs { + background: $modal--tabs--in-body--background; + box-shadow: inset -1px 0 $modal--heading--border; + flex: 0 0 135px; + margin: 0; + padding-top: $modal--padding--vertical; + } } + } + } - &__tabs { - margin: 10px 0 0 $modal--padding--horizontal * -1; + .modal { - .modal { + &__tabs { + margin: $modal--padding--vertical 0 0 $modal--padding--horizontal * -1; - &__tab { - display: block; - margin-right: 0; - padding: $modal--tabs--padding--vertical--top $modal--tabs--padding--vertical--right $modal--tabs--padding--vertical--bottom $modal--tabs--padding--vertical--left; + .modal { - &:after { - bottom: 0; - content: ''; - height: auto; - left: auto; - position: absolute; - right: 0; - top: 0; - transition: background 0.25s; - width: 1px; - } + &__tab { + display: block; + margin-right: 0; + padding: $modal--tabs--padding--vertical--top $modal--tabs--padding--vertical--right $modal--tabs--padding--vertical--bottom $modal--tabs--padding--vertical--left; + + &:after { + bottom: 0; + content: ''; + height: auto; + left: auto; + position: absolute; + right: 0; + top: 0; + transition: background 0.25s; + width: 2px; } } } + } - &__content { - display: flex; - flex-direction: column; - min-height: 500px; - } + &__body { + display: flex; + flex-direction: column; + } - &__body { - flex: 1 0 auto; - } + &__content { + display: flex; + flex-direction: column; + } - &__actions { - flex: 0 0 auto; - } + &__actions { + flex: 0 0 auto; } } } - &--large { - width: 700px; + &--size { + + &-large { + width: 700px; + + &.modal { + + &__content { + + &__wrapper { + bottom: 10%; + } + } + } + } } } diff --git a/client/source/sass/components/_torrent-details-panel.scss b/client/source/sass/components/_torrent-details-panel.scss index 8d14db21..963e744c 100644 --- a/client/source/sass/components/_torrent-details-panel.scss +++ b/client/source/sass/components/_torrent-details-panel.scss @@ -1,76 +1,55 @@ -$torrent-details--background: saturate(lighten(#0c1b26, 2.5%), 4%); //#162835; -$torrent-details--border: rgba($background, 0.1); - -$torrent-details--header--border: rgba(#040d13, 0.3); -$torrent-details--header--name--foreground: #c7dbeb; +$torrent-details--header--name--foreground: #5b6d7c; $torrent-details--header--tertiary--foreground: #7b9cb4; -$torrent-details--header--progress-bar--track: #2a3e4c; -$torrent-details--header--progress-bar--fill--error: #be2558; -$torrent-details--header--progress-bar--fill--stopped: #7b9cb4; - -$torrent-details--navigation--border: rgba(#040d13, 0.4); -$torrent-details--navigation--item--background--active: rgba(#349cf4, 0.07); -$torrent-details--navigation--item--foreground--active: #349cf4; -$torrent-details--navigation--item--border--active: #349cf4; -$torrent-details--navigation--background: transparent; $torrent-details--content--background: rgba(desaturate(#0c1b26, 15%), 0.4); $torrent-details--table--foreground: #527893; $torrent-details--table--header: rgba(#527893, 0.5); -$torrent-details--table--header--count--background: rgba(#527893, 0.5); -$torrent-details--table--header--count--foreground: #0c1b26; +$torrent-details--table--header--count--background: rgba(#527893, 0.2); +$torrent-details--table--header--count--foreground: #527893; $torrent-details--header--icon--default--fill: rgba(#4d6f87, 0.5); -.application { +$torrent-details--detail--label--foreground: #527893; - &__panel { +$directory-tree--filename--foreground: rgba(#527893, 0.7); +$directory-tree--directory--foreground: #527893; +$directory-tree--directory--foreground--open: darken($directory-tree--directory--foreground, 5%); - &--torrent-details { - background: $torrent-details--background; - } - } -} +$directory-tree--icon--file: rgba(#527893, 0.4); +$directory-tree--icon--folder: rgba(#527893, 0.4); +$directory-tree--icon--folder--active: $directory-tree--icon--folder; +$directory-tree--icon--folder--open: rgba(darken(#527893, 10%), 0.4); + +$directory-tree--file-details--foreground: rgba(#527893, 0.7); +$directory-tree--file-details--hover--foreground: rgba(#527893, 0.8); + +$torrent-details--directory-tree--file--hover--background: rgba($blue, 0.1); +$torrent-details--directory-tree--file--hover--foreground: $blue; +$torrent-details--directory-tree--file--hover--border: rgba($blue, 0.5); + +$torrent-details--directory-tree--parent-directory--foreground: rgba(#527893, 0.6); +$torrent-details--directory-tree--parent-directory--icon--fill: rgba(#527893, 0.5); .torrent-details { - background: $torrent-details--background; - bottom: 0; - box-shadow: -1px 0 0 $torrent-details--border; - display: flex; - flex-direction: column; - font-size: 0.8em; - left: 0; - min-width: 400px; - overflow: auto; - position: absolute; - right: 0; - top: 0; - transition: opacity 1s; - z-index: 2; - - &__wrapper { - flex: 1; - height: 100%; - } - - &__scroll-container { - height: auto !important; - } &__heading { color: $torrent-details--header--name--foreground; - font-size: 1.7em; + font-size: 1.2rem; font-weight: 300; + margin-bottom: $spacing-unit * 2/5; } &__sub-heading { display: flex; - margin-bottom: $spacing-unit * 1/4; + flex-wrap: wrap; + font-weight: 400; + margin-bottom: $spacing-unit * 1/5; &__secondary { color: $torrent-details--header--tertiary--foreground; display: flex; + font-size: 0.85rem; flex: 1 0 auto; &:first-child { @@ -129,56 +108,6 @@ $torrent-details--header--icon--default--fill: rgba(#4d6f87, 0.5); } } - &__header { - box-shadow: 0 1px 0 $torrent-details--header--border; - flex: 0 0 auto; - padding: $spacing-unit * 1/2 $spacing-unit $spacing-unit * 2/3 $spacing-unit; - - &.has-error { - - .progress-bar { - - &__fill { - background: $torrent-details--header--progress-bar--fill--error; - } - } - } - - &.is-stopped { - - .progress-bar { - - &__fill { - background: $torrent-details--header--progress-bar--fill--stopped; - - &__wrapper { - - &:after { - background: $torrent-details--header--progress-bar--track; - } - } - } - - .icon { - fill: $torrent-details--header--progress-bar--fill--stopped; - } - } - } - - .progress-bar { - - &__fill { - - &__wrapper { - - &:after { - background: $torrent-details--header--progress-bar--track; - } - } - } - } - } - &__action { cursor: pointer; transition: color 0.25s; @@ -216,43 +145,8 @@ $torrent-details--header--icon--default--fill: rgba(#4d6f87, 0.5); } } - &__content { - flex: 1; - overflow: auto; - padding: $spacing-unit * 2/3 $spacing-unit; - - &__wrapper { - background: $torrent-details--content--background; - display: flex; - flex: 1; - overflow: hidden; - } - } - - &__navigation { - background: $torrent-details--navigation--background; - box-shadow: 1px 0 0 $torrent-details--navigation--border; - min-width: 125px; - padding: $spacing-unit * 1/2 0; - - .navigation { - - &__item { - cursor: pointer; - padding: $spacing-unit * 1/4 $spacing-unit; - text-align: right; - transition: background 0.25s, box-shadow 0.25s, color 0.25s; - - &.is-active { - background: $torrent-details--navigation--item--background--active; - box-shadow: 1px 0 0 $torrent-details--navigation--item--border--active; - color: $torrent-details--navigation--item--foreground--active; - } - } - } - } - &__section { + font-size: 0.8rem; &__heading, &__null-data { @@ -261,33 +155,123 @@ $torrent-details--header--icon--default--fill: rgba(#4d6f87, 0.5); margin-bottom: $spacing-unit * 1/10; } - &__heading { + &--file-tree { + margin-left: -8px; - .badge { - background: $torrent-details--table--header--count--background; - color: $torrent-details--table--header--count--foreground; + .directory-tree { + color: $directory-tree--filename--foreground; + + &__node { + + &--group { + + &:after { + background: rgba(#527893, 0.1); + } + } + + &--directory { + color: $directory-tree--directory--foreground; + + &.is-expanded { + color: $directory-tree--directory--foreground--open; + font-weight: 500; + + .icon { + fill: $directory-tree--icon--folder--open; + } + } + + .icon { + fill: $directory-tree--icon--folder; + } + } + + .file { + width: auto; + + &__detail { + + &--size, + &--priority { + color: $directory-tree--file-details--foreground; + } + } + + .icon { + fill: $directory-tree--icon--file; + } + } + } + + &__selectable { + position: relative; + transition: background 0.25s, border 0.25s, color 0.25s; + + &:after { + background: rgba($blue, 0); + bottom: 0; + content: ''; + height: auto; + left: -2px; + position: absolute; + top: 0; + transition: background 0.25s; + width: 2px; + } + + &:hover { + background: $torrent-details--directory-tree--file--hover--background; + color: $torrent-details--directory-tree--file--hover--foreground; + + &:after { + background: rgba($blue, 1); + } + + .file { + + &__detail { + + &--size, + &--priority { + color: $directory-tree--file-details--hover--foreground; + } + } + } + + .icon { + fill: $torrent-details--directory-tree--file--hover--foreground; + } + + } + } + + &__parent-directory { + color: $torrent-details--directory-tree--parent-directory--foreground; + + .icon { + fill: $torrent-details--directory-tree--parent-directory--icon--fill; + } + } } + + .icon { + fill: $directory-tree--icon--file; + } + } + + .badge { + background: $torrent-details--table--header--count--background; + color: $torrent-details--table--header--count--foreground; } } &__detail { &__label { - margin-right: 0.5em; - opacity: 0.5; + color: $torrent-details--detail--label--foreground; + font-weight: 500; + margin-right: $spacing-unit * 3/5; } } } - -.torrent-details-enter { - opacity: 0; -} - -.torrent-details-enter-active { - opacity: 1; -} - -.torrent-details-leave { - opacity: 0; - transition: opacity 0.5s; -} diff --git a/client/source/sass/components/_torrents.scss b/client/source/sass/components/_torrents.scss index 3cc21bd4..8b1a1acb 100644 --- a/client/source/sass/components/_torrents.scss +++ b/client/source/sass/components/_torrents.scss @@ -19,6 +19,9 @@ $torrent--background--hover: #f6f8fa; $torrent--background--selected: $blue; $torrent--background--error: #e95779; +$more-info--box-shadow--hue: #1a2f3d; +$more-info--border: $textbox-repeater--button--border; + .torrents { display: flex; flex: 1 1 100px; @@ -91,6 +94,7 @@ $torrent--background--error: #e95779; .torrent { cursor: default; padding: 10px 20px; + overflow: hidden; position: relative; transition: background 0.25s; @@ -101,6 +105,7 @@ $torrent--background--error: #e95779; &__more-info { opacity: 1; + transform: translateX(0); } } } @@ -114,14 +119,38 @@ $torrent--background--error: #e95779; } &__more-info { - height: 20px; - margin-top: -10px; + border-radius: 32px 0 0 32px; + box-shadow: + 0 0 30px rgba($more-info--box-shadow--hue, 0.11), + 0 0 0 1px rgba($more-info--box-shadow--hue, 0.07); + height: 32px; + margin-top: -16px; position: absolute; - left: -7px; opacity: 0; + right: -5px; top: 50%; - transition: background 0.25s, box-shadow 0.25s, opacity 0.25s; - width: 20px; + transform: translateX(15px); + transition: background 0.25s, box-shadow 0.25s, opacity 0.25s, transform 0.25s; + width: 42px; + + .icon { + fill: rgba($foreground, 0.5); + position: absolute; + height: 16px; + left: 16px; + width: 16px; + } + + &:hover { + background: $white; + box-shadow: + 0 0 30px rgba($more-info--box-shadow--hue, 0.2), + 0 0 0 1px rgba($more-info--box-shadow--hue, 0.1); + + .icon { + fill: $blue; + } + } } } diff --git a/client/source/scripts/actions/UIActions.js b/client/source/scripts/actions/UIActions.js index 6581e8f6..cb4efad0 100644 --- a/client/source/scripts/actions/UIActions.js +++ b/client/source/scripts/actions/UIActions.js @@ -27,10 +27,8 @@ const UIActions = { }, dismissModal: () => { + // TODO: Remove this try..catch. try { - // if (AppDispatcher.isDispatching()) { - // AppDispatcher.waitFor([TorrentStore.dispatcherID]); - // } AppDispatcher.dispatchUIAction({ type: ActionTypes.UI_DISPLAY_MODAL, data: null diff --git a/client/source/scripts/app.js b/client/source/scripts/app.js index b8b0b2b9..a4726d5c 100644 --- a/client/source/scripts/app.js +++ b/client/source/scripts/app.js @@ -8,7 +8,6 @@ import Modals from './components/modals/Modals'; import Sidebar from './components/panels/Sidebar'; import SettingsStore from './stores/SettingsStore'; import TorrentActions from './actions/TorrentActions'; -import TorrentDetailsView from './components/panels/TorrentDetailsView'; import TorrentListView from './components/panels/TorrentListView'; class FloodApp extends React.Component { @@ -22,7 +21,6 @@ class FloodApp extends React.Component { - diff --git a/client/source/scripts/components/filesystem/DirectoryFileList.js b/client/source/scripts/components/filesystem/DirectoryFileList.js index 1327f36a..8877b95f 100644 --- a/client/source/scripts/components/filesystem/DirectoryFileList.js +++ b/client/source/scripts/components/filesystem/DirectoryFileList.js @@ -26,6 +26,7 @@ export default class DirectoryFiles extends React.Component { render() { let branch = Object.assign([], this.props.branch); + let fileIcon = ; branch.sort((a, b) => { return a.filename.localeCompare(b.filename); @@ -35,10 +36,10 @@ export default class DirectoryFiles extends React.Component { let fileSize = format.data(file.sizeBytes, '', 1); return ( -
- + {fileIcon} {file.filename}
diff --git a/client/source/scripts/components/filesystem/DirectoryTreeNode.js b/client/source/scripts/components/filesystem/DirectoryTreeNode.js index 8cc43ff3..ba7261b8 100644 --- a/client/source/scripts/components/filesystem/DirectoryTreeNode.js +++ b/client/source/scripts/components/filesystem/DirectoryTreeNode.js @@ -41,7 +41,7 @@ export default class DirectoryTreeNode extends React.Component { render() { let branchClasses = `directory-tree__branch directory-tree__branch--depth-${this.props.depth}`; - let directoryClasses = classnames('directory-tree__node', + let directoryClasses = classnames('directory-tree__node directory-tree__selectable', 'directory-tree__node--directory', {'is-expanded': this.state.expanded} ); diff --git a/client/source/scripts/components/icons/InformationIcon.js b/client/source/scripts/components/icons/InformationIcon.js new file mode 100644 index 00000000..112b7fec --- /dev/null +++ b/client/source/scripts/components/icons/InformationIcon.js @@ -0,0 +1,16 @@ +import React from 'react'; + +import BaseIcon from './BaseIcon'; + +export default class InformationIcon extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/source/scripts/components/modals/AddTorrents.js b/client/source/scripts/components/modals/AddTorrents.js index bf299e66..4776dccb 100644 --- a/client/source/scripts/components/modals/AddTorrents.js +++ b/client/source/scripts/components/modals/AddTorrents.js @@ -13,11 +13,11 @@ export default class AddTorrents extends React.Component { render() { let tabs = { 'by-url': { - content: , + content: AddTorrentsByURL, label: 'By URL' }, 'by-file': { - content: , + content: AddTorrentsByFile, label: 'By File' } }; diff --git a/client/source/scripts/components/modals/ConfirmModal.js b/client/source/scripts/components/modals/ConfirmModal.js index a4c496f8..3d9998df 100644 --- a/client/source/scripts/components/modals/ConfirmModal.js +++ b/client/source/scripts/components/modals/ConfirmModal.js @@ -7,11 +7,19 @@ export default class AddTorrents extends React.Component { super(); } + getContent() { + return ( +
+ {this.props.options.content} +
+ ); + } + render() { return ( ); diff --git a/client/source/scripts/components/modals/Modal.js b/client/source/scripts/components/modals/Modal.js index e4a37119..38f24b89 100644 --- a/client/source/scripts/components/modals/Modal.js +++ b/client/source/scripts/components/modals/Modal.js @@ -38,13 +38,16 @@ export default class Modal extends React.Component { } render() { - let content = this.props.content; let footer = null; let contentClasses = classnames('modal__content__wrapper', - `modal--align-${this.props.alignment}`, { - 'modal--orientation--horizontal': this.props.orientation === 'horizontal', - 'modal--orientation--vertical': this.props.orientation === 'vertical' + `modal--align-${this.props.alignment}`, `modal--size-${this.props.size}`, { + 'modal--horizontal': this.props.orientation === 'horizontal', + 'modal--vertical': this.props.orientation === 'vertical', + 'modal--tabs-in-header': !this.props.tabsInBody, + 'modal--tabs-in-body': this.props.tabsInBody }, this.props.classNames); + let modalBody = [this.props.content]; + let modalHeader = [this.props.heading]; let headerClasses = classnames('modal__header', { 'has-tabs': this.props.tabs }); @@ -52,31 +55,45 @@ export default class Modal extends React.Component { if (this.props.tabs) { let activeTabId = this.getActiveTabId(); + let activeTab = this.props.tabs[activeTabId]; - content = this.props.tabs[activeTabId].content; - tabs = ( - + let ModalContentComponent = activeTab.content; + let modalContentData = activeTab.props; + + let tabs = ( + + ); + + if (this.props.tabsInBody) { + modalBody = [tabs]; + } else { + modalHeader.push(tabs); + } + + modalBody.push( +
+ +
); } if (this.props.actions) { footer = ( - +
+ +
); } return (
- {this.props.heading} - {tabs} + {modalHeader}
-
-
- {content} -
+
+ {modalBody} {footer}
@@ -87,5 +104,7 @@ export default class Modal extends React.Component { Modal.defaultProps = { alignment: 'left', classNames: null, - orientation: 'horizontal' + size: 'medium', + orientation: 'horizontal', + tabsInBody: false }; diff --git a/client/source/scripts/components/modals/Modals.js b/client/source/scripts/components/modals/Modals.js index 4c1c6ee4..19ef4c9c 100644 --- a/client/source/scripts/components/modals/Modals.js +++ b/client/source/scripts/components/modals/Modals.js @@ -8,6 +8,7 @@ import EventTypes from '../../constants/EventTypes'; import Modal from './Modal'; import MoveTorrents from './MoveTorrents'; import SettingsModal from './SettingsModal'; +import TorrentDetailsModal from './TorrentDetailsModal'; import UIActions from '../../actions/UIActions'; import UIStore from '../../stores/UIStore'; @@ -25,6 +26,7 @@ export default class Modals extends React.Component { confirm: ConfirmModal, 'move-torrents': MoveTorrents, 'add-torrents': AddTorrents, + 'torrent-details': TorrentDetailsModal, 'settings': SettingsModal }; diff --git a/client/source/scripts/components/modals/SettingsModal.js b/client/source/scripts/components/modals/SettingsModal.js index d5865f5a..daf533d6 100644 --- a/client/source/scripts/components/modals/SettingsModal.js +++ b/client/source/scripts/components/modals/SettingsModal.js @@ -110,16 +110,17 @@ export default class AddTorrents extends React.Component { render() { let tabs = { 'speed-limit': { - content: ( - - ), + content: SettingsSpeedLimit, + props: { + onSettingsChange: this.handleSettingsChange, + settings: this.state.settings + }, label: 'Speed Limits' } }; return ( - ); diff --git a/client/source/scripts/components/modals/TorrentDetailsModal.js b/client/source/scripts/components/modals/TorrentDetailsModal.js new file mode 100644 index 00000000..5f0bfd7e --- /dev/null +++ b/client/source/scripts/components/modals/TorrentDetailsModal.js @@ -0,0 +1,106 @@ +import React from 'react'; + +import Modal from './Modal'; +import EventTypes from '../../constants/EventTypes'; +import TorrentFiles from '../torrent-details/TorrentFiles'; +import TorrentGeneralInfo from '../torrent-details/TorrentGeneralInfo'; +import TorrentHeading from '../torrent-details/TorrentHeading'; +import TorrentPeers from '../torrent-details/TorrentPeers'; +import TorrentStore from '../../stores/TorrentStore'; +import TorrentTrackers from '../torrent-details/TorrentTrackers'; +import UIActions from '../../actions/UIActions'; +import UIStore from '../../stores/UIStore'; + +const METHODS_TO_BIND = ['onTorrentDetailsChange', 'onReceiveTorrentsSuccess']; + +export default class TorrentDetailsModal extends React.Component { + constructor() { + super(...arguments); + + this.state = { + torrentDetails: null + }; + + METHODS_TO_BIND.forEach((method) => { + this[method] = this[method].bind(this); + }); + } + + componentWillMount() { + this.setState({ + torrent: TorrentStore.getTorrent(UIStore.getTorrentDetailsHash()), + torrentDetails: TorrentStore.getTorrentDetails(UIStore.getTorrentDetailsHash()) + }); + } + + componentDidMount() { + TorrentStore.listen(EventTypes.CLIENT_TORRENT_DETAILS_CHANGE, this.onTorrentDetailsChange); + TorrentStore.listen(EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS, this.onReceiveTorrentsSuccess); + TorrentStore.fetchTorrentDetails(); + } + + componentWillUnmount() { + TorrentStore.unlisten(EventTypes.CLIENT_TORRENT_DETAILS_CHANGE, this.onTorrentDetailsChange); + TorrentStore.unlisten(EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS, this.onReceiveTorrentsSuccess); + TorrentStore.stopPollingTorrentDetails(); + } + + onReceiveTorrentsSuccess() { + this.setState({ + torrent: TorrentStore.getTorrent(UIStore.getTorrentDetailsHash()) + }); + } + + onTorrentDetailsChange() { + this.setState({ + torrentDetails: TorrentStore.getTorrentDetails(UIStore.getTorrentDetailsHash()) + }); + } + + dismissModal() { + UIActions.dismissModal(); + } + + getModalHeading() { + return ( + + ); + } + + render() { + let props = { + ...this.props.options, + torrent: this.state.torrent, + ...this.state.torrentDetails + }; + + let tabs = { + 'torrent-details': { + content: TorrentGeneralInfo, + label: 'Details', + props + }, + 'torrent-files': { + content: TorrentFiles, + label: 'Files', + props + }, + 'torrent-peers': { + content: TorrentPeers, + label: 'Peers', + props + }, + 'torrent-trackers': { + content: TorrentTrackers, + label: 'Trackers', + props + } + }; + + return ( + + ); + } +} diff --git a/client/source/scripts/components/panels/TorrentDetailsView.js b/client/source/scripts/components/panels/TorrentDetailsView.js deleted file mode 100644 index ac5908f6..00000000 --- a/client/source/scripts/components/panels/TorrentDetailsView.js +++ /dev/null @@ -1,173 +0,0 @@ -import _ from 'lodash'; -import classNames from 'classnames'; -import React from 'react'; -import CSSTransitionGroup from 'react-addons-css-transition-group'; - -import ApplicationPanel from '../layout/ApplicationPanel'; -import CustomScrollbars from '../ui/CustomScrollbars'; -import EventTypes from '../../constants/EventTypes'; -import NavigationList from '../ui/NavigationList'; -import TorrentActions from '../../actions/TorrentActions'; -import TorrentFiles from '../torrent-details/TorrentFiles'; -import TorrentGeneralInfo from '../torrent-details/TorrentGeneralInfo'; -import TorrentHeading from '../torrent-details/TorrentHeading'; -import TorrentPeers from '../torrent-details/TorrentPeers'; -import TorrentStore from '../../stores/TorrentStore'; -import TorrentTrackers from '../torrent-details/TorrentTrackers'; -import UIStore from '../../stores/UIStore'; - -const METHODS_TO_BIND = [ - 'handleNavChange', - 'onTorrentDetailsHashChange', - 'onOpenChange', - 'onReceiveTorrentsSuccess', - 'onTorrentDetailsChange' -]; - -export default class TorrentDetails extends React.Component { - constructor() { - super(); - - this.state = { - isOpen: false, - torrentDetailsSuccess: false, - torrentDetailsError: false, - selectedTorrent: {}, - selectedTorrentHash: null, - torrentDetails: {}, - torrentDetailsPane: 'general' - }; - - METHODS_TO_BIND.forEach((method) => { - this[method] = this[method].bind(this); - }); - } - - componentDidMount() { - TorrentStore.listen(EventTypes.CLIENT_TORRENT_DETAILS_CHANGE, this.onTorrentDetailsChange); - TorrentStore.listen(EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS, this.onReceiveTorrentsSuccess); - UIStore.listen(EventTypes.UI_TORRENT_DETAILS_OPEN_CHANGE, this.onOpenChange); - UIStore.listen(EventTypes.UI_TORRENT_DETAILS_HASH_CHANGE, this.onTorrentDetailsHashChange); - } - - componentWillUpdate(nextProps, nextState) { - if (nextState.isOpen && !this.state.isOpen) { - TorrentStore.fetchTorrentDetails(); - } else if (!nextState.isOpen) { - TorrentStore.stopPollingTorrentDetails(); - } - } - - componentWillUnmount() { - TorrentStore.unlisten(EventTypes.CLIENT_TORRENT_DETAILS_CHANGE, this.onTorrentDetailsChange); - TorrentStore.unlisten(EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS, this.onReceiveTorrentsSuccess); - UIStore.unlisten(EventTypes.UI_TORRENT_DETAILS_OPEN_CHANGE, this.onOpenChange); - UIStore.unlisten(EventTypes.UI_TORRENT_DETAILS_HASH_CHANGE, this.onTorrentDetailsHashChange); - } - - onReceiveTorrentsSuccess() { - this.forceUpdate(); - } - - onTorrentDetailsHashChange() { - if (this.state.isOpen) { - TorrentStore.fetchTorrentDetails(UIStore.getTorrentDetailsHash()); - } - } - - onOpenChange() { - this.setState({ - isOpen: UIStore.isTorrentDetailsOpen() - }); - } - - onTorrentDetailsChange() { - this.setState({ - torrentDetails: TorrentStore.getTorrentDetails(UIStore.getTorrentDetailsHash()) - }); - } - - getNavigationItem(item) { - let torrentDetails = this.state.torrentDetails || {}; - let selectedHash = UIStore.getTorrentDetailsHash(); - let torrent = TorrentStore.getTorrent(selectedHash); - - switch (item) { - case 'general': - return ; - case 'files': - return ; - break; - case 'trackers': - return ; - break; - case 'peers': - return ; - break; - } - - return null; - } - - getNavigationItems() { - return [ - { - slug: 'general', - label: 'General' - }, - { - slug: 'files', - label: 'Files' - }, - { - slug: 'peers', - label: 'Peers' - }, - { - slug: 'trackers', - label: 'Trackers' - } - ]; - } - - handleNavChange(item) { - this.setState({torrentDetailsPane: item.slug}); - } - - render() { - let detailContent = null; - - if (this.state.isOpen) { - let selectedHash = UIStore.getTorrentDetailsHash(); - let torrent = TorrentStore.getTorrent(selectedHash); - - detailContent = ( -
- -
- - -
- {this.getNavigationItem(this.state.torrentDetailsPane)} -
-
-
-
- ); - } - - return ( - - - {detailContent} - - - ); - } -} diff --git a/client/source/scripts/components/torrent-details/NavigationList.js b/client/source/scripts/components/torrent-details/NavigationList.js index a42a0a48..2e93a01e 100644 --- a/client/source/scripts/components/torrent-details/NavigationList.js +++ b/client/source/scripts/components/torrent-details/NavigationList.js @@ -3,7 +3,7 @@ import React from 'react'; class NavigationList extends React.Component { constructor() { - constructor(); + super(); this.state = { selectedItem: null diff --git a/client/source/scripts/components/torrent-details/TorrentFiles.js b/client/source/scripts/components/torrent-details/TorrentFiles.js index 2b2e8831..58e1ad23 100644 --- a/client/source/scripts/components/torrent-details/TorrentFiles.js +++ b/client/source/scripts/components/torrent-details/TorrentFiles.js @@ -71,7 +71,7 @@ export default class TorrentFiles extends React.Component { } return ( -
+
{directoryHeading} {fileInfo}
diff --git a/client/source/scripts/components/torrent-details/TorrentGeneralInfo.js b/client/source/scripts/components/torrent-details/TorrentGeneralInfo.js index 29a967d6..dca3e0ae 100644 --- a/client/source/scripts/components/torrent-details/TorrentGeneralInfo.js +++ b/client/source/scripts/components/torrent-details/TorrentGeneralInfo.js @@ -17,7 +17,7 @@ export default class TorrentGeneralInfo extends React.Component { let freeDiskSpace = format.data(torrent.freeDiskSpace); return ( -
    +
    • Size {totalSize.value} diff --git a/client/source/scripts/components/torrent-list/Torrent.js b/client/source/scripts/components/torrent-list/Torrent.js index 5582e10d..48c0488e 100644 --- a/client/source/scripts/components/torrent-list/Torrent.js +++ b/client/source/scripts/components/torrent-list/Torrent.js @@ -4,10 +4,10 @@ import React from 'react'; import CalendarIcon from '../icons/CalendarIcon'; import ClockIcon from '../icons/ClockIcon'; import DiskIcon from '../icons/DiskIcon'; -import DotsMini from '../icons/DotsMini'; import DownloadThickIcon from '../icons/DownloadThickIcon'; import EventTypes from '../../constants/EventTypes'; import format from '../../util/formatData'; +import InformationIcon from '../icons/InformationIcon'; import PeersIcon from '../icons/PeersIcon'; import ProgressBar from '../ui/ProgressBar'; import RatioIcon from '../icons/RatioIcon'; @@ -141,7 +141,7 @@ export default class Torrent extends React.Component {
    • ); diff --git a/client/source/scripts/components/torrent-list/TorrentList.js b/client/source/scripts/components/torrent-list/TorrentList.js index 2748eddc..7749dd47 100644 --- a/client/source/scripts/components/torrent-list/TorrentList.js +++ b/client/source/scripts/components/torrent-list/TorrentList.js @@ -163,6 +163,11 @@ export default class TorrentListContainer extends React.Component { hash: torrent.hash, event }); + + UIActions.displayModal({ + id: 'torrent-details', + options: {hash: torrent.hash} + }); } handleRightClick(torrent, event) { @@ -213,10 +218,6 @@ export default class TorrentListContainer extends React.Component { } onTorrentSelectionChange() { - if (TorrentStore.getSelectedTorrents().length !== 1) { - UIStore.closeTorrentDetailsPanel(); - } - this.forceUpdate(); } diff --git a/client/source/scripts/stores/TorrentStore.js b/client/source/scripts/stores/TorrentStore.js index 0e6a711a..7d9c67c5 100644 --- a/client/source/scripts/stores/TorrentStore.js +++ b/client/source/scripts/stores/TorrentStore.js @@ -76,7 +76,7 @@ class TorrentStoreClass extends BaseStore { } getTorrentDetails(hash) { - return this.torrents[hash].details || {}; + return this.torrents[hash].details || null; } getSelectedTorrents() { @@ -151,13 +151,12 @@ class TorrentStoreClass extends BaseStore { setTorrentDetails(hash, torrentDetails) { this.torrents[hash].details = torrentDetails; - this.emit(EventTypes.CLIENT_TORRENT_DETAILS_CHANGE); this.resolveRequest('fetch-torrent-details'); + this.emit(EventTypes.CLIENT_TORRENT_DETAILS_CHANGE); } sortTorrents(torrents) { let torrentsSort = TorrentFilterStore.getTorrentsSort(); - this.torrents = torrents; // Convert torrents hash to array and sort it. diff --git a/client/source/scripts/stores/UIStore.js b/client/source/scripts/stores/UIStore.js index 054225e6..0ee38ba0 100644 --- a/client/source/scripts/stores/UIStore.js +++ b/client/source/scripts/stores/UIStore.js @@ -8,22 +8,22 @@ import TorrentStore from './TorrentStore'; class UIStoreClass extends BaseStore { constructor() { - super(); + super(...arguments); this.activeContextMenu = null; this.activeModal = null; this.dependencies = []; this.latestTorrentLocation = null; this.torrentDetailsHash = null; - this.torrentDetailsOpen = false; + // this.torrentDetailsOpen = false; } - closeTorrentDetailsPanel() { - if (this.torrentDetailsOpen) { - this.torrentDetailsOpen = false; - this.emit(EventTypes.UI_TORRENT_DETAILS_OPEN_CHANGE); - } - } + // closeTorrentDetailsPanel() { + // if (this.torrentDetailsOpen) { + // this.torrentDetailsOpen = false; + // this.emit(EventTypes.UI_TORRENT_DETAILS_OPEN_CHANGE); + // } + // } getActiveContextMenu() { return this.activeContextMenu; @@ -55,9 +55,9 @@ class UIStoreClass extends BaseStore { return this.dependencies.length === 0; } - isTorrentDetailsOpen() { - return this.torrentDetailsOpen; - } + // isTorrentDetailsOpen() { + // return this.torrentDetailsOpen; + // } registerDependency(ids) { if (!Array.isArray(ids)) { @@ -104,9 +104,9 @@ UIStore.dispatcherID = AppDispatcher.register((payload) => { const {action, source} = payload; switch (action.type) { - case ActionTypes.UI_CLICK_TORRENT_DETAILS: - UIStore.handleTorrentDetailsClick(action.data.hash, action.data.event); - break; + // case ActionTypes.UI_CLICK_TORRENT_DETAILS: + // UIStore.handleTorrentDetailsClick(action.data.hash, action.data.event); + // break; case ActionTypes.UI_CLICK_TORRENT: UIStore.handleTorrentClick(action.data.hash); break; diff --git a/server/db/settings/settings.db b/server/db/settings/settings.db index cc4bb526..cea0b6ac 100644 --- a/server/db/settings/settings.db +++ b/server/db/settings/settings.db @@ -2,3 +2,5 @@ {"id":"torrentDestination","data":"/Users/john/Music/Ween","_id":"jc4oGMCYe8jpyBYE"} {"id":"startTorrents","data":false,"_id":"kaW53bQDKgERpgXm"} {"id":"speedLimits","data":{"download":[1024,10240,102400,512000,1048576,2097152,5242880,10485760,0],"upload":[1024,10240,102400,512000,1048576,2097152,5242880,10485760,0]},"_id":"nyOO9DKRnfjktreF"} +{"id":"startTorrentsOnLoad","data":false,"_id":"FsReCALg9EWp5AZw"} +{"id":"torrentDestination","data":"/Users/john/Music/fonts","_id":"jc4oGMCYe8jpyBYE"}