Compare commits
19 Commits
1974ff00c4
...
57560336d7
Author | SHA1 | Date |
---|---|---|
khr | 57560336d7 | |
khr | d3de5513c8 | |
khr | d902bbab25 | |
khr | 739e64d2a9 | |
chr | f38a43506c | |
Eugen Rochko | 370ec7e771 | |
ThibG | 9222c26e19 | |
Hinaloe | 94439a1da7 | |
ThibG | a6815a7578 | |
Ben Lubar | d587a943a5 | |
ThibG | 3c27687a6e | |
ThibG | ee17d81b8a | |
Neil Moore | 9e95af3391 | |
nzws | 91e25a20ce | |
ThibG | 47e0928c5b | |
Maciek Baron | c407a4edf8 | |
Jeong Arm | 7a6464bea0 | |
nzws | 9679ec4fcb | |
ThibG | b40dfc124b |
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -3,6 +3,28 @@ Changelog
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [2.8.3] - 2019-05-19
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `og:image:alt` OpenGraph tag ([BenLubar](https://github.com/tootsuite/mastodon/pull/10779))
|
||||||
|
- Add clickable area below avatar in statuses in web UI ([Dar13](https://github.com/tootsuite/mastodon/pull/10766))
|
||||||
|
- Add crossed-out eye icon on account gallery in web UI ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10715))
|
||||||
|
- Add media description tooltip to thumbnails in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10713))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Change "mark as sensitive" button into a checkbox for clarity ([ThibG](https://github.com/tootsuite/mastodon/pull/10748))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix bug allowing users to publicly boost their private statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10775), [ThibG](https://github.com/tootsuite/mastodon/pull/10783))
|
||||||
|
- Fix performance in formatter by a little ([ThibG](https://github.com/tootsuite/mastodon/pull/10765))
|
||||||
|
- Fix some colors in the light theme ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10754))
|
||||||
|
- Fix some colors of the high contrast theme ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10711))
|
||||||
|
- Fix ambivalent active state of poll refresh button in web UI ([MaciekBaron](https://github.com/tootsuite/mastodon/pull/10720))
|
||||||
|
- Fix duplicate posting being possible from web UI ([hinaloe](https://github.com/tootsuite/mastodon/pull/10785))
|
||||||
|
- Fix "invited by" not showing up in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10791))
|
||||||
|
|
||||||
## [2.8.2] - 2019-05-05
|
## [2.8.2] - 2019-05-05
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 232 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 398 KiB |
Binary file not shown.
After Width: | Height: | Size: 184 KiB |
|
@ -356,6 +356,7 @@ class Status extends ImmutablePureComponent {
|
||||||
{prepend}
|
{prepend}
|
||||||
|
|
||||||
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>
|
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>
|
||||||
|
<div className='status__expand' onClick={this.handleClick} role='presentation' />
|
||||||
<div className='status__info'>
|
<div className='status__info'>
|
||||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import Icon from 'mastodon/components/icon';
|
||||||
import { autoPlayGif, displayMedia } from 'mastodon/initial_state';
|
import { autoPlayGif, displayMedia } from 'mastodon/initial_state';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { decode } from 'blurhash';
|
import { decode } from 'blurhash';
|
||||||
|
@ -88,8 +89,10 @@ export default class MediaItem extends ImmutablePureComponent {
|
||||||
const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
|
const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
|
||||||
const height = width;
|
const height = width;
|
||||||
const status = attachment.get('status');
|
const status = attachment.get('status');
|
||||||
|
const title = status.get('spoiler_text') || attachment.get('description');
|
||||||
|
|
||||||
let thumbnail = '';
|
let thumbnail = '';
|
||||||
|
let icon;
|
||||||
|
|
||||||
if (attachment.get('type') === 'unknown') {
|
if (attachment.get('type') === 'unknown') {
|
||||||
// Skip
|
// Skip
|
||||||
|
@ -131,11 +134,20 @@ export default class MediaItem extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
icon = (
|
||||||
|
<span className='account-gallery__item__icons'>
|
||||||
|
<Icon id='eye-slash' />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='account-gallery__item' style={{ width, height }}>
|
<div className='account-gallery__item' style={{ width, height }}>
|
||||||
<a className='media-gallery__item-thumbnail' href={status.get('url')} target='_blank' onClick={this.handleClick}>
|
<a className='media-gallery__item-thumbnail' href={status.get('url')} target='_blank' onClick={this.handleClick} title={title}>
|
||||||
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })} />
|
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })} />
|
||||||
{visible && thumbnail}
|
{visible && thumbnail}
|
||||||
|
{!visible && icon}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,7 +20,7 @@ const mapStateToProps = state => ({
|
||||||
focusDate: state.getIn(['compose', 'focusDate']),
|
focusDate: state.getIn(['compose', 'focusDate']),
|
||||||
caretPosition: state.getIn(['compose', 'caretPosition']),
|
caretPosition: state.getIn(['compose', 'caretPosition']),
|
||||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
preselectDate: state.getIn(['compose', 'preselectDate']),
|
||||||
is_submitting: state.getIn(['compose', 'is_submitting']),
|
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { changeComposeSensitivity } from 'mastodon/actions/compose';
|
import { changeComposeSensitivity } from 'mastodon/actions/compose';
|
||||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||||
import Icon from 'mastodon/components/icon';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
marked: { id: 'compose_form.sensitive.marked', defaultMessage: 'Media is marked as sensitive' },
|
marked: { id: 'compose_form.sensitive.marked', defaultMessage: 'Media is marked as sensitive' },
|
||||||
|
@ -38,9 +37,19 @@ class SensitiveButton extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='compose-form__sensitive-button'>
|
<div className='compose-form__sensitive-button'>
|
||||||
<button className={classNames('icon-button', { active })} onClick={onClick} disabled={disabled} title={intl.formatMessage(active ? messages.marked : messages.unmarked)}>
|
<label className={classNames('icon-button', { active })} title={intl.formatMessage(active ? messages.marked : messages.unmarked)}>
|
||||||
<Icon id='eye-slash' /> <FormattedMessage id='compose_form.sensitive.hide' defaultMessage='Mark media as sensitive' />
|
<input
|
||||||
</button>
|
name='mark-sensitive'
|
||||||
|
type='checkbox'
|
||||||
|
checked={active}
|
||||||
|
onChange={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span className={classNames('checkbox', { active })} />
|
||||||
|
|
||||||
|
<FormattedMessage id='compose_form.sensitive.hide' defaultMessage='Mark media as sensitive' />
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,3 +67,11 @@
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nothing-here {
|
||||||
|
color: $darker-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-layout .public-account-header__tabs__tabs .counter.active::after {
|
||||||
|
border-bottom: 4px solid $ui-highlight-color;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
@import 'application';
|
||||||
|
|
||||||
|
/* Allow columns to grow wider as the screen gets
|
||||||
|
* wider, but don't ever let them get more than
|
||||||
|
* 400px (some people have a bunch of columns!) */
|
||||||
|
@media screen and (min-width: 1300px) {
|
||||||
|
.column {
|
||||||
|
flex-grow: 1 !important;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer {
|
||||||
|
width: 17%; /* Not part of the flex fun */
|
||||||
|
max-width: 400px;
|
||||||
|
min-width: 330px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cap the column height at 100vh (fixed an old
|
||||||
|
* bug someone encountered in safari, but which
|
||||||
|
* I've seen resurface from time to time) */
|
||||||
|
.column {
|
||||||
|
max-height:100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't show outline around statuses if we're in
|
||||||
|
* mouse or touch mode (rather than keyboard) */
|
||||||
|
[data-whatinput="mouse"], [data-whatinput="touch"] {
|
||||||
|
.status__content:focus, .status:focus,
|
||||||
|
.status__wrapper:focus, .status__content__text:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Less emphatic show more */
|
||||||
|
.status__content__read-more-button {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $dark-text-color;
|
||||||
|
|
||||||
|
.status__prepend-icon {
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show a little arrowey thing after the time in a
|
||||||
|
* status to signal that you can click it to see
|
||||||
|
* a detailed view */
|
||||||
|
.status time:after,
|
||||||
|
.detailed-status__datetime span:after {
|
||||||
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
content: "\00a0\00a0\f08e";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't display the elephant mascot (we have our
|
||||||
|
* own, thanks) */
|
||||||
|
.drawer__inner__mastodon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Let the compose area/drawer be short, but
|
||||||
|
* expand if necessary */
|
||||||
|
.drawer .drawer__inner {
|
||||||
|
overflow: visible;
|
||||||
|
height:inherit;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.drawer__pager {
|
||||||
|
overflow-y:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use display: none instead of visibility:hidden
|
||||||
|
* to hide the suggested follows list on non-mobile */
|
||||||
|
@media screen and (min-width: 630px) {
|
||||||
|
.search-results .trends {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@import 'fullwidth-media';
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
$ui-base-color: #181818; // darkest
|
||||||
|
$ui-highlight-color: #1ea21e; // vibrant
|
||||||
|
$ui-secondary-color: #E4F2E4; // lightest
|
||||||
|
$ui-primary-color: #E4F2E4; // lighter
|
||||||
|
$ui-primary-color-alt: #a0b49c; // darker, for external pages
|
||||||
|
|
||||||
|
$about-page-text: lighten($ui-base-color, 50%);
|
||||||
|
|
||||||
|
@import 'cybre-base';
|
||||||
|
|
||||||
|
@keyframes floating {
|
||||||
|
from {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
65% {
|
||||||
|
transform: translate(0, 4px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translate(0, -0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body, body.about-body {
|
||||||
|
background: $ui-base-color url('../images/background-cybre.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
body.about-body {
|
||||||
|
// basics.scss &.about-body
|
||||||
|
background: darken($ui-base-color, 8%) url('../images/background-cybre.png');
|
||||||
|
|
||||||
|
background-position-y: 200px;
|
||||||
|
background-position-x: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-body .mascot {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
.status__content p, .status__content a {
|
||||||
|
color: lighten($ui-base-color, 35%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status__display-name strong {
|
||||||
|
color: lighten($ui-base-color, 35%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose-form__buttons button.active:last-child {
|
||||||
|
color:$ui-secondary-color;
|
||||||
|
background-color: $ui-highlight-color;
|
||||||
|
border-radius:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-with-signup {
|
||||||
|
min-height:300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.hero .closed-registrations-message .clock {
|
||||||
|
font-size: 150%;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column .static-content.getting-started {
|
||||||
|
background-image: url('../images/logo-cybre.png'), url('../images/background-cybre.png');
|
||||||
|
background-size:auto 50%, cover;
|
||||||
|
background-position: 50% 75%, center center;
|
||||||
|
background-repeat:no-repeat, no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns-area {
|
||||||
|
background: $ui-base-color url('../images/background-cybre.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions .button.button-alternative {
|
||||||
|
background: $ui-highlight-color;
|
||||||
|
color: $ui-primary-color;
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten($ui-highlight-color, 4%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1280px) {
|
||||||
|
.landing-page .container.links {
|
||||||
|
top: -15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page.alternative .header {
|
||||||
|
background-image:url('../images/header-cybre-colour.jpg');
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
background-size:contain;
|
||||||
|
height:45vh;
|
||||||
|
width: 100%;
|
||||||
|
position:absolute;
|
||||||
|
z-index: 1;
|
||||||
|
text-align:center;
|
||||||
|
|
||||||
|
display: unset!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page.alternative .header img {
|
||||||
|
margin: auto;
|
||||||
|
max-height:45vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.landing-page.alternative .grid {
|
||||||
|
position: relative;
|
||||||
|
z-index:2;
|
||||||
|
margin-top:15vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page.alternative .landing-page__hero img {
|
||||||
|
visibility: hidden;
|
||||||
|
max-height:170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page.alternative .landing-page__forms {
|
||||||
|
height:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page.alternative .column-1 {
|
||||||
|
display:flex;
|
||||||
|
align-items:flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page.alternative .column {
|
||||||
|
max-height:initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page.alternative .row__mascot {
|
||||||
|
.floats {
|
||||||
|
position:absolute;
|
||||||
|
img {
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
transition: all 0.1s linear;
|
||||||
|
animation-name: floating;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-direction: alternate;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-1 {
|
||||||
|
width:50px;
|
||||||
|
height:50px;
|
||||||
|
bottom:60px;
|
||||||
|
left:110px;
|
||||||
|
animation-duration: 3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-2 {
|
||||||
|
width:130px;
|
||||||
|
height:130px;
|
||||||
|
left:85px;
|
||||||
|
bottom: -60px;
|
||||||
|
animation-duration: 3.5s;
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-3 {
|
||||||
|
width:100px;
|
||||||
|
height:100px;
|
||||||
|
right: 50;
|
||||||
|
top: -10px;
|
||||||
|
animation-duration: 4s;
|
||||||
|
animation-delay: 0.5s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-stream {
|
||||||
|
.status.light {
|
||||||
|
.status__header .status__meta .status__relative-time {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name span {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status__content {
|
||||||
|
a.status__content__spoiler-link {
|
||||||
|
background: $ui-primary-color-alt;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: lighten($ui-primary-color-alt, 8%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status.light {
|
||||||
|
.detailed-status__display-name .display-name span {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status__content a.status__content__spoiler-link {
|
||||||
|
background: $ui-primary-color-alt;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: lighten($ui-primary-color-alt, 8%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status__meta {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-spoiler {
|
||||||
|
background: $ui-primary-color-alt;
|
||||||
|
&:hover {
|
||||||
|
background: darken($ui-primary-color-alt, 5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre-header {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
.status__display-name.muted strong {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed .activity-stream .entry .detailed-status.light .button.button-secondary.logo-button {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
svg {
|
||||||
|
path:first-child {
|
||||||
|
fill: $ui-primary-color-alt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
svg path:first-child {
|
||||||
|
fill: lighten($ui-primary-color-alt, 4%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-search {
|
||||||
|
background: $simple-background-color;
|
||||||
|
input {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
border: 1px solid $ui-primary-color-alt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-anchor {
|
||||||
|
color: $ui-primary-color-alt;
|
||||||
|
&:hover {
|
||||||
|
color: darken($ui-primary-color-alt, 8%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-popout {
|
||||||
|
background: $ui-base-color;
|
||||||
|
color: $ui-primary-color;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
color: $ui-primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
color: $ui-highlight-color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
/* Use 100% of the viewport width always*/
|
||||||
|
.detailed-status > .media-spoiler,
|
||||||
|
.status > .media-spoiler,
|
||||||
|
.status .video-player,
|
||||||
|
.media-gallery,
|
||||||
|
.status .status-card {
|
||||||
|
margin-top:20px;
|
||||||
|
margin-left:-68px;
|
||||||
|
width: calc(100% + 80px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unset max-width for spoilers and the video player */
|
||||||
|
.detailed-status > .media-spoiler,
|
||||||
|
.status > .media-spoiler,
|
||||||
|
.video-player {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there's no status text, add an extra margin on top */
|
||||||
|
.status .status__info + .media-gallery,
|
||||||
|
.status .status__info + .media-spoiler,
|
||||||
|
.status .status__info + .video-player,
|
||||||
|
.status .status__info + .status-card {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Undo transforms on videos inside the video player */
|
||||||
|
.status__video-player-video {
|
||||||
|
transform:unset;
|
||||||
|
top:unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Adjust offset and width for detail view */
|
||||||
|
.detailed-status .media-gallery {
|
||||||
|
margin-left:-10px;
|
||||||
|
width: calc(100% + 22px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-layout .status {
|
||||||
|
.status__content {
|
||||||
|
min-height: 15px;
|
||||||
|
}
|
||||||
|
& > .media-spoiler,
|
||||||
|
.video-player,
|
||||||
|
.media-gallery,
|
||||||
|
.status-card {
|
||||||
|
margin-top:20px;
|
||||||
|
width: calc(100% + 94px);
|
||||||
|
margin-left: -78px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -162,7 +162,7 @@
|
||||||
.actions-modal ul li:not(:empty) a:focus button,
|
.actions-modal ul li:not(:empty) a:focus button,
|
||||||
.actions-modal ul li:not(:empty) a:hover,
|
.actions-modal ul li:not(:empty) a:hover,
|
||||||
.actions-modal ul li:not(:empty) a:hover button,
|
.actions-modal ul li:not(:empty) a:hover button,
|
||||||
.admin-wrapper .sidebar ul ul a.selected,
|
.admin-wrapper .sidebar ul li a.selected,
|
||||||
.simple_form .block-button,
|
.simple_form .block-button,
|
||||||
.simple_form .button,
|
.simple_form .button,
|
||||||
.simple_form button {
|
.simple_form button {
|
||||||
|
@ -230,6 +230,7 @@
|
||||||
.empty-column-indicator,
|
.empty-column-indicator,
|
||||||
.error-column {
|
.error-column {
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
|
background: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the default colors used on some parts of the profile pages
|
// Change the default colors used on some parts of the profile pages
|
||||||
|
|
|
@ -268,9 +268,34 @@
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
||||||
.icon-button {
|
font-size: 14px;
|
||||||
font-size: 14px;
|
font-weight: 500;
|
||||||
font-weight: 500;
|
|
||||||
|
&.active {
|
||||||
|
color: $highlight-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid $ui-primary-color;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-right: 10px;
|
||||||
|
top: -1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: $highlight-text-color;
|
||||||
|
background: $highlight-text-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1386,6 +1411,15 @@ a.account__display-name {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status__expand {
|
||||||
|
width: 68px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
.status__content p,
|
.status__content p,
|
||||||
.status__content a {
|
.status__content a {
|
||||||
|
@ -4829,6 +4863,14 @@ a.status-card.compact:hover {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
|
|
||||||
|
&__icons {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification__filter-bar,
|
.notification__filter-bar,
|
||||||
|
|
|
@ -114,11 +114,14 @@
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
|
|
||||||
&:hover,
|
&:hover {
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
background-color: rgba($dark-text-color, .1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
|
|
@ -187,7 +187,7 @@ class Formatter
|
||||||
end
|
end
|
||||||
|
|
||||||
def rewrite(text, entities)
|
def rewrite(text, entities)
|
||||||
chars = text.to_s.to_char_a
|
text = text.to_s
|
||||||
|
|
||||||
# Sort by start index
|
# Sort by start index
|
||||||
entities = entities.sort_by do |entity|
|
entities = entities.sort_by do |entity|
|
||||||
|
@ -199,12 +199,12 @@ class Formatter
|
||||||
|
|
||||||
last_index = entities.reduce(0) do |index, entity|
|
last_index = entities.reduce(0) do |index, entity|
|
||||||
indices = entity.respond_to?(:indices) ? entity.indices : entity[:indices]
|
indices = entity.respond_to?(:indices) ? entity.indices : entity[:indices]
|
||||||
result << encode(chars[index...indices.first].join)
|
result << encode(text[index...indices.first])
|
||||||
result << yield(entity)
|
result << yield(entity)
|
||||||
indices.last
|
indices.last
|
||||||
end
|
end
|
||||||
|
|
||||||
result << encode(chars[last_index..-1].join)
|
result << encode(text[last_index..-1])
|
||||||
|
|
||||||
result.flatten.join
|
result.flatten.join
|
||||||
end
|
end
|
||||||
|
@ -231,23 +231,14 @@ class Formatter
|
||||||
# Note: I couldn't obtain list_slug with @user/list-name format
|
# Note: I couldn't obtain list_slug with @user/list-name format
|
||||||
# for mention so this requires additional check
|
# for mention so this requires additional check
|
||||||
special = Extractor.extract_urls_with_indices(escaped, options).map do |extract|
|
special = Extractor.extract_urls_with_indices(escaped, options).map do |extract|
|
||||||
# exactly one of :url, :hashtag, :screen_name, :cashtag keys is present
|
|
||||||
key = (extract.keys & [:url, :hashtag, :screen_name, :cashtag]).first
|
|
||||||
|
|
||||||
new_indices = [
|
new_indices = [
|
||||||
old_to_new_index.find_index(extract[:indices].first),
|
old_to_new_index.find_index(extract[:indices].first),
|
||||||
old_to_new_index.find_index(extract[:indices].last),
|
old_to_new_index.find_index(extract[:indices].last),
|
||||||
]
|
]
|
||||||
|
|
||||||
has_prefix_char = [:hashtag, :screen_name, :cashtag].include?(key)
|
|
||||||
value_indices = [
|
|
||||||
new_indices.first + (has_prefix_char ? 1 : 0), # account for #, @ or $
|
|
||||||
new_indices.last - 1,
|
|
||||||
]
|
|
||||||
|
|
||||||
next extract.merge(
|
next extract.merge(
|
||||||
:indices => new_indices,
|
indices: new_indices,
|
||||||
key => text[value_indices.first..value_indices.last]
|
url: text[new_indices.first..new_indices.last - 1]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,10 @@ class User < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def invited?
|
def invited?
|
||||||
|
invite_id.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_invitation?
|
||||||
invite_id.present? && invite.valid_for_use?
|
invite_id.present? && invite.valid_for_use?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -274,7 +278,7 @@ class User < ApplicationRecord
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_approved
|
def set_approved
|
||||||
self.approved = open_registrations? || invited? || external?
|
self.approved = open_registrations? || valid_invitation? || external?
|
||||||
end
|
end
|
||||||
|
|
||||||
def open_registrations?
|
def open_registrations?
|
||||||
|
|
|
@ -18,7 +18,9 @@ class ReblogService < BaseService
|
||||||
|
|
||||||
return reblog unless reblog.nil?
|
return reblog unless reblog.nil?
|
||||||
|
|
||||||
reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: options[:visibility] || account.user&.setting_default_privacy)
|
visibility = options[:visibility] || account.user&.setting_default_privacy
|
||||||
|
visibility = reblogged_status.visibility if reblogged_status.hidden?
|
||||||
|
reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility)
|
||||||
|
|
||||||
DistributionWorker.perform_async(reblog.id)
|
DistributionWorker.perform_async(reblog.id)
|
||||||
Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id)
|
Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class BlacklistedEmailValidator < ActiveModel::Validator
|
class BlacklistedEmailValidator < ActiveModel::Validator
|
||||||
def validate(user)
|
def validate(user)
|
||||||
return if user.invited?
|
return if user.valid_invitation?
|
||||||
|
|
||||||
@email = user.email
|
@email = user.email
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
- unless media.file.meta.nil?
|
- unless media.file.meta.nil?
|
||||||
= opengraph 'og:image:width', media.file.meta.dig('original', 'width')
|
= opengraph 'og:image:width', media.file.meta.dig('original', 'width')
|
||||||
= opengraph 'og:image:height', media.file.meta.dig('original', 'height')
|
= opengraph 'og:image:height', media.file.meta.dig('original', 'height')
|
||||||
|
- if media.description.present?
|
||||||
|
= opengraph 'og:image:alt', media.description
|
||||||
- elsif media.video? || media.gifv?
|
- elsif media.video? || media.gifv?
|
||||||
- player_card = true
|
- player_card = true
|
||||||
= opengraph 'og:image', full_asset_url(media.file.url(:small))
|
= opengraph 'og:image', full_asset_url(media.file.url(:small))
|
||||||
|
|
|
@ -1005,9 +1005,10 @@ en:
|
||||||
<p>Originally adapted from the <a href="https://github.com/discourse/discourse">Discourse privacy policy</a>.</p>
|
<p>Originally adapted from the <a href="https://github.com/discourse/discourse">Discourse privacy policy</a>.</p>
|
||||||
title: "%{instance} Terms of Service and Privacy Policy"
|
title: "%{instance} Terms of Service and Privacy Policy"
|
||||||
themes:
|
themes:
|
||||||
contrast: Mastodon (High contrast)
|
contrast: High contrast
|
||||||
default: Mastodon (Dark)
|
default: Cybrespace
|
||||||
mastodon-light: Mastodon (Light)
|
mastodon: Mastodon
|
||||||
|
mastodon-light: Mastodon (light)
|
||||||
time:
|
time:
|
||||||
formats:
|
formats:
|
||||||
default: "%b %d, %Y, %H:%M"
|
default: "%b %d, %Y, %H:%M"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
default: styles/application.scss
|
default: styles/cybre.scss
|
||||||
|
mastodon: styles/application.scss
|
||||||
contrast: styles/contrast.scss
|
contrast: styles/contrast.scss
|
||||||
mastodon-light: styles/mastodon-light.scss
|
mastodon-light: styles/mastodon-light.scss
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
class RemoveBoostsWideningAudience < ActiveRecord::Migration[5.2]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
public_boosts = Status.find_by_sql(<<-SQL)
|
||||||
|
SELECT boost.id
|
||||||
|
FROM statuses AS boost
|
||||||
|
LEFT JOIN statuses AS boosted ON boost.reblog_of_id = boosted.id
|
||||||
|
WHERE
|
||||||
|
boost.id > 101746055577600000
|
||||||
|
AND (boost.local = TRUE OR boost.uri IS NULL)
|
||||||
|
AND boost.visibility IN (0, 1)
|
||||||
|
AND boost.reblog_of_id IS NOT NULL
|
||||||
|
AND boosted.visibility = 2
|
||||||
|
SQL
|
||||||
|
|
||||||
|
RemovalWorker.push_bulk(public_boosts.pluck(:id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
raise ActiveRecord::IrreversibleMigration
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2019_04_20_025523) do
|
ActiveRecord::Schema.define(version: 2019_05_19_130537) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
|
@ -13,7 +13,7 @@ module Mastodon
|
||||||
end
|
end
|
||||||
|
|
||||||
def patch
|
def patch
|
||||||
2
|
3
|
||||||
end
|
end
|
||||||
|
|
||||||
def pre
|
def pre
|
||||||
|
|
|
@ -4,10 +4,9 @@ RSpec.describe ReblogService, type: :service do
|
||||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||||
|
|
||||||
context 'creates a reblog with appropriate visibility' do
|
context 'creates a reblog with appropriate visibility' do
|
||||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
|
||||||
let(:visibility) { :public }
|
let(:visibility) { :public }
|
||||||
let(:reblog_visibility) { :public }
|
let(:reblog_visibility) { :public }
|
||||||
let(:status) { Fabricate(:status, account: bob, visibility: visibility) }
|
let(:status) { Fabricate(:status, account: alice, visibility: visibility) }
|
||||||
|
|
||||||
subject { ReblogService.new }
|
subject { ReblogService.new }
|
||||||
|
|
||||||
|
@ -22,6 +21,15 @@ RSpec.describe ReblogService, type: :service do
|
||||||
expect(status.reblogs.first.visibility).to eq 'private'
|
expect(status.reblogs.first.visibility).to eq 'private'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'public reblogs of private toots should remain private' do
|
||||||
|
let(:visibility) { :private }
|
||||||
|
let(:reblog_visibility) { :public }
|
||||||
|
|
||||||
|
it 'reblogs privately' do
|
||||||
|
expect(status.reblogs.first.visibility).to eq 'private'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'OStatus' do
|
context 'OStatus' do
|
||||||
|
|
|
@ -8,7 +8,7 @@ RSpec.describe BlacklistedEmailValidator, type: :validator do
|
||||||
let(:errors) { double(add: nil) }
|
let(:errors) { double(add: nil) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(user).to receive(:invited?) { false }
|
allow(user).to receive(:valid_invitation?) { false }
|
||||||
allow_any_instance_of(described_class).to receive(:blocked_email?) { blocked_email }
|
allow_any_instance_of(described_class).to receive(:blocked_email?) { blocked_email }
|
||||||
described_class.new.validate(user)
|
described_class.new.validate(user)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue