diff --git a/.flowconfig b/.flowconfig index 18c5bea0e..413e454bf 100644 --- a/.flowconfig +++ b/.flowconfig @@ -20,5 +20,6 @@ module.name_mapper='^page\(.*\)$' -> '/src/renderer/page\1' module.name_mapper='^lbry\(.*\)$' -> '/src/renderer/lbry\1' module.name_mapper='^rewards\(.*\)$' -> '/src/renderer/rewards\1' module.name_mapper='^modal\(.*\)$' -> '/src/renderer/modal\1' +module.name_mapper='^app\(.*\)$' -> '/src/renderer/app\1' [strict] diff --git a/flow-typed/react-markdown.js b/flow-typed/react-markdown.js new file mode 100644 index 000000000..63b7f2aad --- /dev/null +++ b/flow-typed/react-markdown.js @@ -0,0 +1,3 @@ +declare module 'react-markdown' { + declare module.exports: any; +} diff --git a/flow-typed/render-media.js b/flow-typed/render-media.js new file mode 100644 index 000000000..acb885cc8 --- /dev/null +++ b/flow-typed/render-media.js @@ -0,0 +1,3 @@ +declare module 'render-media' { + declare module.exports: any; +} diff --git a/src/renderer/component/app/view.jsx b/src/renderer/component/app/view.jsx index 9669a7795..3368df8d7 100644 --- a/src/renderer/component/app/view.jsx +++ b/src/renderer/component/app/view.jsx @@ -53,7 +53,7 @@ class App extends React.PureComponent { const { currentStackIndex: prevStackIndex } = prevProps; const { currentStackIndex, currentPageAttributes } = this.props; - if (this.mainContent && currentStackIndex !== prevStackIndex) { + if (this.mainContent && currentStackIndex !== prevStackIndex && currentPageAttributes) { this.mainContent.scrollTop = currentPageAttributes.scrollY || 0; } } diff --git a/src/renderer/component/common.js b/src/renderer/component/common.js index e9f3c4342..43d0c69a5 100644 --- a/src/renderer/component/common.js +++ b/src/renderer/component/common.js @@ -43,62 +43,4 @@ export class CurrencySymbol extends React.PureComponent { return LBC; } } - -export class Thumbnail extends React.PureComponent { - static propTypes = { - src: PropTypes.string, - }; - - handleError() { - if (this.state.imageUrl != this._defaultImageUri) { - this.setState({ - imageUri: this._defaultImageUri, - }); - } - } - - constructor(props) { - super(props); - - this._defaultImageUri = lbry.imagePath('default-thumb.svg'); - this._maxLoadTime = 10000; - this._isMounted = false; - - this.state = { - imageUri: this.props.src || this._defaultImageUri, - }; - } - - componentDidMount() { - this._isMounted = true; - setTimeout(() => { - if (this._isMounted && !this.refs.img.complete) { - this.setState({ - imageUri: this._defaultImageUri, - }); - } - }, this._maxLoadTime); - } - - componentWillUnmount() { - this._isMounted = false; - } - - render() { - const className = this.props.className ? this.props.className : '', - otherProps = Object.assign({}, this.props); - delete otherProps.className; - return ( - { - this.handleError(); - }} - {...otherProps} - className={className} - src={this.state.imageUri} - /> - ); - } -} /* eslint-enable */ diff --git a/src/renderer/component/common/form.jsx b/src/renderer/component/common/form.jsx index fe0cf8d3d..c422e4b7f 100644 --- a/src/renderer/component/common/form.jsx +++ b/src/renderer/component/common/form.jsx @@ -2,15 +2,23 @@ /* eslint-disable react/no-multi-comp */ import * as React from 'react'; import Button from 'component/link'; +import classnames from 'classnames'; type FormRowProps = { children: React.Node, + padded?: boolean, }; -export const FormRow = (props: FormRowProps) => { - const { children } = props; - return
{children}
; -}; +export class FormRow extends React.PureComponent { + static defaultProps = { + padded: false, + }; + + render() { + const { children, padded } = this.props; + return
{children}
; + } +} type FormFieldProps = { render: () => React.Node, @@ -18,18 +26,18 @@ type FormFieldProps = { prefix?: string, postfix?: string, error?: string | boolean, + helper?: string | React.Node, }; export class FormField extends React.PureComponent { render() { - const { render, label, prefix, postfix, error } = this.props; + const { render, label, prefix, postfix, error, helper } = this.props; + /* eslint-disable jsx-a11y/label-has-for */ + // Will come back to this on the settings page + // Need htmlFor on the label return (
- {label && ( - - )} + {label && }
{prefix && {prefix}} {render()} @@ -40,8 +48,10 @@ export class FormField extends React.PureComponent { {typeof error === 'string' ? error : __('There was an error')}
)} + {helper &&
{helper}
}
); + /* eslint-enable jsx-a11y/label-has-for */ } } diff --git a/src/renderer/component/common/spinner.jsx b/src/renderer/component/common/spinner.jsx index 14938f9b9..784d71d72 100644 --- a/src/renderer/component/common/spinner.jsx +++ b/src/renderer/component/common/spinner.jsx @@ -1,14 +1,14 @@ +// @flow import React from 'react'; -import classnames from 'classnames'; -export default ({ dark, className }) => ( -
+const Spinner = () => ( +
+
+
+
+
+
+
); + +export default Spinner; diff --git a/src/renderer/component/common/thumbnail.jsx b/src/renderer/component/common/thumbnail.jsx new file mode 100644 index 000000000..30fea5b77 --- /dev/null +++ b/src/renderer/component/common/thumbnail.jsx @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import classnames from 'classnames'; + +type Props = { + src: string, + shouldObscure: boolean, + className: ?string, +}; + +const Thumbnail = (props: Props) => { + const { className, src, shouldObscure } = props; + + return ( + {__('Image + ); +}; + +export default Thumbnail; diff --git a/src/renderer/component/fileActions/view.jsx b/src/renderer/component/fileActions/view.jsx index b2d83f3b9..1d1dcbf71 100644 --- a/src/renderer/component/fileActions/view.jsx +++ b/src/renderer/component/fileActions/view.jsx @@ -1,48 +1,53 @@ +// @flow import React from 'react'; -import Link from 'component/link'; +import Button from 'component/link'; import FileDownloadLink from 'component/fileDownloadLink'; import * as modals from 'constants/modal_types'; +import classnames from 'classnames'; -class FileActions extends React.PureComponent { +type FileInfo = { + claim_id: string, +}; + +type Props = { + uri: string, + openModal: (string, any) => void, + claimIsMine: boolean, + fileInfo: FileInfo, + vertical?: boolean, // should the buttons be stacked vertically? +}; + +class FileActions extends React.PureComponent { render() { - const { fileInfo, uri, openModal, claimIsMine } = this.props; + const { fileInfo, uri, openModal, claimIsMine, vertical } = this.props; - const claimId = fileInfo ? fileInfo.claim_id : null, - showDelete = fileInfo && Object.keys(fileInfo).length > 0; + const claimId = fileInfo ? fileInfo.claim_id : ''; + // showDelete = fileInfo && Object.keys(fileInfo).length > 0; + const showDelete = true; return ( -
+
{showDelete && ( - openModal(modals.CONFIRM_FILE_REMOVE, { uri })} /> )} {!claimIsMine && ( - )} - + {claimIsMine && ( - void, + contentType: string, +}; -class FileDetails extends React.PureComponent { - render() { - const { claim, contentType, fileInfo, metadata, openFolder, uri } = this.props; - - if (!claim || !metadata) { - return ( -
- {__('Empty claim or metadata info.')} -
- ); - } - - const { description, language, license } = metadata; - const mediaType = lbry.getMediaType(contentType); - - const downloadPath = fileInfo ? path.normalize(fileInfo.download_path) : null; +const FileDetails = (props: Props) => { + const { claim, contentType, fileInfo, metadata, openFolder } = props; + if (!claim || !metadata) { return ( -
-
- -
-
+
+ {__('Empty claim or metadata info.')} +
+ ); + } + + const { description, language, license } = metadata; + const mediaType = lbry.getMediaType(contentType); + + const downloadPath = fileInfo ? path.normalize(fileInfo.download_path) : null; + + return ( +
+
+
About
+
-
- - - - - - - - - - - - - - - {downloadPath && ( - - - - - )} - -
{__('Content-Type')}{mediaType}
{__('Language')}{language}
{__('License')}{license}
{__('Downloaded to')} - openFolder(downloadPath)}>{downloadPath} -
+
Info
+
+
+
{__('Content-Type')}
+
{mediaType}
+
{__('Language')}
+
{language}
+
{__('License')}
+
{license}
+ {downloadPath && ( + +
{__('Downloaded to')}
+
+
+
+ )} +
- ); - } -} +
+ ); +}; export default FileDetails; diff --git a/src/renderer/component/fileDownloadLink/view.jsx b/src/renderer/component/fileDownloadLink/view.jsx index 71cf39018..d3da8c581 100644 --- a/src/renderer/component/fileDownloadLink/view.jsx +++ b/src/renderer/component/fileDownloadLink/view.jsx @@ -1,3 +1,5 @@ +// I'll come back to this +/* eslint-disable */ import React from 'react'; import { BusyMessage } from 'component/common'; import Icon from 'component/common/icon'; @@ -81,9 +83,8 @@ class FileDownloadLink extends React.PureComponent { } return ( { purchaseUri(uri); @@ -91,15 +92,7 @@ class FileDownloadLink extends React.PureComponent { /> ); } else if (fileInfo && fileInfo.download_path) { - return ( - openFile()} - /> - ); + return openFile()} />; } return null; @@ -107,3 +100,4 @@ class FileDownloadLink extends React.PureComponent { } export default FileDownloadLink; +/* eslint-enable */ diff --git a/src/renderer/component/filePrice/view.jsx b/src/renderer/component/filePrice/view.jsx index 7c4d6836d..090c93126 100644 --- a/src/renderer/component/filePrice/view.jsx +++ b/src/renderer/component/filePrice/view.jsx @@ -38,7 +38,7 @@ class FilePrice extends React.PureComponent { const isEstimate = costInfo ? !costInfo.includesData : false; if (!costInfo) { - return ???; + return PRICE; } return ( diff --git a/src/renderer/component/link/view.jsx b/src/renderer/component/link/view.jsx index f53fc8723..cd962b552 100644 --- a/src/renderer/component/link/view.jsx +++ b/src/renderer/component/link/view.jsx @@ -25,6 +25,7 @@ type Props = { noStyle: ?boolean, noUnderline: ?boolean, description: ?string, + secondary: ?boolean, }; const Button = (props: Props) => { @@ -49,6 +50,7 @@ const Button = (props: Props) => { description, noStyle, noUnderline, + secondary, ...otherProps } = props; @@ -58,7 +60,8 @@ const Button = (props: Props) => { ? 'btn--no-style' : { 'btn--link': fakeLink, - 'btn--primary': !alt && !fakeLink, + 'btn--primary': !alt && !fakeLink && !secondary, // default to primary + 'btn--secondary': secondary, 'btn--alt': alt, 'btn--inverse': inverse, 'btn--disabled': disabled, diff --git a/src/renderer/component/subscribeButton/view.jsx b/src/renderer/component/subscribeButton/view.jsx index 216b0bf72..e251b40d6 100644 --- a/src/renderer/component/subscribeButton/view.jsx +++ b/src/renderer/component/subscribeButton/view.jsx @@ -1,7 +1,23 @@ +// @flow import React from 'react'; -import Link from 'component/link'; +import Button from 'component/link'; +import type { Subscription } from 'redux/reducers/subscriptions'; -export default ({ channelName, uri, subscriptions, doChannelSubscribe, doChannelUnsubscribe }) => { +type SubscribtionArgs = { + channelName: string, + uri: string, +}; + +type Props = { + channelName: ?string, + uri: ?string, + subscriptions: Array, + doChannelSubscribe: ({ channelName: string, uri: string }) => void, + doChannelUnsubscribe: SubscribtionArgs => void, +}; + +export default (props: Props) => { + const { channelName, uri, subscriptions, doChannelSubscribe, doChannelUnsubscribe } = props; const isSubscribed = subscriptions.map(subscription => subscription.channelName).indexOf(channelName) !== -1; @@ -10,18 +26,16 @@ export default ({ channelName, uri, subscriptions, doChannelSubscribe, doChannel const subscriptionLabel = isSubscribed ? __('Unsubscribe') : __('Subscribe'); return channelName && uri ? ( -
- - subscriptionHandler({ - channelName, - uri, - }) - } - /> -
+
@@ -69,4 +95,3 @@ class WalletSendTip extends React.PureComponent { } export default WalletSendTip; -/* eslint-enable */ diff --git a/src/renderer/component/wunderbar/view.jsx b/src/renderer/component/wunderbar/view.jsx index 0abdd43dc..2ba5388a2 100644 --- a/src/renderer/component/wunderbar/view.jsx +++ b/src/renderer/component/wunderbar/view.jsx @@ -29,7 +29,7 @@ class WunderBar extends React.PureComponent { } handleChange(e: SyntheticInputEvent<*>) { - const { updateSearchQuery, getSearchSuggestions } = this.props; + const { updateSearchQuery } = this.props; const { value } = e.target; updateSearchQuery(value); @@ -74,7 +74,6 @@ class WunderBar extends React.PureComponent { input: ?HTMLInputElement; throttledGetSearchSuggestions: string => void; - render() { const { searchQuery, isActive, address, suggestions } = this.props; diff --git a/src/renderer/constants/modal_types.js b/src/renderer/constants/modal_types.js index 19b86238e..dd7f0167d 100644 --- a/src/renderer/constants/modal_types.js +++ b/src/renderer/constants/modal_types.js @@ -14,3 +14,4 @@ export const TRANSACTION_FAILED = 'transaction_failed'; export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required'; export const AFFIRM_PURCHASE = 'affirm_purchase'; export const CONFIRM_CLAIM_REVOKE = 'confirmClaimRevoke'; +export const SEND_TIP = 'sendTip'; diff --git a/src/renderer/modal/modal.js b/src/renderer/modal/modal.jsx similarity index 62% rename from src/renderer/modal/modal.js rename to src/renderer/modal/modal.jsx index 2bae02a6d..e1dc62aa4 100644 --- a/src/renderer/modal/modal.js +++ b/src/renderer/modal/modal.jsx @@ -1,26 +1,36 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +// @flow +/* eslint-disable react/no-multi-comp */ +// These should probably just be combined into one modal component +import * as React from 'react'; import ReactModal from 'react-modal'; -import Link from 'component/link/index'; +import Button from 'component/link/index'; import app from 'app'; -export class Modal extends React.PureComponent { - static propTypes = { - type: PropTypes.oneOf(['alert', 'confirm', 'custom']), - overlay: PropTypes.bool, - onConfirmed: PropTypes.func, - onAborted: PropTypes.func, - confirmButtonLabel: PropTypes.string, - abortButtonLabel: PropTypes.string, - confirmButtonDisabled: PropTypes.bool, - abortButtonDisabled: PropTypes.bool, - }; +type ModalProps = { + type: string, + overlay: boolean, + confirmButtonLabel: string, + abortButtonLabel: string, + confirmButtonDisabled: boolean, + abortButtonDisabled: boolean, + onConfirmed?: any => any, + onAborted?: any => any, + className?: string, + overlayClassName?: string, + children?: React.Node, + extraContent?: React.Node, + expandButtonLabel?: string, + hideButtonLabel?: string, +}; +export class Modal extends React.PureComponent { static defaultProps = { type: 'alert', overlay: true, + /* eslint-disable no-underscore-dangle */ confirmButtonLabel: app.i18n.__('OK'), abortButtonLabel: app.i18n.__('Cancel'), + /* eslint-enable no-underscore-dangle */ confirmButtonDisabled: false, abortButtonDisabled: false, }; @@ -38,20 +48,17 @@ export class Modal extends React.PureComponent { } >
{this.props.children}
- {this.props.type == 'custom' ? null : ( // custom modals define their own buttons -
- +
+
+
+ +
+
@@ -119,4 +162,3 @@ class FilePage extends React.PureComponent { } export default FilePage; -/* eslint-enable */ diff --git a/src/renderer/redux/actions/video.js b/src/renderer/redux/actions/video.js deleted file mode 100644 index 4ede45bd0..000000000 --- a/src/renderer/redux/actions/video.js +++ /dev/null @@ -1,10 +0,0 @@ -// @flow -import * as actions from 'constants/action_types'; -import type { Dispatch } from 'redux/reducers/video'; - -// eslint-disable-next-line import/prefer-default-export -export const setVideoPause = (data: boolean) => (dispatch: Dispatch) => - dispatch({ - type: actions.SET_VIDEO_PAUSE, - data, - }); diff --git a/src/renderer/redux/reducers/content.js b/src/renderer/redux/reducers/content.js index e5242b599..e9cbdff99 100644 --- a/src/renderer/redux/reducers/content.js +++ b/src/renderer/redux/reducers/content.js @@ -3,6 +3,7 @@ import * as ACTIONS from 'constants/action_types'; const reducers = {}; const defaultState = { playingUri: null, + currentlyIsPlaying: false, rewardedContentClaimIds: [], channelClaimCounts: {}, }; diff --git a/src/renderer/redux/reducers/video.js b/src/renderer/redux/reducers/video.js deleted file mode 100644 index 4025c0d77..000000000 --- a/src/renderer/redux/reducers/video.js +++ /dev/null @@ -1,25 +0,0 @@ -// @flow -import * as ACTIONS from 'constants/action_types'; -import { handleActions } from 'util/redux-utils'; - -export type VideoState = { videoPause: boolean }; - -type setVideoPause = { - type: ACTIONS.SET_VIDEO_PAUSE, - data: boolean, -}; - -export type Action = setVideoPause; -export type Dispatch = (action: Action) => any; - -const defaultState = { videoPause: false }; - -export default handleActions( - { - [ACTIONS.SET_VIDEO_PAUSE]: (state: VideoState, action: setVideoPause): VideoState => ({ - ...state, - videoPause: action.data, - }), - }, - defaultState -); diff --git a/src/renderer/redux/selectors/navigation.js b/src/renderer/redux/selectors/navigation.js index d24430fc3..aec05c82f 100644 --- a/src/renderer/redux/selectors/navigation.js +++ b/src/renderer/redux/selectors/navigation.js @@ -42,15 +42,12 @@ export const selectActiveHistoryEntry = createSelector( state => state.stack[state.index] ); -export const selectPageTitle = createSelector( - selectCurrentPage, - (page) => { - switch (page) { - default: - return ''; - } +export const selectPageTitle = createSelector(selectCurrentPage, page => { + switch (page) { + default: + return ''; } -); +}); export const selectNavLinks = createSelector( selectCurrentPage, diff --git a/src/renderer/redux/selectors/search.js b/src/renderer/redux/selectors/search.js index 8eb6c95c0..b69a8d2af 100644 --- a/src/renderer/redux/selectors/search.js +++ b/src/renderer/redux/selectors/search.js @@ -1,8 +1,4 @@ -import { - selectCurrentPage, - selectCurrentParams, - selectPageTitle, -} from 'redux/selectors/navigation'; +import { selectCurrentPage, selectCurrentParams } from 'redux/selectors/navigation'; import { createSelector } from 'reselect'; export const selectState = state => state.search || {}; @@ -26,13 +22,13 @@ export const makeSelectSearchUris = query => export const selectWunderBarAddress = createSelector( selectCurrentPage, - selectPageTitle, selectSearchQuery, - (page, title, query) => { + selectCurrentParams, + (page, query, params) => { // only populate the wunderbar address if we are on the file/channel pages // or show the search query if (page === 'show') { - return title; + return params.uri; } else if (page === 'search') { return query; } diff --git a/src/renderer/redux/selectors/video.js b/src/renderer/redux/selectors/video.js deleted file mode 100644 index 62534be6b..000000000 --- a/src/renderer/redux/selectors/video.js +++ /dev/null @@ -1,6 +0,0 @@ -import { createSelector } from 'reselect'; - -const selectState = state => state.video || {}; - -// eslint-disable-next-line import/prefer-default-export -export const selectVideoPause = createSelector(selectState, state => state.videoPause); diff --git a/src/renderer/scss/_gui.scss b/src/renderer/scss/_gui.scss index c438ffb35..d76e75b96 100644 --- a/src/renderer/scss/_gui.scss +++ b/src/renderer/scss/_gui.scss @@ -6,6 +6,14 @@ font-weight: normal; font-style: normal; text-rendering: optimizeLegibility; + src: url('../../../static/font/metropolis/Metropolis-Medium.woff2') format('woff2'); +} + +@font-face { + font-family: 'Metropolis'; + font-weight: 300; + font-style: normal; + text-rendering: optimizeLegibility; src: url('../../../static/font/metropolis/Metropolis-Regular.woff2') format('woff2'); } @@ -86,12 +94,15 @@ ul { } input { - width: 100%; - cursor: text; + cursor: pointer; border-bottom: var(--input-border-size) solid var(--input-border-color); color: var(--input-color); line-height: 1; + &[type='text'] { + cursor: text; + } + &.input-copyable { background: var(--input-bg); color: var(--input-disabled-color); @@ -101,8 +112,28 @@ input { } } -/* -*/ +dl { + width: 100%; + overflow: hidden; + padding: 0; + margin: 0; + overflow-x: scroll; +} + +dt { + float: left; + width: 20%; + padding: 0; + margin: 0; +} + +dd { + float: left; + width: 80%; + padding: 0; + margin: 0; +} + .page { display: grid; grid-template-rows: var(--header-height) calc(100vh - var(--header-height)); @@ -176,11 +207,11 @@ input { padding: 5px; border-radius: 5px; font-weight: 700; - font-size: 0.7em; + font-size: 0.6em; } .credit-amount--free { - color: var(--color-black); + color: var(--color-primary); background-color: var(--color-secondary); } @@ -204,6 +235,11 @@ input { // font-weight: 700; // } +.divider__horizontal { + border-top: var(--color-divider); + margin: 16px 0; +} + .hidden { display: none; } @@ -259,16 +295,16 @@ input { } } -.sort-section { - display: block; - margin-bottom: $spacing-vertical * 2/3; - - text-align: right; - line-height: 1; - font-size: 0.85em; - color: var(--color-help); -} - -section.section-spaced { - margin-bottom: $spacing-vertical; -} +// .sort-section { +// display: block; +// margin-bottom: $spacing-vertical * 2/3; +// +// text-align: right; +// line-height: 1; +// font-size: 0.85em; +// color: var(--color-help); +// } +// +// section.section-spaced { +// margin-bottom: $spacing-vertical; +// } diff --git a/src/renderer/scss/_reset.scss b/src/renderer/scss/_reset.scss index b93394c96..b9e3eca1c 100644 --- a/src/renderer/scss/_reset.scss +++ b/src/renderer/scss/_reset.scss @@ -68,7 +68,7 @@ select { border: 0 none; } img { - width: auto\9; + width: auto; height: auto; vertical-align: middle; -ms-interpolation-mode: bicubic; diff --git a/src/renderer/scss/_vars.scss b/src/renderer/scss/_vars.scss index d7c120615..32dac4f48 100644 --- a/src/renderer/scss/_vars.scss +++ b/src/renderer/scss/_vars.scss @@ -18,7 +18,7 @@ $width-page-constrained: 800px; --text-color: var(--color-black); --color-brand: #155b4a; - // --color-dark-overlay: rgba(32, 32, 32, 0.9); + --color-dark-overlay: rgba(32, 32, 32, 0.9); --color-help: rgba(0, 0, 0, 0.54); // --color-notice: #8a6d3b; --color-error: #a94442; @@ -32,14 +32,18 @@ $width-page-constrained: 800px; --color-placeholder: #ececec; --color-nav-bg: #f6f6f6; - /* Misc */ + /* Video */ + --height-video-embedded: 450px; + --height-video-embedded-min: 325px; + --width-video-embedded: 700px; + + // --width-video-embedded: var(--height-video-embedded) * 16/9; // --content-max-width: 1000px; // --nsfw-blur-intensity: 20px; - // --height-video-embedded: $width-page-constrained * 9 / 16; /* Font */ --font-size: 16px; - --font-line-height: 1.3333; + --font-line-height: 1.7; --font-size-subtext-multiple: 0.82; /* Shadows */ diff --git a/src/renderer/scss/all.scss b/src/renderer/scss/all.scss index 3293a57f4..541c9ecd1 100644 --- a/src/renderer/scss/all.scss +++ b/src/renderer/scss/all.scss @@ -21,11 +21,9 @@ @import 'component/_pagination.scss'; @import 'component/_markdown-editor.scss'; @import 'component/_scrollbar.scss'; -@import 'component/_tabs.scss'; @import 'component/_divider.scss'; @import 'component/_checkbox.scss'; @import 'component/_radio.scss'; @import 'component/_shapeshift.scss'; @import 'component/_spinner.scss'; @import 'component/_nav.scss'; -@import 'page/_show.scss'; diff --git a/src/renderer/scss/component/_button.scss b/src/renderer/scss/component/_button.scss index 78f8e181b..3a20ca105 100644 --- a/src/renderer/scss/component/_button.scss +++ b/src/renderer/scss/component/_button.scss @@ -1,8 +1,29 @@ -/* -TODO: -Determine [disabled] or .disabled -Add support (probably just get rid of button prefix) -*/ +// This will go away +// It's for the download progress "button" +.faux-button-block { + display: inline-block; + height: var(--button-height); + line-height: var(--button-height); + text-decoration: none; + border: 0 none; + text-align: center; + border-radius: var(--button-radius); + text-transform: uppercase; + .icon { + top: 0em; + } + .icon:first-child { + padding-right: 5px; + } + .icon:last-child { + padding-left: 5px; + } + .icon:only-child { + padding-left: 0; + padding-right: 0; + } +} + .btn { border: none; text-decoration: none; @@ -19,6 +40,7 @@ Add support (probably just get rid of button prefix) align-items: center; justify-content: center; fill: currentColor; // for proper icon color + font-size: 0.8em; &:hover { box-shadow: var(--box-shadow-layer); @@ -71,6 +93,11 @@ Add support (probably just get rid of button prefix) } } +.btn--secondary { + background-color: var(--color-secondary); + color: var(--color-primary); +} + .btn--no-style { font-size: inherit; font-weight: inherit; diff --git a/src/renderer/scss/component/_card.scss b/src/renderer/scss/component/_card.scss index 431d18030..fbdc43dac 100644 --- a/src/renderer/scss/component/_card.scss +++ b/src/renderer/scss/component/_card.scss @@ -6,10 +6,10 @@ user-select: text; display: flex; position: relative; + flex-direction: column; } .card--section { - flex-direction: column; background-color: var(--color-white); padding: $spacing-vertical; margin-top: $spacing-vertical * 2/3; @@ -51,8 +51,27 @@ margin-top: $spacing-vertical * 1/3; } -// TODO: regular .card__title -// maybe not needed? +.card__title-identity--file { + display: flex; + align-items: center; + + .credit-amount, + .icon { + margin-left: $spacing-vertical * 2/3; + } +} + +.card__title-identity-icons { + display: flex; + align-items: center; +} + +.card__title { + font-size: 1.5em; + font-weight: 800; + padding: $spacing-vertical / 3 0; +} + .card__title--small { font-weight: 600; font-size: 0.9em; @@ -64,10 +83,26 @@ padding-top: $spacing-vertical * 1/3; } +.card__subtitle--file { + font-size: 1em; + padding-top: 0; +} + .card-media__internal-links { position: absolute; - top: 5px; - right: 5px; + top: $spacing-vertical * 2/3; + right: $spacing-vertical * 2/3; +} + +// Channel info with buttons on the right side +.card__channel-info { + display: flex; + justify-content: space-between; + align-items: center; + + .card__actions .btn:not(:first-of-type) { + margin-left: $spacing-vertical / 3; + } } .card__content { @@ -75,11 +110,47 @@ margin-bottom: var(--card-margin); } +.card__subtext-title { + color: var(--color-black); + margin-top: $spacing-vertical * 3/2; + font-size: calc(var(--font-size-subtext-multiple) * 1.5em); + font-weight: 700; +} + +.card__subtext { + color: var(--color-grey-dark); + font-size: calc(var(--font-size-subtext-multiple) * 1em); + padding-top: $spacing-vertical * 1/3; + word-break: break-word; + font-weight: 300; +} + .card__actions { margin-top: var(--card-margin); display: flex; } +.card__actions--no-margin { + magin-top: 0; +} + +.card__actions--vertical { + flex-direction: column; + margin-top: 0; + + .btn:not(:first-child) { + margin-top: $spacing-vertical * 2/3; + } +} + +.card__actions--center { + align-items: center; + + .btn { + margin: 0 $spacing-vertical / 3; + } +} + /* .card-row is used on the discover/subscriptions page It is a list of cards that extend past the right edge of the screen @@ -135,3 +206,9 @@ margin-right: $spacing-vertical * 2/3; } } + +// TODO: come back to this +// Do we want an opacity over the image? +.card--obscured { + background-color: #e09898; +} diff --git a/src/renderer/scss/component/_form-field.scss b/src/renderer/scss/component/_form-field.scss index 10cb942a2..5a1ad7406 100644 --- a/src/renderer/scss/component/_form-field.scss +++ b/src/renderer/scss/component/_form-field.scss @@ -5,11 +5,21 @@ .form-field:not(:first-of-type) { padding-left: $spacing-vertical; } + + &.form-row--padded { + padding: $spacing-vertical * 2/3 0; + } } .form-field__wrapper { display: flex; padding: $spacing-vertical / 3 0; + + // Hmm, not sure about this + // without this the checkbox isn't on the same line as other form-field text + input[type='checkbox'] { + margin-top: 5px; + } } .form-field__error { diff --git a/src/renderer/scss/component/_spinner.scss b/src/renderer/scss/component/_spinner.scss index f3e0485f7..5cc228e80 100644 --- a/src/renderer/scss/component/_spinner.scss +++ b/src/renderer/scss/component/_spinner.scss @@ -1,58 +1,43 @@ .spinner { - position: relative; - width: 11em; - height: 11em; - margin: 20px auto; - font-size: 3px; - border-radius: 50%; + margin: $spacing-vertical * 1/3; + width: 50px; + height: 40px; + text-align: center; + font-size: 10px; +} - background: linear-gradient(to right, #fff 10%, rgba(255, 255, 255, 0) 50%); - animation: spin 1.4s infinite linear; - transform: translateZ(0); +.spinner > div { + display: inline-block; + height: 100%; + width: 6px; + margin: 0 2px; + background-color: var(--color-white); + animation: sk-stretchdelay 1.2s infinite ease-in-out; - @keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + &.rect2 { + animation-delay: -1.1s; } - &:before, - &:after { - content: ''; - position: absolute; - top: 0; - left: 0; + &.rect3 { + animation-delay: -1s; } - &:before { - width: 50%; - height: 50%; - background: #fff; - border-radius: 100% 0 0 0; + &.rect4 { + animation-delay: -0.9s; } - &:after { - height: 75%; - width: 75%; - margin: auto; - bottom: 0; - right: 0; - background: #000; - border-radius: 50%; + &.rect5 { + animation-delay: -0.8s; } } -.spinner.spinner--dark { - background: linear-gradient(to right, var(--button-primary-bg) 10%, var(--color-bg) 50%); - - &:before { - background: var(--button-primary-bg); +@keyframes sk-stretchdelay { + 0%, + 40%, + 100% { + transform: scaleY(0.4); } - - &:after { - background: var(--color-bg); + 20% { + transform: scaleY(1); } } diff --git a/src/renderer/scss/component/_tabs.scss b/src/renderer/scss/component/_tabs.scss deleted file mode 100644 index 69f6bc7a3..000000000 --- a/src/renderer/scss/component/_tabs.scss +++ /dev/null @@ -1,64 +0,0 @@ -/* Tabs */ - -nav.sub-header { - text-transform: uppercase; - max-width: $width-page-constrained; - margin-bottom: 40px; - border-bottom: var(--divider); - user-select: none; - > a { - height: 38px; - line-height: 38px; - text-align: center; - font-weight: 500; - text-transform: uppercase; - display: inline-block; - vertical-align: baseline; - margin: 0 12px; - padding: 0 8px; - color: var(--tab-color); - position: relative; - - &:first-child { - margin-left: 0; - } - &:last-child { - margin-right: 0; - } - &.sub-header-selected { - color: var(--tab-active-color); - &:before { - width: 100%; - height: var(--tab-border-size); - background: var(--tab-active-color); - position: absolute; - bottom: 0; - left: 0; - content: ''; - animation-name: activeTab; - animation-duration: var(--animation-duration); - animation-timing-function: var(--animation-style); - } - } - &:hover { - color: var(--tab-active-color); - } - } - - &.sub-header--full-width { - max-width: 100%; - } - - &.sub-header--small-margin { - margin-bottom: $spacing-vertical; - } -} - -@keyframes activeTab { - from { - width: 0; - } - to { - width: 100%; - } -} diff --git a/src/renderer/scss/component/_video.scss b/src/renderer/scss/component/_video.scss index aa449c48e..dcaf16972 100644 --- a/src/renderer/scss/component/_video.scss +++ b/src/renderer/scss/component/_video.scss @@ -1,25 +1,11 @@ -$height-video-embedded: $width-page-constrained * 9 / 16; - -video { - object-fit: contain; - box-sizing: border-box; - max-height: 100%; - max-width: 100%; - background-size: contain; - background-position: center center; - background-repeat: no-repeat; -} - -.video { - background: #000; - color: white; -} - -.video-embedded { - max-width: $width-page-constrained; - max-height: $height-video-embedded; - height: $height-video-embedded; +.video__embedded { position: relative; + background-color: var(--color-black); + min-height: var(--height-video-embedded-min); + position: relative; + display: flex; + align-items: center; + video { height: 100%; width: 100%; @@ -27,57 +13,36 @@ video { top: 0; left: 0; } - &.video--hidden { - height: $height-video-embedded; - } } -.video--obscured .video__cover { - position: relative; - filter: blur(var(--nsfw-blur-intensity)); + +// Video thumbnail with play/download button +.video__cover { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-size: auto 100%; + background-position: center center; + background-repeat: no-repeat; + display: flex; + align-items: center; + justify-content: center; + + &:not(.card--obscured) { + background-color: var(--color-black); + } } .video__loading-screen { height: 100%; display: flex; + flex: 1; + flex-direction: column; justify-content: center; align-items: center; } -.video__loading-status { - padding-top: 20px; - color: white; -} - -.video__cover { - text-align: center; - height: 100%; - width: 100%; - background-size: auto 100%; - background-position: center center; - background-repeat: no-repeat; - position: relative; - .video__play-button { - display: flex; - align-items: center; - justify-content: center; - } -} - -.video__play-button { - position: absolute; - width: 100%; - height: 100%; - cursor: pointer; - display: none; - font-size: $spacing-vertical * 3; - color: white; - z-index: 1; - background: var(--color-dark-overlay); - opacity: 0.6; - left: 0; - top: 0; - &:hover { - opacity: 1; - transition: opacity var(--transition-duration) var(--transition-type); - } +.video__loading-text { + color: var(--color-white); } diff --git a/src/renderer/scss/page/_show.scss b/src/renderer/scss/page/_show.scss deleted file mode 100644 index 88ec2b7dd..000000000 --- a/src/renderer/scss/page/_show.scss +++ /dev/null @@ -1,13 +0,0 @@ -.show-page-media { - text-align: center; - margin-bottom: 16px; - overflow: auto; - img { - max-width: 100%; - } - - iframe { - width: 100%; - min-height: 500px; - } -} diff --git a/src/renderer/store.js b/src/renderer/store.js index ca6db3afd..3937e348b 100644 --- a/src/renderer/store.js +++ b/src/renderer/store.js @@ -96,7 +96,7 @@ const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']); const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']); const persistOptions = { - whitelist: ['claims', 'subscriptions'], + whitelist: ['claims', 'subscriptions', 'navigation'], // Order is important. Needs to be compressed last or other transforms can't // read the data transforms: [saveClaimsFilter, subscriptionsFilter, compressor], @@ -106,7 +106,7 @@ const persistOptions = { window.cacheStore = persistStore(store, persistOptions, err => { if (err) { - console.error('Unable to load saved SETTINGS'); + console.error('Unable to load saved settings'); // eslint-disable-line no-console } });