From a541e937ca8b299092d0dc7f45d8f0b66ecc0131 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 24 Aug 2016 21:08:00 +0200 Subject: [PATCH] More styling for statuses --- .../components/components/avatar.jsx | 17 + .../components/components/display_name.jsx | 22 ++ .../components/relative_timestamp.jsx | 55 +++ .../components/components/status.jsx | 23 +- app/assets/stylesheets/application.scss | 3 +- app/assets/stylesheets/components.scss | 20 + app/assets/stylesheets/dashboard.scss | 356 ------------------ app/assets/stylesheets/home.scss | 11 - app/assets/stylesheets/settings.scss | 3 - app/assets/stylesheets/statuses.scss | 3 - package.json | 1 + 11 files changed, 135 insertions(+), 379 deletions(-) create mode 100644 app/assets/javascripts/components/components/avatar.jsx create mode 100644 app/assets/javascripts/components/components/display_name.jsx create mode 100644 app/assets/javascripts/components/components/relative_timestamp.jsx create mode 100644 app/assets/stylesheets/components.scss delete mode 100644 app/assets/stylesheets/dashboard.scss delete mode 100644 app/assets/stylesheets/home.scss delete mode 100644 app/assets/stylesheets/settings.scss delete mode 100644 app/assets/stylesheets/statuses.scss diff --git a/app/assets/javascripts/components/components/avatar.jsx b/app/assets/javascripts/components/components/avatar.jsx new file mode 100644 index 0000000000..7db3eeff7d --- /dev/null +++ b/app/assets/javascripts/components/components/avatar.jsx @@ -0,0 +1,17 @@ +const Avatar = React.createClass({ + + propTypes: { + src: React.PropTypes.string.isRequired + }, + + render () { + return ( +
+ +
+ ); + } + +}); + +export default Avatar; diff --git a/app/assets/javascripts/components/components/display_name.jsx b/app/assets/javascripts/components/components/display_name.jsx new file mode 100644 index 0000000000..97db317078 --- /dev/null +++ b/app/assets/javascripts/components/components/display_name.jsx @@ -0,0 +1,22 @@ +import ImmutablePropTypes from 'react-immutable-proptypes'; + +const DisplayName = React.createClass({ + propTypes: { + account: ImmutablePropTypes.map.isRequired + }, + + render () { + var displayName = this.props.account.get('display_name', this.props.account.get('username')); + var acct = this.props.account.get('acct'); + var url = this.props.account.get('url'); + + return ( + + {displayName} {acct} + + ); + } + +}); + +export default DisplayName; diff --git a/app/assets/javascripts/components/components/relative_timestamp.jsx b/app/assets/javascripts/components/components/relative_timestamp.jsx new file mode 100644 index 0000000000..3216d0a0d3 --- /dev/null +++ b/app/assets/javascripts/components/components/relative_timestamp.jsx @@ -0,0 +1,55 @@ +import moment from 'moment'; + +moment.updateLocale('en', { + relativeTime : { + future: "in %s", + past: "%s ago", + s: "s", + m: "a minute", + mm: "%dm", + h: "an hour", + hh: "%dh", + d: "a day", + dd: "%dd", + M: "a month", + MM: "%dm", + y: "a year", + yy: "%dy" + } +}); + +const RelativeTimestamp = React.createClass({ + getInitialState () { + return { + text: '' + }; + }, + + propTypes: { + timestamp: React.PropTypes.string.isRequired + }, + + componentWillMount () { + this._updateMomentText(); + this.interval = setInterval(this._updateMomentText, 6000); + }, + + componentWillUnmount () { + clearInterval(this.interval); + }, + + _updateMomentText () { + this.setState({ text: moment(this.props.timestamp).fromNow() }); + }, + + render () { + return ( + + {this.state.text} + + ); + } + +}); + +export default RelativeTimestamp; diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx index 68c1efaad8..e54bc8c042 100644 --- a/app/assets/javascripts/components/components/status.jsx +++ b/app/assets/javascripts/components/components/status.jsx @@ -1,17 +1,32 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; +import Avatar from './avatar'; +import DisplayName from './display_name'; +import RelativeTimestamp from './relative_timestamp'; const Status = React.createClass({ propTypes: { status: ImmutablePropTypes.map.isRequired }, - render: function() { + render () { var content = { __html: this.props.status.get('content') }; + var status = this.props.status; return ( -
-
{this.props.status.getIn(['account', 'username'])}
-
+
+ + +
+
+
+ +
+ + +
+ +
+
); } diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 668b6f90d6..a444a2ff2e 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -357,7 +357,6 @@ body { } } -@import 'home'; @import 'accounts'; @import 'stream_entries'; -@import 'dashboard' +@import 'components' diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss new file mode 100644 index 0000000000..0bec9ba802 --- /dev/null +++ b/app/assets/stylesheets/components.scss @@ -0,0 +1,20 @@ +.status__content { + a { + color: #2b90d9; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + + &.mention { + &:hover { + text-decoration: none; + + span { + text-decoration: underline; + } + } + } + } +} diff --git a/app/assets/stylesheets/dashboard.scss b/app/assets/stylesheets/dashboard.scss deleted file mode 100644 index 983172bd0d..0000000000 --- a/app/assets/stylesheets/dashboard.scss +++ /dev/null @@ -1,356 +0,0 @@ -.dashboard-wrapper { - background: #282c37; - border-radius: 4px; - margin: 20px auto; - width: 940px; - display: flex; - overflow: hidden; - - .dashboard__sidebar { - width: 240px; - border-radius: 4px 0 0 4px; - flex-shrink: 0; - - .dashboard__top-bar { - border-radius: 4px 0 0 0; - } - - ul { - padding: 20px 0; - - a { - display: block; - padding: 7px 20px; - color: #d9e1e8; - text-decoration: none; - font-size: 14px; - font-weight: 400; - - .fa { - display: inline-block; - width: 18px; - text-align: center; - margin-right: 5px; - } - - &:hover { - color: #fff; - background: darken(#282c37, 1%); - } - } - - .active { - a { - background: darken(#282c37, 5%); - border-left: 2px solid #2b90d9; - padding-left: 18px; - } - } - } - } - - .dashboard__current-user { - padding: 20px; - - a { - text-decoration: none; - color: inherit; - outline: 0; - } - - .dashboard__current-user__avatar { - display: block; - width: 50px; - height: 50px; - border-radius: 50px; - float: left; - margin-right: 15px; - } - - .dashboard__current-user__display-name { - font-weight: 500; - font-size: 13px; - color: #d9e1e8; - display: block; - margin-top: 5px; - } - - .dashboard__current-user__username { - font-size: 12px; - display: block; - color: #2b90d9; - } - } - - .dashboard__logo { - color: #2b90d9; - - span { - font-weight: 500; - } - } - - .dashboard__top-bar { - background: #fff; - padding: 20px; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); - color: #282c37; - font-size: 16px; - overflow: hidden; - - &.alternate { - background: lighten(#282c37, 10%); - text-align: center; - } - - ul { - float: right; - list-style: none; - display: block; - - li { - display: inline-block; - margin-left: 7px; - } - } - - a { - color: #9baec8; - text-decoration: none; - } - } - - .dashboard__content { - flex: 1; - background: #d9e1e8; - border-radius: 0 4px 4px 0; - - .dashboard__top-bar { - border-radius: 0 4px 0 0; - } - } -} - -.dashboard__content__content { - padding: 20px; - color: #282c37; - line-height: 18px; - - h3 { - font-size: 14px; - font-weight: 500; - margin-bottom: 15px; - } - - p { - margin-bottom: 15px; - } - - samp { - font-family: 'Roboto Mono', monospace; - } - - ul { - list-style: circle; - padding-left: 15px; - margin-bottom: 15px; - } - - .table { - width: 100%; - - th { - font-weight: 500; - text-align: left; - border-bottom: 1px solid lighten(#282c37, 55%); - } - - th, td { - padding: 5px 0; - line-height: 18px; - } - } - - a { - color: #2b90d9; - text-decoration: underline; - - &:hover { - text-decoration: none; - } - } - - .btn { - display: inline-block; - border: 0; - background: #2b90d9; - border-radius: 16px; - padding: 6px 16px; - font-size: 12px; - font-weight: 500; - color: #fff; - cursor: pointer; - font-family: 'Roboto', sans-serif; - text-decoration: none; - - &:hover { - background: lighten(#2b90d9, 5%); - } - - &.btn-iconized { - font-size: 16px; - font-weight: 400; - width: 24px; - text-align: center; - padding: 10px 7px; - border-radius: 100px; - box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); - } - } - - hr { - clear: both; - border: 0; - padding: 0; - width: 100%; - height: 0; - margin: 30px 0; - } -} - -.simple_form { - .form-actions { - padding-top: 20px; - text-align: center; - } - - .error_notification { - color: #df405a; - font-weight: 500; - margin-bottom: 15px; - } - - .input { - margin-bottom: 15px; - - label { - display: block; - text-transform: uppercase; - font-size: 11px; - font-weight: 500; - margin-bottom: 10px; - } - - input[type=text], input[type=email], input[type=password], textarea { - display: block; - box-sizing: border-box; - width: 100%; - border: 0; - background: transparent; - border-bottom: 1px solid lighten(#282c37, 55%); - padding: 5px 0; - outline: 0; - padding-bottom: 6px; - font-size: 14px; - font-family: 'Roboto', sans-serif; - color: #282c37; - - &:focus, &:active { - border-bottom: 2px solid #2b90d9; - padding-bottom: 5px; - } - } - - input[type=file] { - display: block; - } - - .hint { - display: block; - margin-top: 5px; - color: lighten(#282c37, 25%); - } - - &.field_with_errors { - input[type=text], input[type=email], input[type=password], textarea { - border-bottom: 2px solid #df405a; - padding-bottom: 5px; - - &:focus, &:active { - border-bottom: 2px solid #2b90d9; - padding-bottom: 5px; - } - } - - .error { - display: block; - margin-top: 5px; - color: #df405a; - } - } - } -} - -.panel { - box-sizing: border-box; - padding: 10px 15px; - background: lighten(#d9e1e8, 5%); - margin-bottom: 20px; - border-radius: 4px; - - .panel-heading { - font-size: 13px; - text-transform: uppercase; - color: lighten(#282c37, 25%); - margin-bottom: 10px; - } - - &.panel-full { - width: 100%; - } - - .panel-row { - display: flex; - - dt { - color: #282c37; - width: 100px; - } - - dd { - flex: 1; - color: lighten(#282c37, 25%); - } - - &.panel-row-wider { - dt { - width: auto; - flex: 1; - } - } - } -} - -.row { - overflow: hidden; - clear: both; - - .panel { - float: left; - width: 320px; - margin-right: 20px; - - &:last-child { - margin-right: 0; - } - } -} - -.page-actions { - margin-top: 20px; - text-align: right; - - .btn { - margin-left: 5px; - } -} diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss deleted file mode 100644 index c9f2740ad0..0000000000 --- a/app/assets/stylesheets/home.scss +++ /dev/null @@ -1,11 +0,0 @@ -.api-descriptions { - .address { - samp { - font-weight: 400; - - &.method { - font-weight: 500; - } - } - } -} diff --git a/app/assets/stylesheets/settings.scss b/app/assets/stylesheets/settings.scss deleted file mode 100644 index fbe6690806..0000000000 --- a/app/assets/stylesheets/settings.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the settings controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/statuses.scss b/app/assets/stylesheets/statuses.scss deleted file mode 100644 index bb9365adcc..0000000000 --- a/app/assets/stylesheets/statuses.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the statuses controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/package.json b/package.json index 7ba01ecfd5..ae590c3a73 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "immutable": "^3.8.1", + "moment": "^2.14.1", "react-immutable-proptypes": "^2.1.0", "react-redux": "^4.4.5", "redux": "^3.5.2",