mirror of https://framagit.org/tykayn/mastodon.git
Merge branch 'messaging'
This commit is contained in:
commit
d867f9c57b
|
@ -62,3 +62,11 @@ ubuntu-xenial-16.04-cloudimg-console.log
|
|||
|
||||
# Ignore Docker option files
|
||||
docker-compose.override.yml
|
||||
|
||||
|
||||
/public/packs
|
||||
/public/packs-test
|
||||
/node_modules
|
||||
/yarn-error.log
|
||||
yarn-debug.log*
|
||||
.yarn-integrity
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
defaults
|
|
@ -1 +1 @@
|
|||
2.6.5
|
||||
2.6.4
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
// Place all the behaviors and hooks related to the matching controller here.
|
||||
// All this logic will automatically be available in application.js.
|
|
@ -0,0 +1,80 @@
|
|||
body {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
margin: 33px;
|
||||
}
|
||||
|
||||
body, p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
th {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0 5px 7px;
|
||||
}
|
||||
|
||||
div.field,
|
||||
div.actions {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#notice {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.field_with_errors {
|
||||
padding: 2px;
|
||||
background-color: red;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#error_explanation {
|
||||
width: 450px;
|
||||
border: 2px solid red;
|
||||
padding: 7px 7px 0;
|
||||
margin-bottom: 20px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#error_explanation h2 {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
padding: 5px 5px 5px 15px;
|
||||
font-size: 12px;
|
||||
margin: -7px -7px 0;
|
||||
background-color: #c00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#error_explanation ul li {
|
||||
font-size: 12px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/*
|
||||
Place all the styles related to the matching controller here.
|
||||
They will automatically be included in application.css.
|
||||
*/
|
|
@ -0,0 +1,58 @@
|
|||
class UserGroupsController < ApplicationController
|
||||
before_action :set_user_group, only: [:show, :edit, :update, :destroy]
|
||||
|
||||
# GET /user_groups
|
||||
def index
|
||||
@user_groups = UserGroup.all
|
||||
end
|
||||
|
||||
# GET /user_groups/1
|
||||
def show
|
||||
end
|
||||
|
||||
# GET /user_groups/new
|
||||
def new
|
||||
@user_group = UserGroup.new
|
||||
end
|
||||
|
||||
# GET /user_groups/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /user_groups
|
||||
def create
|
||||
@user_group = UserGroup.new(user_group_params)
|
||||
|
||||
if @user_group.save
|
||||
redirect_to @user_group, notice: 'User group was successfully created.'
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /user_groups/1
|
||||
def update
|
||||
if @user_group.update(user_group_params)
|
||||
redirect_to @user_group, notice: 'User group was successfully updated.'
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /user_groups/1
|
||||
def destroy
|
||||
@user_group.destroy
|
||||
redirect_to user_groups_url, notice: 'User group was successfully destroyed.'
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_user_group
|
||||
@user_group = UserGroup.find(params[:id])
|
||||
end
|
||||
|
||||
# Only allow a trusted parameter "white list" through.
|
||||
def user_group_params
|
||||
params.require(:user_group).permit(:name, :createAt, :visibility, :members)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,2 @@
|
|||
module UserGroupsHelper
|
||||
end
|
|
@ -1,9 +1,5 @@
|
|||
import api, { getLinks } from '../api';
|
||||
import {
|
||||
importFetchedAccounts,
|
||||
importFetchedStatuses,
|
||||
importFetchedStatus,
|
||||
} from './importer';
|
||||
import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
|
||||
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
|
||||
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
|
||||
|
@ -30,7 +26,7 @@ export const unmountConversations = () => ({
|
|||
export const markConversationRead = conversationId => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: CONVERSATIONS_READ,
|
||||
id: conversationId,
|
||||
id : conversationId,
|
||||
});
|
||||
|
||||
api(getState).post(`/api/v1/conversations/${conversationId}/read`);
|
||||
|
|
|
@ -3,7 +3,7 @@ import openDB from '../storage/db';
|
|||
import { evictStatus } from '../storage/modifier';
|
||||
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer';
|
||||
import { importAccount, importFetchedStatus, importFetchedStatuses, importStatus } from './importer';
|
||||
import { ensureComposeIsVisible } from './compose';
|
||||
|
||||
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
|
||||
|
@ -37,7 +37,7 @@ export function fetchStatusRequest(id, skipLoading) {
|
|||
id,
|
||||
skipLoading,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function getFromDB(dispatch, getState, accountIndex, index, id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -113,24 +113,24 @@ export function fetchStatus(id) {
|
|||
dispatch(fetchStatusFail(id, error, skipLoading));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchStatusSuccess(skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_SUCCESS,
|
||||
skipLoading,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchStatusFail(id, error, skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_FAIL,
|
||||
type : STATUS_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipLoading,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function redraft(status, raw_text) {
|
||||
return {
|
||||
|
@ -138,7 +138,7 @@ export function redraft(status, raw_text) {
|
|||
status,
|
||||
raw_text,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||
return (dispatch, getState) => {
|
||||
|
@ -163,29 +163,29 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
|
|||
dispatch(deleteStatusFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatusRequest(id) {
|
||||
return {
|
||||
type: STATUS_DELETE_REQUEST,
|
||||
id: id,
|
||||
id : id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatusSuccess(id) {
|
||||
return {
|
||||
type: STATUS_DELETE_SUCCESS,
|
||||
id: id,
|
||||
id : id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatusFail(id, error) {
|
||||
return {
|
||||
type: STATUS_DELETE_FAIL,
|
||||
id: id,
|
||||
type : STATUS_DELETE_FAIL,
|
||||
id : id,
|
||||
error: error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContext(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
@ -203,33 +203,33 @@ export function fetchContext(id) {
|
|||
dispatch(fetchContextFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContextRequest(id) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContextSuccess(id, ancestors, descendants) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_SUCCESS,
|
||||
type : CONTEXT_FETCH_SUCCESS,
|
||||
id,
|
||||
ancestors,
|
||||
descendants,
|
||||
statuses: ancestors.concat(descendants),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContextFail(id, error) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_FAIL,
|
||||
type : CONTEXT_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
@ -241,21 +241,21 @@ export function muteStatus(id) {
|
|||
dispatch(muteStatusFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatusRequest(id) {
|
||||
return {
|
||||
type: STATUS_MUTE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatusSuccess(id) {
|
||||
return {
|
||||
type: STATUS_MUTE_SUCCESS,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatusFail(id, error) {
|
||||
return {
|
||||
|
@ -263,7 +263,7 @@ export function muteStatusFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
@ -275,21 +275,21 @@ export function unmuteStatus(id) {
|
|||
dispatch(unmuteStatusFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatusRequest(id) {
|
||||
return {
|
||||
type: STATUS_UNMUTE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatusSuccess(id) {
|
||||
return {
|
||||
type: STATUS_UNMUTE_SUCCESS,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatusFail(id, error) {
|
||||
return {
|
||||
|
@ -297,7 +297,7 @@ export function unmuteStatusFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function hideStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
|
@ -308,7 +308,7 @@ export function hideStatus(ids) {
|
|||
type: STATUS_HIDE,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function revealStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
|
@ -319,4 +319,4 @@ export function revealStatus(ids) {
|
|||
type: STATUS_REVEAL,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -37,34 +37,37 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
|
|||
export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
disabled: PropTypes.bool,
|
||||
placeholder: PropTypes.string,
|
||||
onSuggestionSelected: PropTypes.func.isRequired,
|
||||
value : PropTypes.string,
|
||||
suggestions : ImmutablePropTypes.list,
|
||||
disabled : PropTypes.bool,
|
||||
placeholder : PropTypes.string,
|
||||
onSuggestionSelected : PropTypes.func.isRequired,
|
||||
onSuggestionsClearRequested: PropTypes.func.isRequired,
|
||||
onSuggestionsFetchRequested: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onKeyUp: PropTypes.func,
|
||||
onKeyDown: PropTypes.func,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
autoFocus: PropTypes.bool,
|
||||
onChange : PropTypes.func.isRequired,
|
||||
onKeyUp : PropTypes.func,
|
||||
onKeyDown : PropTypes.func,
|
||||
onPaste : PropTypes.func.isRequired,
|
||||
autoFocus : PropTypes.bool,
|
||||
directMessage : PropTypes.bool,
|
||||
directMessageRecipient : PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
autoFocus: true,
|
||||
autoFocus : true,
|
||||
directMessage: false,
|
||||
};
|
||||
|
||||
state = {
|
||||
suggestionsHidden: true,
|
||||
focused: false,
|
||||
suggestionsHidden : true,
|
||||
focused : false,
|
||||
selectedSuggestion: 0,
|
||||
lastToken: null,
|
||||
tokenStart: 0,
|
||||
lastToken : null,
|
||||
tokenStart : 0,
|
||||
};
|
||||
|
||||
onChange = (e) => {
|
||||
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
|
||||
const [tokenStart, token] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
|
||||
|
||||
if (token !== null && this.state.lastToken !== token) {
|
||||
this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
|
||||
|
@ -75,7 +78,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onChange(e);
|
||||
}
|
||||
};
|
||||
|
||||
onKeyDown = (e) => {
|
||||
const { suggestions, disabled } = this.props;
|
||||
|
@ -92,7 +95,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
switch(e.key) {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
if (suggestions.size === 0 || suggestionsHidden) {
|
||||
document.querySelector('.ui').parentElement.focus();
|
||||
|
@ -124,7 +127,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
e.stopPropagation();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -133,27 +135,27 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
};
|
||||
|
||||
onBlur = () => {
|
||||
this.setState({ suggestionsHidden: true, focused: false });
|
||||
}
|
||||
};
|
||||
|
||||
onFocus = (e) => {
|
||||
this.setState({ focused: true });
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionClick = (e) => {
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
||||
e.preventDefault();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
||||
this.textarea.focus();
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
|
||||
this.setState({ suggestionsHidden: false });
|
||||
}
|
||||
|
@ -161,14 +163,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
|
||||
setTextarea = (c) => {
|
||||
this.textarea = c;
|
||||
}
|
||||
};
|
||||
|
||||
onPaste = (e) => {
|
||||
if (e.clipboardData && e.clipboardData.files.length === 1) {
|
||||
this.props.onPaste(e.clipboardData.files);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderSuggestion = (suggestion, i) => {
|
||||
const { selectedSuggestion } = this.state;
|
||||
|
@ -186,13 +188,20 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
|
||||
<div
|
||||
role='button'
|
||||
tabIndex='0'
|
||||
key={key}
|
||||
data-index={i}
|
||||
className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })}
|
||||
onMouseDown={this.onSuggestionClick}
|
||||
>
|
||||
{inner}
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
|
||||
const { suggestionsHidden } = this.state;
|
||||
const style = { direction: 'ltr' };
|
||||
|
@ -202,10 +211,13 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
return [
|
||||
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
|
||||
<div
|
||||
className='compose-form__autosuggest-wrapper'
|
||||
key='autosuggest-wrapper'
|
||||
>
|
||||
<div className='autosuggest-textarea'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
||||
<label >
|
||||
<span style={{ display: 'none' }}>{placeholder}</span >
|
||||
|
||||
<Textarea
|
||||
inputRef={this.setTextarea}
|
||||
|
@ -223,16 +235,21 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
style={style}
|
||||
aria-autocomplete='list'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</label >
|
||||
</div >
|
||||
{children}
|
||||
</div>,
|
||||
</div >,
|
||||
|
||||
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
<div
|
||||
className='autosuggest-textarea__suggestions-wrapper'
|
||||
key='suggestions-wrapper'
|
||||
>
|
||||
<div
|
||||
className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}
|
||||
>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div>
|
||||
</div>,
|
||||
</div >
|
||||
</div >,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { createPortal } from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
||||
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
|
||||
moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
|
||||
show : { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
||||
hide : { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
|
||||
moveLeft : { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
|
||||
moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
|
||||
});
|
||||
|
||||
|
@ -20,19 +20,19 @@ class ColumnHeader extends React.PureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
active: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
extraButton: PropTypes.node,
|
||||
intl : PropTypes.object.isRequired,
|
||||
title : PropTypes.node,
|
||||
icon : PropTypes.string,
|
||||
active : PropTypes.bool,
|
||||
multiColumn : PropTypes.bool,
|
||||
extraButton : PropTypes.node,
|
||||
showBackButton: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
pinned: PropTypes.bool,
|
||||
placeholder: PropTypes.bool,
|
||||
onPin: PropTypes.func,
|
||||
onMove: PropTypes.func,
|
||||
onClick: PropTypes.func,
|
||||
children : PropTypes.node,
|
||||
pinned : PropTypes.bool,
|
||||
placeholder : PropTypes.bool,
|
||||
onPin : PropTypes.func,
|
||||
onMove : PropTypes.func,
|
||||
onClick : PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -46,41 +46,41 @@ class ColumnHeader extends React.PureComponent {
|
|||
} else {
|
||||
this.context.router.history.goBack();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleToggleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ collapsed: !this.state.collapsed, animating: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleTitleClick = () => {
|
||||
this.props.onClick();
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveLeft = () => {
|
||||
this.props.onMove(-1);
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveRight = () => {
|
||||
this.props.onMove(1);
|
||||
}
|
||||
};
|
||||
|
||||
handleBackClick = () => {
|
||||
this.historyBack();
|
||||
}
|
||||
};
|
||||
|
||||
handleTransitionEnd = () => {
|
||||
this.setState({ animating: false });
|
||||
}
|
||||
};
|
||||
|
||||
handlePin = () => {
|
||||
if (!this.props.pinned) {
|
||||
this.historyBack();
|
||||
}
|
||||
this.props.onPin();
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props;
|
||||
const { collapsed, animating } = this.state;
|
||||
|
||||
|
@ -105,31 +105,82 @@ class ColumnHeader extends React.PureComponent {
|
|||
|
||||
if (children) {
|
||||
extraContent = (
|
||||
<div key='extra-content' className='column-header__collapsible__extra'>
|
||||
<div
|
||||
key='extra-content'
|
||||
className='column-header__collapsible__extra'
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
if (multiColumn && pinned) {
|
||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
|
||||
pinButton =
|
||||
(<button
|
||||
key='pin-button'
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handlePin}
|
||||
><Icon
|
||||
id='times'
|
||||
/> <FormattedMessage
|
||||
id='column_header.unpin'
|
||||
defaultMessage='Unpin'
|
||||
/></button >);
|
||||
|
||||
moveButtons = (
|
||||
<div key='move-buttons' className='column-header__setting-arrows'>
|
||||
<button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='text-btn column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' /></button>
|
||||
<button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='text-btn column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' /></button>
|
||||
</div>
|
||||
<div
|
||||
key='move-buttons'
|
||||
className='column-header__setting-arrows'
|
||||
>
|
||||
<button
|
||||
title={formatMessage(messages.moveLeft)}
|
||||
aria-label={formatMessage(messages.moveLeft)}
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handleMoveLeft}
|
||||
><Icon
|
||||
id='chevron-left'
|
||||
/></button >
|
||||
<button
|
||||
title={formatMessage(messages.moveRight)}
|
||||
aria-label={formatMessage(messages.moveRight)}
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handleMoveRight}
|
||||
><Icon
|
||||
id='chevron-right'
|
||||
/></button >
|
||||
</div >
|
||||
);
|
||||
} else if (multiColumn && this.props.onPin) {
|
||||
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
|
||||
pinButton =
|
||||
(<button
|
||||
key='pin-button'
|
||||
className='text-btn column-header__setting-btn'
|
||||
onClick={this.handlePin}
|
||||
><Icon
|
||||
id='plus'
|
||||
/> <FormattedMessage
|
||||
id='column_header.pin'
|
||||
defaultMessage='Pin'
|
||||
/>
|
||||
</button >);
|
||||
}
|
||||
|
||||
if (!pinned && (multiColumn || showBackButton)) {
|
||||
backButton = (
|
||||
<button onClick={this.handleBackClick} className='column-header__back-button'>
|
||||
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
|
||||
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
|
||||
</button>
|
||||
<button
|
||||
onClick={this.handleBackClick}
|
||||
className='column-header__back-button'
|
||||
>
|
||||
<Icon
|
||||
id='chevron-left'
|
||||
className='column-back-button__icon'
|
||||
fixedWidth
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='column_back_button.label'
|
||||
defaultMessage='Back'
|
||||
/>
|
||||
</button >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -143,7 +194,16 @@ class ColumnHeader extends React.PureComponent {
|
|||
}
|
||||
|
||||
if (children || (multiColumn && this.props.onPin)) {
|
||||
collapseButton = <button className={collapsibleButtonClassName} title={formatMessage(collapsed ? messages.show : messages.hide)} aria-label={formatMessage(collapsed ? messages.show : messages.hide)} aria-pressed={collapsed ? 'false' : 'true'} onClick={this.handleToggleClick}><Icon id='sliders' /></button>;
|
||||
collapseButton =
|
||||
(<button
|
||||
className={collapsibleButtonClassName}
|
||||
title={formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
aria-label={formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
aria-pressed={collapsed ? 'false' : 'true'}
|
||||
onClick={this.handleToggleClick}
|
||||
>
|
||||
<Icon id='sliders' />
|
||||
</button >);
|
||||
}
|
||||
|
||||
const hasTitle = icon && title;
|
||||
|
@ -153,9 +213,13 @@ class ColumnHeader extends React.PureComponent {
|
|||
<h1 className={buttonClassName}>
|
||||
{hasTitle && (
|
||||
<button onClick={this.handleTitleClick}>
|
||||
<Icon id={icon} fixedWidth className='column-header__icon' />
|
||||
<Icon
|
||||
id={icon}
|
||||
fixedWidth
|
||||
className='column-header__icon'
|
||||
/>
|
||||
{title}
|
||||
</button>
|
||||
</button >
|
||||
)}
|
||||
|
||||
{!hasTitle && backButton}
|
||||
|
@ -164,15 +228,19 @@ class ColumnHeader extends React.PureComponent {
|
|||
{hasTitle && backButton}
|
||||
{extraButton}
|
||||
{collapseButton}
|
||||
</div>
|
||||
</h1>
|
||||
</div >
|
||||
</h1 >
|
||||
|
||||
<div className={collapsibleClassName} tabIndex={collapsed ? -1 : null} onTransitionEnd={this.handleTransitionEnd}>
|
||||
<div
|
||||
className={collapsibleClassName}
|
||||
tabIndex={collapsed ? -1 : null}
|
||||
onTransitionEnd={this.handleTransitionEnd}
|
||||
>
|
||||
<div className='column-header__collapsible-inner'>
|
||||
{(!collapsed || animating) && collapsedContent}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
|
||||
if (multiColumn || placeholder) {
|
||||
|
|
|
@ -6,12 +6,12 @@ import { autoPlayGif } from 'mastodon/initial_state';
|
|||
export default class DisplayName extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
others: ImmutablePropTypes.list,
|
||||
account : ImmutablePropTypes.map.isRequired,
|
||||
others : ImmutablePropTypes.list,
|
||||
localDomain: PropTypes.string,
|
||||
};
|
||||
|
||||
_updateEmojis () {
|
||||
_updateEmojis() {
|
||||
const node = this.node;
|
||||
|
||||
if (!node || autoPlayGif) {
|
||||
|
@ -32,33 +32,40 @@ export default class DisplayName extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
this._updateEmojis();
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
componentDidUpdate() {
|
||||
this._updateEmojis();
|
||||
}
|
||||
|
||||
handleEmojiMouseEnter = ({ target }) => {
|
||||
target.src = target.getAttribute('data-original');
|
||||
}
|
||||
};
|
||||
|
||||
handleEmojiMouseLeave = ({ target }) => {
|
||||
target.src = target.getAttribute('data-static');
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { others, localDomain } = this.props;
|
||||
|
||||
let displayName, suffix, account;
|
||||
|
||||
if (others && others.size > 1) {
|
||||
displayName = others.take(2).map(a => <bdi key={a.get('id')}><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }} /></bdi>).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
displayName = others.take(2).map(a =>
|
||||
(<bdi key={a.get('id')}>
|
||||
<strong
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
||||
/>
|
||||
|
||||
</bdi >)).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
|
||||
if (others.size - 2 > 0) {
|
||||
suffix = `+${others.size - 2}`;
|
||||
|
@ -76,14 +83,22 @@ export default class DisplayName extends React.PureComponent {
|
|||
acct = `${acct}@${localDomain}`;
|
||||
}
|
||||
|
||||
displayName = <bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>;
|
||||
suffix = <span className='display-name__account'>@{acct}</span>;
|
||||
displayName = <bdi ><strong
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
|
||||
/>
|
||||
|
||||
</bdi >;
|
||||
suffix = <span className='display-name__account'>@{acct}</span >;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className='display-name' ref={this.setRef}>
|
||||
<span
|
||||
className='display-name'
|
||||
ref={this.setRef}
|
||||
>
|
||||
{displayName} {suffix}
|
||||
</span>
|
||||
</span >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,41 +6,41 @@ import Icon from 'mastodon/components/icon';
|
|||
export default class IconButton extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
title: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
className : PropTypes.string,
|
||||
title : PropTypes.string.isRequired,
|
||||
icon : PropTypes.string.isRequired,
|
||||
onClick : PropTypes.func,
|
||||
onMouseDown: PropTypes.func,
|
||||
onKeyDown: PropTypes.func,
|
||||
onKeyPress: PropTypes.func,
|
||||
size: PropTypes.number,
|
||||
active: PropTypes.bool,
|
||||
pressed: PropTypes.bool,
|
||||
expanded: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
onKeyDown : PropTypes.func,
|
||||
onKeyPress : PropTypes.func,
|
||||
size : PropTypes.number,
|
||||
active : PropTypes.bool,
|
||||
pressed : PropTypes.bool,
|
||||
expanded : PropTypes.bool,
|
||||
style : PropTypes.object,
|
||||
activeStyle: PropTypes.object,
|
||||
disabled: PropTypes.bool,
|
||||
inverted: PropTypes.bool,
|
||||
animate: PropTypes.bool,
|
||||
overlay: PropTypes.bool,
|
||||
tabIndex: PropTypes.string,
|
||||
disabled : PropTypes.bool,
|
||||
inverted : PropTypes.bool,
|
||||
animate : PropTypes.bool,
|
||||
overlay : PropTypes.bool,
|
||||
tabIndex : PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
size: 18,
|
||||
active: false,
|
||||
size : 18,
|
||||
active : false,
|
||||
disabled: false,
|
||||
animate: false,
|
||||
overlay: false,
|
||||
animate : false,
|
||||
overlay : false,
|
||||
tabIndex: '0',
|
||||
};
|
||||
|
||||
state = {
|
||||
activate: false,
|
||||
activate : false,
|
||||
deactivate: false,
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!nextProps.animate) return;
|
||||
|
||||
if (this.props.active && !nextProps.active) {
|
||||
|
@ -56,32 +56,32 @@ export default class IconButton extends React.PureComponent {
|
|||
if (!this.props.disabled) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (this.props.onKeyPress && !this.props.disabled) {
|
||||
this.props.onKeyPress(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown = (e) => {
|
||||
if (!this.props.disabled && this.props.onMouseDown) {
|
||||
this.props.onMouseDown(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (!this.props.disabled && this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const style = {
|
||||
fontSize: `${this.props.size}px`,
|
||||
width: `${this.props.size * 1.28571429}px`,
|
||||
height: `${this.props.size * 1.28571429}px`,
|
||||
lineHeight: `${this.props.size}px`,
|
||||
// fontSize: `${this.props.size}px`,
|
||||
// width: `${this.props.size * 1.28571429}px`,
|
||||
// height: `${this.props.size * 1.28571429}px`,
|
||||
// lineHeight: `${this.props.size}px`,
|
||||
...this.props.style,
|
||||
...(this.props.active ? this.props.activeStyle : {}),
|
||||
};
|
||||
|
@ -128,8 +128,12 @@ export default class IconButton extends React.PureComponent {
|
|||
tabIndex={tabIndex}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />
|
||||
</button>
|
||||
<Icon
|
||||
id={icon}
|
||||
fixedWidth
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</button >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,17 +10,17 @@ import StatusContent from './status_content';
|
|||
import StatusActionBar from './status_action_bar';
|
||||
import AttachmentList from './attachment_list';
|
||||
import Card from '../features/status/components/card';
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
|
||||
import { Audio, MediaGallery, Video } from '../features/ui/util/async-components';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import { displayMedia } from '../initial_state';
|
||||
|
||||
import { displayMedia, isStaff } from '../initial_state';
|
||||
// We use the component (and not the container) since we do not want
|
||||
// to use the progress bar to show download progress
|
||||
import Bundle from '../features/ui/components/bundle';
|
||||
import imageShowThread from '../../images/icon_reply.svg';
|
||||
|
||||
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
||||
const displayName = status.getIn(['account', 'display_name']);
|
||||
|
@ -59,33 +59,37 @@ class Status extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map,
|
||||
account: ImmutablePropTypes.map,
|
||||
otherAccounts: ImmutablePropTypes.list,
|
||||
onClick: PropTypes.func,
|
||||
onReply: PropTypes.func,
|
||||
onFavourite: PropTypes.func,
|
||||
onReblog: PropTypes.func,
|
||||
onDelete: PropTypes.func,
|
||||
onDirect: PropTypes.func,
|
||||
onMention: PropTypes.func,
|
||||
onPin: PropTypes.func,
|
||||
onOpenMedia: PropTypes.func,
|
||||
onOpenVideo: PropTypes.func,
|
||||
onBlock: PropTypes.func,
|
||||
onEmbed: PropTypes.func,
|
||||
onHeightChange: PropTypes.func,
|
||||
onToggleHidden: PropTypes.func,
|
||||
muted: PropTypes.bool,
|
||||
hidden: PropTypes.bool,
|
||||
unread: PropTypes.bool,
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
showThread: PropTypes.bool,
|
||||
getScrollPosition: PropTypes.func,
|
||||
status : ImmutablePropTypes.map,
|
||||
account : ImmutablePropTypes.map,
|
||||
otherAccounts : ImmutablePropTypes.list,
|
||||
onClick : PropTypes.func,
|
||||
onReply : PropTypes.func,
|
||||
onFavourite : PropTypes.func,
|
||||
onReblog : PropTypes.func,
|
||||
onDelete : PropTypes.func,
|
||||
onDirect : PropTypes.func,
|
||||
onMention : PropTypes.func,
|
||||
onPin : PropTypes.func,
|
||||
onOpenMedia : PropTypes.func,
|
||||
onOpenVideo : PropTypes.func,
|
||||
onBlock : PropTypes.func,
|
||||
onEmbed : PropTypes.func,
|
||||
onHeightChange : PropTypes.func,
|
||||
onToggleHidden : PropTypes.func,
|
||||
muted : PropTypes.bool,
|
||||
hidden : PropTypes.bool,
|
||||
unread : PropTypes.bool,
|
||||
onMoveUp : PropTypes.func,
|
||||
onMoveDown : PropTypes.func,
|
||||
showThread : PropTypes.bool,
|
||||
threadsCompile : PropTypes.bool,
|
||||
getScrollPosition : PropTypes.func,
|
||||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth: PropTypes.func,
|
||||
cachedMediaWidth: PropTypes.number,
|
||||
cacheMediaWidth : PropTypes.func,
|
||||
cachedMediaWidth : PropTypes.number,
|
||||
};
|
||||
static defaultProps = {
|
||||
threadsCompile: true,
|
||||
};
|
||||
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
|
@ -99,15 +103,26 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
state = {
|
||||
showMedia: defaultMediaVisibility(this.props.status),
|
||||
statusId: undefined,
|
||||
statusId : undefined,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
|
||||
return {
|
||||
showMedia: defaultMediaVisibility(nextProps.status),
|
||||
statusId : nextProps.status.get('id'),
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Track height changes we know about to compensate scrolling
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||
}
|
||||
|
||||
getSnapshotBeforeUpdate () {
|
||||
getSnapshotBeforeUpdate() {
|
||||
if (this.props.getScrollPosition) {
|
||||
return this.props.getScrollPosition();
|
||||
} else {
|
||||
|
@ -115,19 +130,8 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
|
||||
return {
|
||||
showMedia: defaultMediaVisibility(nextProps.status),
|
||||
statusId: nextProps.status.get('id'),
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Compensate height changes
|
||||
componentDidUpdate (prevProps, prevState, snapshot) {
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||
|
||||
if (doShowCard && !this.didShowCard) {
|
||||
|
@ -154,7 +158,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleToggleMediaVisibility = () => {
|
||||
this.setState({ showMedia: !this.state.showMedia });
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (this.props.onClick) {
|
||||
|
@ -168,7 +172,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
const { status } = this.props;
|
||||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleExpandClick = (e) => {
|
||||
if (this.props.onClick) {
|
||||
|
@ -184,7 +188,7 @@ class Status extends ImmutablePureComponent {
|
|||
const { status } = this.props;
|
||||
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAccountClick = (e) => {
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
|
@ -192,27 +196,36 @@ class Status extends ImmutablePureComponent {
|
|||
e.preventDefault();
|
||||
this.context.router.history.push(`/accounts/${id}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleExpandedToggle = () => {
|
||||
this.props.onToggleHidden(this._properStatus());
|
||||
};
|
||||
|
||||
renderLoadingMediaGallery () {
|
||||
return <div className='media-gallery' style={{ height: '110px' }} />;
|
||||
renderLoadingMediaGallery() {
|
||||
return (<div
|
||||
className='media-gallery'
|
||||
style={{ height: '110px' }}
|
||||
/>);
|
||||
}
|
||||
|
||||
renderLoadingVideoPlayer () {
|
||||
return <div className='video-player' style={{ height: '110px' }} />;
|
||||
renderLoadingVideoPlayer() {
|
||||
return (<div
|
||||
className='video-player'
|
||||
style={{ height: '110px' }}
|
||||
/>);
|
||||
}
|
||||
|
||||
renderLoadingAudioPlayer () {
|
||||
return <div className='audio-player' style={{ height: '110px' }} />;
|
||||
renderLoadingAudioPlayer() {
|
||||
return (<div
|
||||
className='audio-player'
|
||||
style={{ height: '110px' }}
|
||||
/>);
|
||||
}
|
||||
|
||||
handleOpenVideo = (media, startTime) => {
|
||||
this.props.onOpenVideo(media, startTime);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenMedia = e => {
|
||||
const { onOpenMedia, onOpenVideo } = this.props;
|
||||
|
@ -229,51 +242,51 @@ class Status extends ImmutablePureComponent {
|
|||
onOpenMedia(status.get('media_attachments'), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyReply = e => {
|
||||
e.preventDefault();
|
||||
this.props.onReply(this._properStatus(), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
this.props.onFavourite(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyBoost = e => {
|
||||
this.props.onReblog(this._properStatus(), e);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
e.preventDefault();
|
||||
this.props.onMention(this._properStatus().get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpen = () => {
|
||||
this.context.router.history.push(`/statuses/${this._properStatus().get('id')}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = e => {
|
||||
this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = e => {
|
||||
this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleHidden = () => {
|
||||
this.props.onToggleHidden(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleSensitive = () => {
|
||||
this.handleToggleMediaVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
_properStatus () {
|
||||
_properStatus() {
|
||||
const { status } = this.props;
|
||||
|
||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
|
@ -285,9 +298,9 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
let media = null;
|
||||
let statusAvatar, prepend, rebloggedByText;
|
||||
|
||||
|
@ -300,63 +313,102 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
const handlers = this.props.muted ? {} : {
|
||||
reply: this.handleHotkeyReply,
|
||||
favourite: this.handleHotkeyFavourite,
|
||||
boost: this.handleHotkeyBoost,
|
||||
mention: this.handleHotkeyMention,
|
||||
open: this.handleHotkeyOpen,
|
||||
openProfile: this.handleHotkeyOpenProfile,
|
||||
moveUp: this.handleHotkeyMoveUp,
|
||||
moveDown: this.handleHotkeyMoveDown,
|
||||
toggleHidden: this.handleHotkeyToggleHidden,
|
||||
reply : this.handleHotkeyReply,
|
||||
favourite : this.handleHotkeyFavourite,
|
||||
boost : this.handleHotkeyBoost,
|
||||
mention : this.handleHotkeyMention,
|
||||
open : this.handleHotkeyOpen,
|
||||
openProfile : this.handleHotkeyOpenProfile,
|
||||
moveUp : this.handleHotkeyMoveUp,
|
||||
moveDown : this.handleHotkeyMoveDown,
|
||||
toggleHidden : this.handleHotkeyToggleHidden,
|
||||
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||
openMedia: this.handleHotkeyOpenMedia,
|
||||
openMedia : this.handleHotkeyOpenMedia,
|
||||
};
|
||||
|
||||
if (hidden) {
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'>
|
||||
<div
|
||||
ref={this.handleRef}
|
||||
className={classNames('status__wrapper', { focusable: !this.props.muted })}
|
||||
tabIndex='0'
|
||||
>
|
||||
{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
|
||||
{status.get('content')}
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
if (status.get('filtered') || status.getIn(['reblog', 'filtered'])) {
|
||||
const minHandlers = this.props.muted ? {} : {
|
||||
moveUp: this.handleHotkeyMoveUp,
|
||||
moveUp : this.handleHotkeyMoveUp,
|
||||
moveDown: this.handleHotkeyMoveDown,
|
||||
};
|
||||
|
||||
return (
|
||||
<HotKeys handlers={minHandlers}>
|
||||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
|
||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />
|
||||
</div>
|
||||
</HotKeys>
|
||||
<div
|
||||
className='status__wrapper status__wrapper--filtered focusable'
|
||||
tabIndex='0'
|
||||
ref={this.handleRef}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='status.filtered'
|
||||
defaultMessage='Filtered'
|
||||
/>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
if (featured) {
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div>
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned toot' />
|
||||
</div>
|
||||
<div className='status__prepend-icon-wrapper'><Icon
|
||||
id='thumb-tack'
|
||||
className='status__prepend-icon'
|
||||
fixedWidth
|
||||
/></div >
|
||||
<FormattedMessage
|
||||
id='status.pinned'
|
||||
defaultMessage='Pinned toot'
|
||||
/>
|
||||
</div >
|
||||
);
|
||||
} else if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
|
||||
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='retweet' className='status__prepend-icon' fixedWidth /></div>
|
||||
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} />
|
||||
</div>
|
||||
<div className='status__prepend-icon-wrapper'><Icon
|
||||
id='retweet'
|
||||
className='status__prepend-icon'
|
||||
fixedWidth
|
||||
/>
|
||||
</div >
|
||||
<FormattedMessage
|
||||
id='status.reblogged_by'
|
||||
defaultMessage='{name} boosted'
|
||||
values={{
|
||||
name: <a
|
||||
onClick={this.handleAccountClick}
|
||||
data-id={status.getIn(['account', 'id'])}
|
||||
href={status.getIn(['account', 'url'])}
|
||||
className='status__display-name muted'
|
||||
>
|
||||
<bdi ><strong dangerouslySetInnerHTML={display_name_html} /></bdi >
|
||||
</a >,
|
||||
}}
|
||||
/>
|
||||
</div >
|
||||
);
|
||||
|
||||
rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: status.getIn(['account', 'acct']) });
|
||||
rebloggedByText = intl.formatMessage({
|
||||
id : 'status.reblogged_by',
|
||||
defaultMessage: '{name} boosted',
|
||||
}, { name: status.getIn(['account', 'acct']) });
|
||||
|
||||
account = status.get('account');
|
||||
status = status.get('reblog');
|
||||
|
@ -374,7 +426,10 @@ class Status extends ImmutablePureComponent {
|
|||
const attachment = status.getIn(['media_attachments', 0]);
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
||||
<Bundle
|
||||
fetchComponent={Audio}
|
||||
loading={this.renderLoadingAudioPlayer}
|
||||
>
|
||||
{Component => (
|
||||
<Component
|
||||
src={attachment.get('url')}
|
||||
|
@ -384,13 +439,16 @@ class Status extends ImmutablePureComponent {
|
|||
height={70}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
</Bundle >
|
||||
);
|
||||
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||
const attachment = status.getIn(['media_attachments', 0]);
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||
<Bundle
|
||||
fetchComponent={Video}
|
||||
loading={this.renderLoadingVideoPlayer}
|
||||
>
|
||||
{Component => (
|
||||
<Component
|
||||
preview={attachment.get('preview_url')}
|
||||
|
@ -407,11 +465,14 @@ class Status extends ImmutablePureComponent {
|
|||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
</Bundle >
|
||||
);
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
||||
<Bundle
|
||||
fetchComponent={MediaGallery}
|
||||
loading={this.renderLoadingMediaGallery}
|
||||
>
|
||||
{Component => (
|
||||
<Component
|
||||
media={status.get('media_attachments')}
|
||||
|
@ -424,7 +485,7 @@ class Status extends ImmutablePureComponent {
|
|||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
</Bundle >
|
||||
);
|
||||
}
|
||||
} else if (status.get('spoiler_text').length === 0 && status.get('card')) {
|
||||
|
@ -440,46 +501,126 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (otherAccounts && otherAccounts.size > 0) {
|
||||
statusAvatar = <AvatarComposite accounts={otherAccounts} size={48} />;
|
||||
statusAvatar = (<AvatarComposite
|
||||
accounts={otherAccounts}
|
||||
size={48}
|
||||
/>);
|
||||
} else if (account === undefined || account === null) {
|
||||
statusAvatar = <Avatar account={status.get('account')} size={48} />;
|
||||
statusAvatar = (<Avatar
|
||||
account={status.get('account')}
|
||||
size={48}
|
||||
/>);
|
||||
} else {
|
||||
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
|
||||
statusAvatar = (<AvatarOverlay
|
||||
account={status.get('account')}
|
||||
friend={account}
|
||||
/>);
|
||||
}
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
||||
<div
|
||||
className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, {
|
||||
'status__wrapper-reply': !!status.get('in_reply_to_id'),
|
||||
read : unread === false,
|
||||
focusable : !this.props.muted,
|
||||
})}
|
||||
tabIndex={this.props.muted ? null : 0}
|
||||
data-featured={featured ? 'true' : null}
|
||||
aria-label={textForScreenReader(intl, status, rebloggedByText)}
|
||||
ref={this.handleRef}
|
||||
>
|
||||
{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='status__expand' onClick={this.handleExpandClick} role='presentation' />
|
||||
<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.handleExpandClick}
|
||||
role='presentation'
|
||||
/>
|
||||
<div className='status__info'>
|
||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
||||
|
||||
<a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} title={status.getIn(['account', 'acct'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
|
||||
{isStaff && (<div className='administrate-stuff pull-left'>
|
||||
|
||||
<i className='fa fa-gears' />
|
||||
</div >
|
||||
)}
|
||||
<a
|
||||
href={status.get('url')}
|
||||
className='status__relative-time'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
><RelativeTimestamp timestamp={status.get('created_at')} /></a >
|
||||
|
||||
<a
|
||||
onClick={this.handleAccountClick}
|
||||
data-id={status.getIn(['account', 'id'])}
|
||||
href={status.getIn(['account', 'url'])}
|
||||
title={status.getIn(['account', 'acct'])}
|
||||
className='status__display-name'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<div className='status__avatar'>
|
||||
{statusAvatar}
|
||||
</div>
|
||||
</div >
|
||||
|
||||
<DisplayName account={status.get('account')} others={otherAccounts} />
|
||||
</a>
|
||||
</div>
|
||||
<DisplayName
|
||||
account={status.get('account')}
|
||||
others={otherAccounts}
|
||||
/>
|
||||
</a >
|
||||
</div >
|
||||
|
||||
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable />
|
||||
<StatusContent
|
||||
status={status}
|
||||
onClick={this.handleClick}
|
||||
expanded={!status.get('hidden')}
|
||||
onExpandedToggle={this.handleExpandedToggle}
|
||||
collapsable
|
||||
/>
|
||||
|
||||
{media}
|
||||
|
||||
{showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) && (
|
||||
<button className='status__content__read-more-button' onClick={this.handleClick}>
|
||||
<FormattedMessage id='status.show_thread' defaultMessage='Show thread' />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className='status__content__read-more-button'
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
|
||||
<StatusActionBar status={status} account={account} {...other} />
|
||||
</div>
|
||||
</div>
|
||||
</HotKeys>
|
||||
<FormattedMessage
|
||||
id='status.show_thread'
|
||||
defaultMessage='Show thread'
|
||||
/>
|
||||
<img
|
||||
src={imageShowThread}
|
||||
alt='=> '
|
||||
/>
|
||||
</button >
|
||||
)}
|
||||
{/*<div className='well'>*/}
|
||||
|
||||
{/* {status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) &&*/}
|
||||
{/* <p > oui je cause tout seul, c'est un thread</p >*/}
|
||||
{/* }*/}
|
||||
{/* {this.props.threadsCompile &&*/}
|
||||
{/* <p > les threads sont en mode compilés</p >*/}
|
||||
{/* }*/}
|
||||
{/*</div >*/}
|
||||
<StatusActionBar
|
||||
status={status}
|
||||
account={account} {...other}
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ import Icon from 'mastodon/components/icon';
|
|||
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
|
||||
placeholder : { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
|
||||
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
|
||||
publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
|
||||
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
||||
publish : { id: 'compose_form.publish', defaultMessage: 'Toot' },
|
||||
publishLoud : { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
|
@ -38,44 +38,46 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
spoiler: PropTypes.bool,
|
||||
privacy: PropTypes.string,
|
||||
spoilerText: PropTypes.string,
|
||||
focusDate: PropTypes.instanceOf(Date),
|
||||
caretPosition: PropTypes.number,
|
||||
preselectDate: PropTypes.instanceOf(Date),
|
||||
isSubmitting: PropTypes.bool,
|
||||
isChangingUpload: PropTypes.bool,
|
||||
isUploading: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onClearSuggestions: PropTypes.func.isRequired,
|
||||
onFetchSuggestions: PropTypes.func.isRequired,
|
||||
intl : PropTypes.object.isRequired,
|
||||
text : PropTypes.string.isRequired,
|
||||
suggestions : ImmutablePropTypes.list,
|
||||
spoiler : PropTypes.bool,
|
||||
privacy : PropTypes.string,
|
||||
spoilerText : PropTypes.string,
|
||||
focusDate : PropTypes.instanceOf(Date),
|
||||
caretPosition : PropTypes.number,
|
||||
maxTootCharsLimit : PropTypes.number,
|
||||
preselectDate : PropTypes.instanceOf(Date),
|
||||
isSubmitting : PropTypes.bool,
|
||||
isChangingUpload : PropTypes.bool,
|
||||
isUploading : PropTypes.bool,
|
||||
onChange : PropTypes.func.isRequired,
|
||||
onSubmit : PropTypes.func.isRequired,
|
||||
onClearSuggestions : PropTypes.func.isRequired,
|
||||
onFetchSuggestions : PropTypes.func.isRequired,
|
||||
onSuggestionSelected: PropTypes.func.isRequired,
|
||||
onChangeSpoilerText: PropTypes.func.isRequired,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
onPickEmoji: PropTypes.func.isRequired,
|
||||
showSearch: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
singleColumn: PropTypes.bool,
|
||||
onChangeSpoilerText : PropTypes.func.isRequired,
|
||||
onPaste : PropTypes.func.isRequired,
|
||||
onPickEmoji : PropTypes.func.isRequired,
|
||||
showSearch : PropTypes.bool,
|
||||
anyMedia : PropTypes.bool,
|
||||
singleColumn : PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
showSearch: false,
|
||||
showSearch : false,
|
||||
maxTootCharsLimit: 7777,
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
this.props.onChange(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
this.handleSubmit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
if (this.props.text !== this.autosuggestTextarea.textarea.value) {
|
||||
|
@ -88,32 +90,32 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
||||
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
|
||||
if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > 500 || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) {
|
||||
if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > this.props.maxTootCharsLimit || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onSubmit(this.context.router ? this.context.router.history : null);
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
|
||||
}
|
||||
};
|
||||
|
||||
onSpoilerSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
|
||||
}
|
||||
};
|
||||
|
||||
handleChangeSpoilerText = (e) => {
|
||||
this.props.onChangeSpoilerText(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
handleFocus = () => {
|
||||
if (this.composeForm && !this.props.singleColumn) {
|
||||
|
@ -122,9 +124,9 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
this.composeForm.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
componentDidUpdate(prevProps) {
|
||||
// This statement does several things:
|
||||
// - If we're beginning a reply, and,
|
||||
// - Replying to zero or one users, places the cursor at the end of the textbox.
|
||||
|
@ -146,7 +148,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
|
||||
this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
|
||||
this.autosuggestTextarea.textarea.focus();
|
||||
} else if(prevProps.isSubmitting && !this.props.isSubmitting) {
|
||||
} else if (prevProps.isSubmitting && !this.props.isSubmitting) {
|
||||
this.autosuggestTextarea.textarea.focus();
|
||||
} else if (this.props.spoiler !== prevProps.spoiler) {
|
||||
if (this.props.spoiler) {
|
||||
|
@ -159,11 +161,11 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
|
||||
setAutosuggestTextarea = (c) => {
|
||||
this.autosuggestTextarea = c;
|
||||
}
|
||||
};
|
||||
|
||||
setSpoilerText = (c) => {
|
||||
this.spoilerText = c;
|
||||
}
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.composeForm = c;
|
||||
|
@ -175,17 +177,18 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
|
||||
|
||||
this.props.onPickEmoji(position, data, needsSpace);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { intl, onPaste, showSearch, anyMedia } = this.props;
|
||||
const disabled = this.props.isSubmitting;
|
||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > 500 || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
|
||||
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > this.props.maxTootCharsLimit || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
|
||||
let publishText = '';
|
||||
|
||||
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
||||
publishText = <span className='compose-form__publish-private'><Icon id='lock' /> {intl.formatMessage(messages.publish)}</span>;
|
||||
publishText =
|
||||
<span className='compose-form__publish-private'><Icon id='lock' /> {intl.formatMessage(messages.publish)}</span >;
|
||||
} else {
|
||||
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
|
||||
}
|
||||
|
@ -196,7 +199,10 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
|
||||
<ReplyIndicatorContainer />
|
||||
|
||||
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}>
|
||||
<div
|
||||
className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`}
|
||||
ref={this.setRef}
|
||||
>
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
|
@ -212,7 +218,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
/>
|
||||
</div>
|
||||
</div >
|
||||
|
||||
<AutosuggestTextarea
|
||||
ref={this.setAutosuggestTextarea}
|
||||
|
@ -233,8 +239,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<div className='compose-form__modifiers'>
|
||||
<UploadFormContainer />
|
||||
<PollFormContainer />
|
||||
</div>
|
||||
</AutosuggestTextarea>
|
||||
</div >
|
||||
</AutosuggestTextarea >
|
||||
|
||||
<div className='compose-form__buttons-wrapper'>
|
||||
<div className='compose-form__buttons'>
|
||||
|
@ -242,14 +248,23 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<PollButtonContainer />
|
||||
<PrivacyDropdownContainer />
|
||||
<SpoilerButtonContainer />
|
||||
</div>
|
||||
<div className='character-counter__wrapper'><CharacterCounter max={500} text={text} /></div>
|
||||
</div>
|
||||
</div >
|
||||
<div className='character-counter__wrapper'><CharacterCounter
|
||||
max={this.props.maxTootCharsLimit}
|
||||
text={text}
|
||||
/></div >
|
||||
</div >
|
||||
|
||||
<div className='compose-form__publish'>
|
||||
<div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabledButton} block /></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='compose-form__publish-button-wrapper'><Button
|
||||
text={publishText}
|
||||
onClick={this.handleSubmit}
|
||||
disabled={disabledButton}
|
||||
block
|
||||
/></div >
|
||||
</div >
|
||||
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,32 +11,57 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
export default class NavigationBar extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
account : ImmutablePropTypes.map.isRequired,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func,
|
||||
onClose : PropTypes.func,
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<div className='navigation-bar'>
|
||||
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
|
||||
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
|
||||
<Avatar account={this.props.account} size={48} />
|
||||
</Permalink>
|
||||
<Permalink
|
||||
href={this.props.account.get('url')}
|
||||
to={`/accounts/${this.props.account.get('id')}`}
|
||||
>
|
||||
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span >
|
||||
<Avatar
|
||||
account={this.props.account}
|
||||
size={48}
|
||||
/>
|
||||
</Permalink >
|
||||
|
||||
<div className='navigation-bar__profile'>
|
||||
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
|
||||
<strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong>
|
||||
</Permalink>
|
||||
<Permalink
|
||||
href={this.props.account.get('url')}
|
||||
to={`/accounts/${this.props.account.get('id')}`}
|
||||
>
|
||||
<strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong >
|
||||
</Permalink >
|
||||
|
||||
<a href='/settings/profile' className='navigation-bar__profile-edit'><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
|
||||
</div>
|
||||
<a
|
||||
href='/settings/profile'
|
||||
className='navigation-bar__profile-edit'
|
||||
>
|
||||
<i className='fa fa-pencil'></i >
|
||||
<FormattedMessage
|
||||
id='navigation_bar.edit_profile'
|
||||
defaultMessage='Edit profile'
|
||||
/></a >
|
||||
</div >
|
||||
|
||||
<div className='navigation-bar__actions'>
|
||||
<IconButton className='close' title='' icon='close' onClick={this.props.onClose} />
|
||||
<ActionBar account={this.props.account} onLogout={this.props.onLogout} />
|
||||
</div>
|
||||
</div>
|
||||
<IconButton
|
||||
className='close'
|
||||
title=''
|
||||
icon='close'
|
||||
onClick={this.props.onClose}
|
||||
/>
|
||||
<ActionBar
|
||||
account={this.props.account}
|
||||
onLogout={this.props.onLogout}
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import IconButton from 'mastodon/components/icon_button';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
||||
|
@ -56,19 +56,19 @@ class Option extends React.PureComponent {
|
|||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
this.handleToggleMultiple(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isPollMultiple, title, index, intl } = this.props;
|
||||
|
|
|
@ -4,7 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import StatusContent from 'mastodon/components/status_content';
|
||||
import AttachmentList from 'mastodon/components/attachment_list';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||
import AvatarComposite from 'mastodon/components/avatar_composite';
|
||||
import Permalink from 'mastodon/components/permalink';
|
||||
|
@ -15,12 +15,12 @@ import { autoPlayGif } from 'mastodon/initial_state';
|
|||
import classNames from 'classnames';
|
||||
|
||||
const messages = defineMessages({
|
||||
more: { id: 'status.more', defaultMessage: 'More' },
|
||||
open: { id: 'conversation.open', defaultMessage: 'View conversation' },
|
||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
markAsRead: { id: 'conversation.mark_as_read', defaultMessage: 'Mark as read' },
|
||||
delete: { id: 'conversation.delete', defaultMessage: 'Delete conversation' },
|
||||
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||
more : { id: 'status.more', defaultMessage: 'More' },
|
||||
open : { id: 'conversation.open', defaultMessage: 'View conversation' },
|
||||
reply : { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
markAsRead : { id: 'conversation.mark_as_read', defaultMessage: 'Mark as read' },
|
||||
delete : { id: 'conversation.delete', defaultMessage: 'Delete conversation' },
|
||||
muteConversation : { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||
});
|
||||
|
||||
|
@ -33,17 +33,17 @@ class Conversation extends ImmutablePureComponent {
|
|||
|
||||
static propTypes = {
|
||||
conversationId: PropTypes.string.isRequired,
|
||||
accounts: ImmutablePropTypes.list.isRequired,
|
||||
lastStatus: ImmutablePropTypes.map,
|
||||
unread:PropTypes.bool.isRequired,
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
markRead: PropTypes.func.isRequired,
|
||||
delete: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
accounts : ImmutablePropTypes.list.isRequired,
|
||||
lastStatus : ImmutablePropTypes.map,
|
||||
unread : PropTypes.bool.isRequired,
|
||||
onMoveUp : PropTypes.func,
|
||||
onMoveDown : PropTypes.func,
|
||||
markRead : PropTypes.func.isRequired,
|
||||
delete : PropTypes.func.isRequired,
|
||||
intl : PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
_updateEmojis () {
|
||||
_updateEmojis() {
|
||||
const node = this.namesNode;
|
||||
|
||||
if (!node || autoPlayGif) {
|
||||
|
@ -64,21 +64,21 @@ class Conversation extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
this._updateEmojis();
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
componentDidUpdate() {
|
||||
this._updateEmojis();
|
||||
}
|
||||
|
||||
handleEmojiMouseEnter = ({ target }) => {
|
||||
target.src = target.getAttribute('data-original');
|
||||
}
|
||||
};
|
||||
|
||||
handleEmojiMouseLeave = ({ target }) => {
|
||||
target.src = target.getAttribute('data-static');
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (!this.context.router) {
|
||||
|
@ -92,41 +92,41 @@ class Conversation extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.context.router.history.push(`/statuses/${lastStatus.get('id')}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleMarkAsRead = () => {
|
||||
this.props.markRead();
|
||||
}
|
||||
};
|
||||
|
||||
handleReply = () => {
|
||||
this.props.reply(this.props.lastStatus, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleDelete = () => {
|
||||
this.props.delete();
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = () => {
|
||||
this.props.onMoveUp(this.props.conversationId);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = () => {
|
||||
this.props.onMoveDown(this.props.conversationId);
|
||||
}
|
||||
};
|
||||
|
||||
handleConversationMute = () => {
|
||||
this.props.onMute(this.props.lastStatus);
|
||||
}
|
||||
};
|
||||
|
||||
handleShowMore = () => {
|
||||
this.props.onToggleHidden(this.props.lastStatus);
|
||||
}
|
||||
};
|
||||
|
||||
setNamesRef = (c) => {
|
||||
this.namesNode = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { accounts, lastStatus, unread, intl } = this.props;
|
||||
|
||||
if (lastStatus === null) {
|
||||
|
@ -138,7 +138,10 @@ class Conversation extends ImmutablePureComponent {
|
|||
null,
|
||||
];
|
||||
|
||||
menu.push({ text: intl.formatMessage(lastStatus.get('muted') ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMute });
|
||||
menu.push({
|
||||
text : intl.formatMessage(lastStatus.get('muted') ? messages.unmuteConversation : messages.muteConversation),
|
||||
action: this.handleConversationMute,
|
||||
});
|
||||
|
||||
if (unread) {
|
||||
menu.push({ text: intl.formatMessage(messages.markAsRead), action: this.handleMarkAsRead });
|
||||
|
@ -147,33 +150,60 @@ class Conversation extends ImmutablePureComponent {
|
|||
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDelete });
|
||||
|
||||
const names = accounts.map(a => <Permalink to={`/accounts/${a.get('id')}`} href={a.get('url')} key={a.get('id')} title={a.get('acct')}><bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }} /></bdi></Permalink>).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
const names = accounts.map(a => (<Permalink
|
||||
to={`/accounts/${a.get('id')}`}
|
||||
href={a.get('url')}
|
||||
key={a.get('id')}
|
||||
title={a.get('acct')}
|
||||
>
|
||||
<bdi ><strong
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
||||
/></bdi >
|
||||
</Permalink >)).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
|
||||
const handlers = {
|
||||
reply: this.handleReply,
|
||||
open: this.handleClick,
|
||||
moveUp: this.handleHotkeyMoveUp,
|
||||
moveDown: this.handleHotkeyMoveDown,
|
||||
reply : this.handleReply,
|
||||
open : this.handleClick,
|
||||
moveUp : this.handleHotkeyMoveUp,
|
||||
moveDown : this.handleHotkeyMoveDown,
|
||||
toggleHidden: this.handleShowMore,
|
||||
};
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex='0'>
|
||||
<div
|
||||
className={classNames('conversation focusable muted', { 'conversation--unread': unread })}
|
||||
tabIndex='0'
|
||||
>
|
||||
<div className='conversation__avatar'>
|
||||
<AvatarComposite accounts={accounts} size={48} />
|
||||
</div>
|
||||
<AvatarComposite
|
||||
accounts={accounts}
|
||||
size={48}
|
||||
/>
|
||||
</div >
|
||||
|
||||
<div className='conversation__content'>
|
||||
<div className='conversation__content__info'>
|
||||
<div className='conversation__content__relative-time'>
|
||||
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
||||
</div>
|
||||
<span className='conversation_created-at'>
|
||||
{lastStatus.get('created_at').substr(0, 10)}
|
||||
</span >
|
||||
{unread && <span className='conversation__unread' />}
|
||||
<RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
||||
</div >
|
||||
|
||||
<div className='conversation__content__names' ref={this.setNamesRef}>
|
||||
<FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className='conversation__content__names'
|
||||
ref={this.setNamesRef}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='conversation.with'
|
||||
defaultMessage='With {names}'
|
||||
values={{ names: <span >{names}</span > }}
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
|
||||
<StatusContent
|
||||
status={lastStatus}
|
||||
|
@ -191,15 +221,28 @@ class Conversation extends ImmutablePureComponent {
|
|||
)}
|
||||
|
||||
<div className='status__action-bar'>
|
||||
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' onClick={this.handleReply} />
|
||||
|
||||
<div className='status__action-bar-dropdown'>
|
||||
<DropdownMenuContainer status={lastStatus} items={menu} icon='ellipsis-h' size={18} direction='right' title={intl.formatMessage(messages.more)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</HotKeys>
|
||||
<DropdownMenuContainer
|
||||
status={lastStatus}
|
||||
items={menu}
|
||||
icon='ellipsis-h'
|
||||
direction='right'
|
||||
style={{ width: '15em' }}
|
||||
size={18}
|
||||
title={intl.formatMessage(messages.more)}
|
||||
/>
|
||||
</div >
|
||||
<IconButton
|
||||
className='status__action-bar-button conversation_reply'
|
||||
title={intl.formatMessage(messages.reply)}
|
||||
icon='reply'
|
||||
size='15em'
|
||||
onClick={this.handleReply}
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import LoadingIndicator from '../../components/loading_indicator';
|
||||
import { fetchReblogs } from '../../actions/interactions';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import Column from '../ui/components/column';
|
||||
import ScrollableList from '../../components/scrollable_list';
|
||||
|
@ -25,15 +25,15 @@ export default @connect(mapStateToProps)
|
|||
class Reblogs extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
params : PropTypes.object.isRequired,
|
||||
dispatch : PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
multiColumn: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
accountIds : ImmutablePropTypes.list,
|
||||
multiColumn : PropTypes.bool,
|
||||
intl : PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
componentWillMount() {
|
||||
if (!this.props.accountIds) {
|
||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||
}
|
||||
|
@ -47,20 +47,23 @@ class Reblogs extends ImmutablePureComponent {
|
|||
|
||||
handleRefresh = () => {
|
||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = <FormattedMessage id='status.reblogs.empty' defaultMessage='No one has boosted this toot yet. When someone does, they will show up here.' />;
|
||||
const emptyMessage = (<FormattedMessage
|
||||
id='status.reblogs.empty'
|
||||
defaultMessage='No one has boosted this toot yet. When someone does, they will show up here.'
|
||||
/>);
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn}>
|
||||
|
@ -68,7 +71,12 @@ class Reblogs extends ImmutablePureComponent {
|
|||
showBackButton
|
||||
multiColumn={multiColumn}
|
||||
extraButton={(
|
||||
<button className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button>
|
||||
<button
|
||||
className='column-header__button'
|
||||
title={intl.formatMessage(messages.refresh)}
|
||||
aria-label={intl.formatMessage(messages.refresh)}
|
||||
onClick={this.handleRefresh}
|
||||
><Icon id='refresh' /></button >
|
||||
)}
|
||||
/>
|
||||
|
||||
|
@ -79,10 +87,14 @@ class Reblogs extends ImmutablePureComponent {
|
|||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
(<AccountContainer
|
||||
key={id}
|
||||
id={id}
|
||||
withNote={false}
|
||||
/>),
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
</ScrollableList >
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,36 +5,36 @@ import IconButton from '../../../components/icon_button';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { me, isStaff } from '../../../initial_state';
|
||||
import { isStaff, me } from '../../../initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
||||
direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
|
||||
mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
|
||||
delete : { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
redraft : { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
||||
direct : { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
|
||||
mention : { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
||||
reply : { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
reblog : { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||
reblog_private : { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
|
||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||
mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' },
|
||||
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||
block: { id: 'status.block', defaultMessage: 'Block @{name}' },
|
||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||
share: { id: 'status.share', defaultMessage: 'Share' },
|
||||
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||
cannot_reblog : { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||
favourite : { id: 'status.favourite', defaultMessage: 'Favourite' },
|
||||
bookmark : { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||
mute : { id: 'status.mute', defaultMessage: 'Mute @{name}' },
|
||||
muteConversation : { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||
unmuteConversation : { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||
block : { id: 'status.block', defaultMessage: 'Block @{name}' },
|
||||
report : { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||
share : { id: 'status.share', defaultMessage: 'Share' },
|
||||
pin : { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||
unpin : { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||
embed : { id: 'status.embed', defaultMessage: 'Embed' },
|
||||
admin_account : { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||
admin_status : { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
||||
copy : { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
||||
blockDomain : { id: 'account.block_domain', defaultMessage: 'Hide everything from {domain}' },
|
||||
unblockDomain : { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||
unmute : { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||
unblock : { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, { status }) => ({
|
||||
|
@ -50,59 +50,59 @@ class ActionBar extends React.PureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
relationship: ImmutablePropTypes.map,
|
||||
onReply: PropTypes.func.isRequired,
|
||||
onReblog: PropTypes.func.isRequired,
|
||||
onFavourite: PropTypes.func.isRequired,
|
||||
onBookmark: PropTypes.func.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onDirect: PropTypes.func.isRequired,
|
||||
onMention: PropTypes.func.isRequired,
|
||||
onMute: PropTypes.func,
|
||||
onUnmute: PropTypes.func,
|
||||
onBlock: PropTypes.func,
|
||||
onUnblock: PropTypes.func,
|
||||
onBlockDomain: PropTypes.func,
|
||||
onUnblockDomain: PropTypes.func,
|
||||
status : ImmutablePropTypes.map.isRequired,
|
||||
relationship : ImmutablePropTypes.map,
|
||||
onReply : PropTypes.func.isRequired,
|
||||
onReblog : PropTypes.func.isRequired,
|
||||
onFavourite : PropTypes.func.isRequired,
|
||||
onBookmark : PropTypes.func.isRequired,
|
||||
onDelete : PropTypes.func.isRequired,
|
||||
onDirect : PropTypes.func.isRequired,
|
||||
onMention : PropTypes.func.isRequired,
|
||||
onMute : PropTypes.func,
|
||||
onUnmute : PropTypes.func,
|
||||
onBlock : PropTypes.func,
|
||||
onUnblock : PropTypes.func,
|
||||
onBlockDomain : PropTypes.func,
|
||||
onUnblockDomain : PropTypes.func,
|
||||
onMuteConversation: PropTypes.func,
|
||||
onReport: PropTypes.func,
|
||||
onPin: PropTypes.func,
|
||||
onEmbed: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onReport : PropTypes.func,
|
||||
onPin : PropTypes.func,
|
||||
onEmbed : PropTypes.func,
|
||||
intl : PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
handleReplyClick = () => {
|
||||
this.props.onReply(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleReblogClick = (e) => {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
}
|
||||
};
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
this.props.onFavourite(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleBookmarkClick = (e) => {
|
||||
this.props.onBookmark(this.props.status, e);
|
||||
}
|
||||
};
|
||||
|
||||
handleDeleteClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleRedraftClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleDirectClick = () => {
|
||||
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleMentionClick = () => {
|
||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleMuteClick = () => {
|
||||
const { status, relationship, onMute, onUnmute } = this.props;
|
||||
|
@ -113,7 +113,7 @@ class ActionBar extends React.PureComponent {
|
|||
} else {
|
||||
onMute(account);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockClick = () => {
|
||||
const { status, relationship, onBlock, onUnblock } = this.props;
|
||||
|
@ -124,44 +124,44 @@ class ActionBar extends React.PureComponent {
|
|||
} else {
|
||||
onBlock(status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockDomain = () => {
|
||||
const { status, onBlockDomain } = this.props;
|
||||
const account = status.get('account');
|
||||
|
||||
onBlockDomain(account.get('acct').split('@')[1]);
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockDomain = () => {
|
||||
const { status, onUnblockDomain } = this.props;
|
||||
const account = status.get('account');
|
||||
|
||||
onUnblockDomain(account.get('acct').split('@')[1]);
|
||||
}
|
||||
};
|
||||
|
||||
handleConversationMuteClick = () => {
|
||||
this.props.onMuteConversation(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleReport = () => {
|
||||
this.props.onReport(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handlePinClick = () => {
|
||||
this.props.onPin(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleShare = () => {
|
||||
navigator.share({
|
||||
text: this.props.status.get('search_index'),
|
||||
url: this.props.status.get('url'),
|
||||
url : this.props.status.get('url'),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleEmbed = () => {
|
||||
this.props.onEmbed(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleCopy = () => {
|
||||
const url = this.props.status.get('url');
|
||||
|
@ -180,9 +180,9 @@ class ActionBar extends React.PureComponent {
|
|||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { status, relationship, intl } = this.props;
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
|
@ -199,36 +199,66 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
if (me === status.getIn(['account', 'id'])) {
|
||||
if (publicStatus) {
|
||||
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin),
|
||||
action: this.handlePinClick,
|
||||
});
|
||||
} else {
|
||||
if (status.get('visibility') === 'private') {
|
||||
menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private),
|
||||
action: this.handleReblogClick,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation),
|
||||
action: this.handleConversationMuteClick,
|
||||
});
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
||||
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
|
||||
} else {
|
||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }),
|
||||
action: this.handleMentionClick,
|
||||
});
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }),
|
||||
action: this.handleDirectClick,
|
||||
});
|
||||
menu.push(null);
|
||||
|
||||
if (relationship && relationship.get('muting')) {
|
||||
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.unmute, { name: account.get('username') }),
|
||||
action: this.handleMuteClick,
|
||||
});
|
||||
} else {
|
||||
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.mute, { name: account.get('username') }),
|
||||
action: this.handleMuteClick,
|
||||
});
|
||||
}
|
||||
|
||||
if (relationship && relationship.get('blocking')) {
|
||||
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.unblock, { name: account.get('username') }),
|
||||
action: this.handleBlockClick,
|
||||
});
|
||||
} else {
|
||||
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.block, { name: account.get('username') }),
|
||||
action: this.handleBlockClick,
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
||||
menu.push({
|
||||
text : intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }),
|
||||
action: this.handleReport,
|
||||
});
|
||||
|
||||
if (account.get('acct') !== account.get('username')) {
|
||||
const domain = account.get('acct').split('@')[1];
|
||||
|
@ -244,13 +274,23 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
if (isStaff) {
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
||||
href: `/admin/accounts/${status.getIn(['account', 'id'])}`,
|
||||
});
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.admin_status),
|
||||
href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
|
||||
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.share)} icon='share-alt' onClick={this.handleShare} /></div>
|
||||
<div className='detailed-status__button'><IconButton
|
||||
title={intl.formatMessage(messages.share)}
|
||||
icon='share-alt'
|
||||
onClick={this.handleShare}
|
||||
/></div >
|
||||
);
|
||||
|
||||
let replyIcon;
|
||||
|
@ -268,16 +308,48 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className='detailed-status__action-bar'>
|
||||
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton disabled={reblog_disabled} active={status.get('reblogged')} title={reblog_disabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} /></div>
|
||||
<div className='detailed-status__button'><IconButton
|
||||
title={intl.formatMessage(messages.reply)}
|
||||
icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon}
|
||||
onClick={this.handleReplyClick}
|
||||
/></div >
|
||||
<div className='detailed-status__button'><IconButton
|
||||
disabled={reblog_disabled}
|
||||
active={status.get('reblogged')}
|
||||
title={reblog_disabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)}
|
||||
icon={reblogIcon}
|
||||
onClick={this.handleReblogClick}
|
||||
/></div >
|
||||
<div className='detailed-status__button'>
|
||||
<IconButton
|
||||
className='star-icon'
|
||||
animate
|
||||
active={status.get('favourited')}
|
||||
title={intl.formatMessage(messages.favourite)}
|
||||
icon='star'
|
||||
onClick={this.handleFavouriteClick}
|
||||
/></div >
|
||||
{shareButton}
|
||||
<div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>
|
||||
<div className='detailed-status__button'>
|
||||
<IconButton
|
||||
className='bookmark-icon'
|
||||
active={status.get('bookmarked')}
|
||||
title={intl.formatMessage(messages.bookmark)}
|
||||
icon='bookmark'
|
||||
onClick={this.handleBookmarkClick}
|
||||
/></div >
|
||||
|
||||
<div className='detailed-status__action-bar-dropdown'>
|
||||
<DropdownMenuContainer size={18} icon='ellipsis-h' status={status} items={menu} direction='left' title='More' />
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenuContainer
|
||||
size={18}
|
||||
icon='ellipsis-h'
|
||||
status={status}
|
||||
items={menu}
|
||||
direction='left'
|
||||
title='More'
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,41 +5,24 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { createSelector } from 'reselect';
|
||||
import { fetchStatus } from '../../actions/statuses';
|
||||
import { deleteStatus, fetchStatus, hideStatus, muteStatus, revealStatus, unmuteStatus } from '../../actions/statuses';
|
||||
import MissingIndicator from '../../components/missing_indicator';
|
||||
import DetailedStatus from './components/detailed_status';
|
||||
import ActionBar from './components/action_bar';
|
||||
import Column from '../ui/components/column';
|
||||
import {
|
||||
favourite,
|
||||
unfavourite,
|
||||
bookmark,
|
||||
unbookmark,
|
||||
reblog,
|
||||
unreblog,
|
||||
favourite,
|
||||
pin,
|
||||
reblog,
|
||||
unbookmark,
|
||||
unfavourite,
|
||||
unpin,
|
||||
unreblog,
|
||||
} from '../../actions/interactions';
|
||||
import {
|
||||
replyCompose,
|
||||
mentionCompose,
|
||||
directCompose,
|
||||
} from '../../actions/compose';
|
||||
import {
|
||||
muteStatus,
|
||||
unmuteStatus,
|
||||
deleteStatus,
|
||||
hideStatus,
|
||||
revealStatus,
|
||||
} from '../../actions/statuses';
|
||||
import {
|
||||
unblockAccount,
|
||||
unmuteAccount,
|
||||
} from '../../actions/accounts';
|
||||
import {
|
||||
blockDomain,
|
||||
unblockDomain,
|
||||
} from '../../actions/domain_blocks';
|
||||
import { directCompose, mentionCompose, replyCompose } from '../../actions/compose';
|
||||
import { unblockAccount, unmuteAccount } from '../../actions/accounts';
|
||||
import { blockDomain, unblockDomain } from '../../actions/domain_blocks';
|
||||
import { initMuteModal } from '../../actions/mutes';
|
||||
import { initBlockModal } from '../../actions/blocks';
|
||||
import { initReport } from '../../actions/reports';
|
||||
|
@ -49,24 +32,33 @@ import ColumnBackButton from '../../components/column_back_button';
|
|||
import ColumnHeader from '../../components/column_header';
|
||||
import StatusContainer from '../../containers/status_container';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import { boostModal, deleteModal } from '../../initial_state';
|
||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
|
||||
import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
|
||||
import { defaultMediaVisibility, textForScreenReader } from '../../components/status';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||
deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
|
||||
redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
||||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
||||
revealAll: { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
|
||||
hideAll: { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
|
||||
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||
deleteConfirm : { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||
deleteMessage : {
|
||||
id : 'confirmations.delete.message',
|
||||
defaultMessage: 'Are you sure you want to delete this status?',
|
||||
},
|
||||
redraftConfirm : { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
|
||||
redraftMessage : {
|
||||
id : 'confirmations.redraft.message',
|
||||
defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.',
|
||||
},
|
||||
revealAll : { id: 'status.show_more_all', defaultMessage: 'Show more for all' },
|
||||
hideAll : { id: 'status.show_less_all', defaultMessage: 'Show less for all' },
|
||||
detailedStatus : { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
||||
replyConfirm : { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||
replyMessage : {
|
||||
id : 'confirmations.reply.message',
|
||||
defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?',
|
||||
},
|
||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||
});
|
||||
|
||||
|
@ -116,7 +108,8 @@ const makeMapStateToProps = () => {
|
|||
let insertAt = descendantsIds.findIndex((id) => statuses.get(id).get('in_reply_to_account_id') !== statuses.get(id).get('account'));
|
||||
if (insertAt !== -1) {
|
||||
descendantsIds.forEach((id, idx) => {
|
||||
if (idx > insertAt && statuses.get(id).get('in_reply_to_account_id') === statuses.get(id).get('account')) {
|
||||
let account = statuses.get(id).get('account');
|
||||
if (idx > insertAt && statuses.get(id).get('in_reply_to_account_id') === account) {
|
||||
descendantsIds.splice(idx, 1);
|
||||
descendantsIds.splice(insertAt, 0, id);
|
||||
insertAt += 1;
|
||||
|
@ -142,7 +135,7 @@ const makeMapStateToProps = () => {
|
|||
ancestorsIds,
|
||||
descendantsIds,
|
||||
askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
|
||||
domain: state.getIn(['meta', 'domain']),
|
||||
domain : state.getIn(['meta', 'domain']),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -158,45 +151,48 @@ class Status extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
ancestorsIds: ImmutablePropTypes.list,
|
||||
descendantsIds: ImmutablePropTypes.list,
|
||||
intl: PropTypes.object.isRequired,
|
||||
params : PropTypes.object.isRequired,
|
||||
dispatch : PropTypes.func.isRequired,
|
||||
status : ImmutablePropTypes.map,
|
||||
ancestorsIds : ImmutablePropTypes.list,
|
||||
descendantsIds : ImmutablePropTypes.list,
|
||||
intl : PropTypes.object.isRequired,
|
||||
askReplyConfirmation: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
domain: PropTypes.string.isRequired,
|
||||
multiColumn : PropTypes.bool,
|
||||
domain : PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
fullscreen: false,
|
||||
showMedia: defaultMediaVisibility(this.props.status),
|
||||
fullscreen : false,
|
||||
showMedia : defaultMediaVisibility(this.props.status),
|
||||
loadedStatusId: undefined,
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
componentWillMount() {
|
||||
this.props.dispatch(fetchStatus(this.props.params.statusId));
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
attachFullscreenListener(this.onFullScreenChange);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
|
||||
this._scrolledIntoView = false;
|
||||
this.props.dispatch(fetchStatus(nextProps.params.statusId));
|
||||
}
|
||||
|
||||
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
||||
this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') });
|
||||
this.setState({
|
||||
showMedia : defaultMediaVisibility(nextProps.status),
|
||||
loadedStatusId: nextProps.status.get('id'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleMediaVisibility = () => {
|
||||
this.setState({ showMedia: !this.state.showMedia });
|
||||
}
|
||||
};
|
||||
|
||||
handleFavouriteClick = (status) => {
|
||||
if (status.get('favourited')) {
|
||||
|
@ -204,7 +200,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(favourite(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handlePin = (status) => {
|
||||
if (status.get('pinned')) {
|
||||
|
@ -212,24 +208,24 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(pin(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleReplyClick = (status) => {
|
||||
let { askReplyConfirmation, dispatch, intl } = this.props;
|
||||
if (askReplyConfirmation) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: intl.formatMessage(messages.replyMessage),
|
||||
confirm: intl.formatMessage(messages.replyConfirm),
|
||||
message : intl.formatMessage(messages.replyMessage),
|
||||
confirm : intl.formatMessage(messages.replyConfirm),
|
||||
onConfirm: () => dispatch(replyCompose(status, this.context.router.history)),
|
||||
}));
|
||||
} else {
|
||||
dispatch(replyCompose(status, this.context.router.history));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleModalReblog = (status) => {
|
||||
this.props.dispatch(reblog(status));
|
||||
}
|
||||
};
|
||||
|
||||
handleReblogClick = (status, e) => {
|
||||
if (status.get('reblogged')) {
|
||||
|
@ -241,7 +237,7 @@ class Status extends ImmutablePureComponent {
|
|||
this.props.dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBookmarkClick = (status) => {
|
||||
if (status.get('bookmarked')) {
|
||||
|
@ -249,7 +245,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(bookmark(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDeleteClick = (status, history, withRedraft = false) => {
|
||||
const { dispatch, intl } = this.props;
|
||||
|
@ -258,28 +254,28 @@ class Status extends ImmutablePureComponent {
|
|||
dispatch(deleteStatus(status.get('id'), history, withRedraft));
|
||||
} else {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
|
||||
confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
|
||||
message : intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
|
||||
confirm : intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
|
||||
onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDirectClick = (account, router) => {
|
||||
this.props.dispatch(directCompose(account, router));
|
||||
}
|
||||
};
|
||||
|
||||
handleMentionClick = (account, router) => {
|
||||
this.props.dispatch(mentionCompose(account, router));
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenMedia = (media, index) => {
|
||||
this.props.dispatch(openModal('MEDIA', { media, index }));
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenVideo = (media, time) => {
|
||||
this.props.dispatch(openModal('VIDEO', { media, time }));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenMedia = e => {
|
||||
const status = this._properStatus();
|
||||
|
@ -295,11 +291,11 @@ class Status extends ImmutablePureComponent {
|
|||
this.handleOpenMedia(status.get('media_attachments'), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMuteClick = (account) => {
|
||||
this.props.dispatch(initMuteModal(account));
|
||||
}
|
||||
};
|
||||
|
||||
handleConversationMuteClick = (status) => {
|
||||
if (status.get('muted')) {
|
||||
|
@ -307,7 +303,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(muteStatus(status.get('id')));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleToggleHidden = (status) => {
|
||||
if (status.get('hidden')) {
|
||||
|
@ -315,7 +311,7 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(hideStatus(status.get('id')));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleToggleAll = () => {
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
|
@ -326,80 +322,83 @@ class Status extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.dispatch(hideStatus(statusIds));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockClick = (status) => {
|
||||
const { dispatch } = this.props;
|
||||
const account = status.get('account');
|
||||
dispatch(initBlockModal(account));
|
||||
}
|
||||
};
|
||||
|
||||
handleReport = (status) => {
|
||||
this.props.dispatch(initReport(status.get('account'), status));
|
||||
}
|
||||
};
|
||||
|
||||
handleEmbed = (status) => {
|
||||
this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
|
||||
}
|
||||
};
|
||||
|
||||
handleUnmuteClick = account => {
|
||||
this.props.dispatch(unmuteAccount(account.get('id')));
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockClick = account => {
|
||||
this.props.dispatch(unblockAccount(account.get('id')));
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockDomainClick = domain => {
|
||||
this.props.dispatch(openModal('CONFIRM', {
|
||||
message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
|
||||
confirm: this.props.intl.formatMessage(messages.blockDomainConfirm),
|
||||
message : <FormattedMessage
|
||||
id='confirmations.domain_block.message'
|
||||
defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.'
|
||||
values={{ domain: <strong >{domain}</strong > }}
|
||||
/>,
|
||||
confirm : this.props.intl.formatMessage(messages.blockDomainConfirm),
|
||||
onConfirm: () => this.props.dispatch(blockDomain(domain)),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockDomainClick = domain => {
|
||||
this.props.dispatch(unblockDomain(domain));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = () => {
|
||||
this.handleMoveUp(this.props.status.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = () => {
|
||||
this.handleMoveDown(this.props.status.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyReply = e => {
|
||||
e.preventDefault();
|
||||
this.handleReplyClick(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
this.handleFavouriteClick(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyBoost = () => {
|
||||
this.handleReblogClick(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
e.preventDefault();
|
||||
this.handleMentionClick(this.props.status.get('account'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleHidden = () => {
|
||||
this.handleToggleHidden(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleSensitive = () => {
|
||||
this.handleToggleMediaVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveUp = id => {
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
|
@ -416,7 +415,7 @@ class Status extends ImmutablePureComponent {
|
|||
this._selectChild(index - 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveDown = id => {
|
||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||
|
@ -433,9 +432,9 @@ class Status extends ImmutablePureComponent {
|
|||
this._selectChild(index + 1, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_selectChild (index, align_top) {
|
||||
_selectChild(index, align_top) {
|
||||
const container = this.node;
|
||||
const element = container.querySelectorAll('.focusable')[index];
|
||||
|
||||
|
@ -449,7 +448,7 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
renderChildren (list) {
|
||||
renderChildren(list) {
|
||||
return list.map(id => (
|
||||
<StatusContainer
|
||||
key={id}
|
||||
|
@ -463,9 +462,9 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate () {
|
||||
componentDidUpdate() {
|
||||
if (this._scrolledIntoView) {
|
||||
return;
|
||||
}
|
||||
|
@ -482,65 +481,85 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
detachFullscreenListener(this.onFullScreenChange);
|
||||
}
|
||||
|
||||
onFullScreenChange = () => {
|
||||
this.setState({ fullscreen: isFullscreen() });
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
let ancestors, descendants;
|
||||
const { shouldUpdateScroll, status, ancestorsIds, descendantsIds, intl, domain, multiColumn } = this.props;
|
||||
const { fullscreen } = this.state;
|
||||
|
||||
if (status === null) {
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<ColumnBackButton multiColumn={multiColumn} />
|
||||
<MissingIndicator />
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
if (ancestorsIds && ancestorsIds.size > 0) {
|
||||
ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
|
||||
ancestors = <div >{this.renderChildren(ancestorsIds)}</div >;
|
||||
}
|
||||
|
||||
if (descendantsIds && descendantsIds.size > 0) {
|
||||
descendants = <div>{this.renderChildren(descendantsIds)}</div>;
|
||||
descendants = <div >{this.renderChildren(descendantsIds)}</div >;
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
moveUp: this.handleHotkeyMoveUp,
|
||||
moveDown: this.handleHotkeyMoveDown,
|
||||
reply: this.handleHotkeyReply,
|
||||
favourite: this.handleHotkeyFavourite,
|
||||
boost: this.handleHotkeyBoost,
|
||||
mention: this.handleHotkeyMention,
|
||||
openProfile: this.handleHotkeyOpenProfile,
|
||||
toggleHidden: this.handleHotkeyToggleHidden,
|
||||
moveUp : this.handleHotkeyMoveUp,
|
||||
moveDown : this.handleHotkeyMoveDown,
|
||||
reply : this.handleHotkeyReply,
|
||||
favourite : this.handleHotkeyFavourite,
|
||||
boost : this.handleHotkeyBoost,
|
||||
mention : this.handleHotkeyMention,
|
||||
openProfile : this.handleHotkeyOpenProfile,
|
||||
toggleHidden : this.handleHotkeyToggleHidden,
|
||||
toggleSensitive: this.handleHotkeyToggleSensitive,
|
||||
openMedia: this.handleHotkeyOpenMedia,
|
||||
openMedia : this.handleHotkeyOpenMedia,
|
||||
};
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.detailedStatus)}>
|
||||
<Column
|
||||
bindToDocument={!multiColumn}
|
||||
label={intl.formatMessage(messages.detailedStatus)}
|
||||
>
|
||||
<ColumnHeader
|
||||
showBackButton
|
||||
multiColumn={multiColumn}
|
||||
extraButton={(
|
||||
<button className='column-header__button' title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} onClick={this.handleToggleAll} aria-pressed={status.get('hidden') ? 'false' : 'true'}><Icon id={status.get('hidden') ? 'eye-slash' : 'eye'} /></button>
|
||||
<button
|
||||
className='column-header__button'
|
||||
title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)}
|
||||
aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)}
|
||||
onClick={this.handleToggleAll}
|
||||
aria-pressed={status.get('hidden') ? 'false' : 'true'}
|
||||
><Icon id={status.get('hidden') ? 'eye-slash' : 'eye'} /></button >
|
||||
)}
|
||||
/>
|
||||
|
||||
<ScrollContainer scrollKey='thread' shouldUpdateScroll={shouldUpdateScroll}>
|
||||
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}>
|
||||
<ScrollContainer
|
||||
scrollKey='thread'
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
>
|
||||
<div
|
||||
className={classNames('scrollable', { fullscreen })}
|
||||
ref={this.setRef}
|
||||
>
|
||||
{/*<h2 className='debug'>ancestors:</h2 >*/}
|
||||
{ancestors}
|
||||
|
||||
{/*<h2 className='debug'>common:</h2 >*/}
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
|
||||
<div
|
||||
className={classNames('focusable', 'detailed-status__wrapper')}
|
||||
tabIndex='0'
|
||||
aria-label={textForScreenReader(intl, status, false)}
|
||||
>
|
||||
<DetailedStatus
|
||||
status={status}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
|
@ -571,13 +590,13 @@ class Status extends ImmutablePureComponent {
|
|||
onPin={this.handlePin}
|
||||
onEmbed={this.handleEmbed}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
||||
</div >
|
||||
</HotKeys >
|
||||
{/*<h2 className='debug'>Descendants:</h2 >*/}
|
||||
{descendants}
|
||||
</div>
|
||||
</ScrollContainer>
|
||||
</Column>
|
||||
</div >
|
||||
</ScrollContainer >
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
import TabsBar, { links, getIndex, getLink } from './tabs_bar';
|
||||
import TabsBar, { getIndex, getLink, links } from './tabs_bar';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import BundleContainer from '../containers/bundle_container';
|
||||
|
@ -13,17 +13,17 @@ import ColumnLoading from './column_loading';
|
|||
import DrawerLoading from './drawer_loading';
|
||||
import BundleColumnError from './bundle_column_error';
|
||||
import {
|
||||
Compose,
|
||||
Notifications,
|
||||
HomeTimeline,
|
||||
BookmarkedStatuses,
|
||||
CommunityTimeline,
|
||||
PublicTimeline,
|
||||
HashtagTimeline,
|
||||
Compose,
|
||||
Directory,
|
||||
DirectTimeline,
|
||||
FavouritedStatuses,
|
||||
BookmarkedStatuses,
|
||||
HashtagTimeline,
|
||||
HomeTimeline,
|
||||
ListTimeline,
|
||||
Directory,
|
||||
Notifications,
|
||||
PublicTimeline,
|
||||
} from '../../ui/util/async-components';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import ComposePanel from './compose_panel';
|
||||
|
@ -31,19 +31,20 @@ import NavigationPanel from './navigation_panel';
|
|||
|
||||
import detectPassiveEvents from 'detect-passive-events';
|
||||
import { scrollRight } from '../../../scroll';
|
||||
import LinkFooter from './link_footer';
|
||||
|
||||
const componentMap = {
|
||||
'COMPOSE': Compose,
|
||||
'HOME': HomeTimeline,
|
||||
'COMPOSE' : Compose,
|
||||
'HOME' : HomeTimeline,
|
||||
'NOTIFICATIONS': Notifications,
|
||||
'PUBLIC': PublicTimeline,
|
||||
'COMMUNITY': CommunityTimeline,
|
||||
'HASHTAG': HashtagTimeline,
|
||||
'DIRECT': DirectTimeline,
|
||||
'FAVOURITES': FavouritedStatuses,
|
||||
'BOOKMARKS': BookmarkedStatuses,
|
||||
'LIST': ListTimeline,
|
||||
'DIRECTORY': Directory,
|
||||
'PUBLIC' : PublicTimeline,
|
||||
'COMMUNITY' : CommunityTimeline,
|
||||
'HASHTAG' : HashtagTimeline,
|
||||
'DIRECT' : DirectTimeline,
|
||||
'FAVOURITES' : FavouritedStatuses,
|
||||
'BOOKMARKS' : BookmarkedStatuses,
|
||||
'LIST' : ListTimeline,
|
||||
'DIRECTORY' : Directory,
|
||||
};
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -60,16 +61,16 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
columns: ImmutablePropTypes.list.isRequired,
|
||||
isModalOpen: PropTypes.bool.isRequired,
|
||||
intl : PropTypes.object.isRequired,
|
||||
columns : ImmutablePropTypes.list.isRequired,
|
||||
isModalOpen : PropTypes.bool.isRequired,
|
||||
singleColumn: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
children : PropTypes.node,
|
||||
};
|
||||
|
||||
state = {
|
||||
shouldAnimate: false,
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps() {
|
||||
this.setState({ shouldAnimate: false });
|
||||
|
@ -100,7 +101,7 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
this.setState({ shouldAnimate: true });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
if (!this.props.singleColumn) {
|
||||
this.node.removeEventListener('wheel', this.handleWheel);
|
||||
}
|
||||
|
@ -129,14 +130,14 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
this.context.router.history.push(getLink(this.pendingIndex));
|
||||
this.pendingIndex = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAnimationEnd = () => {
|
||||
if (typeof this.pendingIndex === 'number') {
|
||||
this.context.router.history.push(getLink(this.pendingIndex));
|
||||
this.pendingIndex = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleWheel = () => {
|
||||
if (typeof this._interruptScrollAnimation !== 'function') {
|
||||
|
@ -144,11 +145,11 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this._interruptScrollAnimation();
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (node) => {
|
||||
this.node = node;
|
||||
}
|
||||
};
|
||||
|
||||
renderView = (link, index) => {
|
||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||
|
@ -157,80 +158,119 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||
|
||||
const view = (index === columnIndex) ?
|
||||
React.cloneElement(this.props.children) :
|
||||
<ColumnLoading title={title} icon={icon} />;
|
||||
(<ColumnLoading
|
||||
title={title}
|
||||
icon={icon}
|
||||
/>);
|
||||
|
||||
return (
|
||||
<div className='columns-area columns-area--mobile' key={index}>
|
||||
<div
|
||||
className='columns-area columns-area--mobile'
|
||||
key={index}
|
||||
>
|
||||
{view}
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderLoading = columnId => () => {
|
||||
return columnId === 'COMPOSE' ? <DrawerLoading /> : <ColumnLoading />;
|
||||
}
|
||||
};
|
||||
|
||||
renderError = (props) => {
|
||||
return <BundleColumnError {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { columns, children, singleColumn, isModalOpen, intl } = this.props;
|
||||
const { shouldAnimate } = this.state;
|
||||
|
||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||
|
||||
if (singleColumn) {
|
||||
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
|
||||
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : (<Link
|
||||
key='floating-action-button'
|
||||
to='/statuses/new'
|
||||
className='floating-action-button'
|
||||
aria-label={intl.formatMessage(messages.publish)}
|
||||
><Icon id='pencil' /></Link >);
|
||||
|
||||
const content = columnIndex !== -1 ? (
|
||||
<ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>
|
||||
<ReactSwipeableViews
|
||||
key='content'
|
||||
hysteresis={0.2}
|
||||
threshold={15}
|
||||
index={columnIndex}
|
||||
onChangeIndex={this.handleSwipe}
|
||||
onTransitionEnd={this.handleAnimationEnd}
|
||||
animateTransitions={shouldAnimate}
|
||||
springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
{links.map(this.renderView)}
|
||||
</ReactSwipeableViews>
|
||||
</ReactSwipeableViews >
|
||||
) : (
|
||||
<div key='content' className='columns-area columns-area--mobile'>{children}</div>
|
||||
<div
|
||||
key='content'
|
||||
className='columns-area columns-area--mobile'
|
||||
>{children}</div >
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='columns-area__panels'>
|
||||
<LinkFooter withHotkeys />
|
||||
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
|
||||
<div className='columns-area__panels__pane__inner'>
|
||||
<ComposePanel />
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
</div >
|
||||
|
||||
<div className='columns-area__panels__main'>
|
||||
<TabsBar key='tabs' />
|
||||
{content}
|
||||
</div>
|
||||
</div >
|
||||
|
||||
<div className='columns-area__panels__pane columns-area__panels__pane--start columns-area__panels__pane--navigational'>
|
||||
<div className='columns-area__panels__pane__inner'>
|
||||
<NavigationPanel />
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
</div >
|
||||
|
||||
{floatingActionButton}
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`columns-area ${ isModalOpen ? 'unscrollable' : '' }`} ref={this.setRef}>
|
||||
<div
|
||||
className={`columns-area ${isModalOpen ? 'unscrollable' : ''}`}
|
||||
ref={this.setRef}
|
||||
>
|
||||
{columns.map(column => {
|
||||
const params = column.get('params', null) === null ? null : column.get('params').toJS();
|
||||
const other = params && params.other ? params.other : {};
|
||||
|
||||
return (
|
||||
<BundleContainer key={column.get('uuid')} fetchComponent={componentMap[column.get('id')]} loading={this.renderLoading(column.get('id'))} error={this.renderError}>
|
||||
{SpecificComponent => <SpecificComponent columnId={column.get('uuid')} params={params} multiColumn {...other} />}
|
||||
</BundleContainer>
|
||||
<BundleContainer
|
||||
key={column.get('uuid')}
|
||||
fetchComponent={componentMap[column.get('id')]}
|
||||
loading={this.renderLoading(column.get('id'))}
|
||||
error={this.renderError}
|
||||
>
|
||||
{SpecificComponent => (<SpecificComponent
|
||||
columnId={column.get('uuid')}
|
||||
params={params}
|
||||
multiColumn {...other}
|
||||
/>)}
|
||||
|
||||
</BundleContainer >
|
||||
);
|
||||
})}
|
||||
|
||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
|
||||
</div>
|
||||
);
|
||||
</div >
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,15 +2,20 @@ import React from 'react';
|
|||
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||
import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
|
||||
import LinkFooter from './link_footer';
|
||||
import InstantMessaging from './messaging/instantMessaging';
|
||||
|
||||
const showIM = false;
|
||||
const ComposePanel = () => (
|
||||
<div className='compose-panel'>
|
||||
<SearchContainer openInRoute />
|
||||
<NavigationContainer />
|
||||
<ComposeFormContainer singleColumn />
|
||||
<LinkFooter withHotkeys />
|
||||
</div>
|
||||
|
||||
{showIM && (
|
||||
<InstantMessaging />
|
||||
)}
|
||||
|
||||
</div >
|
||||
);
|
||||
|
||||
export default ComposePanel;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { connect } from 'react-redux';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { invitesEnabled, version, repository, source_url } from 'mastodon/initial_state';
|
||||
import { invitesEnabled, repository, source_url, version } from 'mastodon/initial_state';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { isStaff } from '../../../initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
|
||||
|
@ -13,23 +14,31 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
onLogout () {
|
||||
onLogout() {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: intl.formatMessage(messages.logoutMessage),
|
||||
confirm: intl.formatMessage(messages.logoutConfirm),
|
||||
message : intl.formatMessage(messages.logoutMessage),
|
||||
confirm : intl.formatMessage(messages.logoutConfirm),
|
||||
onConfirm: () => logOut(),
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
// const themeIsDark = true;
|
||||
// const displaythemetoggler = true;
|
||||
export default @injectIntl
|
||||
@connect(null, mapDispatchToProps)
|
||||
class LinkFooter extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
withHotkeys: PropTypes.bool,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
enableChristmasSnow: PropTypes.bool,
|
||||
snowActive : PropTypes.bool,
|
||||
withHotkeys : PropTypes.bool,
|
||||
snow : PropTypes.func,
|
||||
onLogout : PropTypes.func.isRequired,
|
||||
intl : PropTypes.object.isRequired,
|
||||
};
|
||||
static defaultProps = {
|
||||
enableChristmasSnow: true,
|
||||
};
|
||||
|
||||
handleLogoutClick = e => {
|
||||
|
@ -39,33 +48,228 @@ class LinkFooter extends React.PureComponent {
|
|||
this.props.onLogout();
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
Date.prototype.getWeek = function () {
|
||||
var onejan = new Date(this.getFullYear(), 0, 1);
|
||||
return Math.ceil((((this - onejan) / 86400000) + onejan.getDay() + 1) / 7);
|
||||
};
|
||||
|
||||
var weekNumber = (new Date()).getWeek();
|
||||
// display snow during the last two weeks of the year
|
||||
console.log('week', weekNumber);
|
||||
const shouldWeDisplaySnow = (weekNumber > 48) && props.enableChristmasSnow;
|
||||
|
||||
this.state = { enableChristmasSnow: shouldWeDisplaySnow };
|
||||
|
||||
// make snow effect
|
||||
if (shouldWeDisplaySnow) {
|
||||
import('../../../utils/snowstorm-min')
|
||||
.then((snowstorm) => {
|
||||
Window.snowstorm = snowstorm.default;
|
||||
this.state.snow = Window.snowstorm;
|
||||
console.log('this.state.snow ', this.state.snow);
|
||||
// snowstorm.start();
|
||||
this.state.snowActive = true;
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
}
|
||||
|
||||
render () {
|
||||
}
|
||||
|
||||
toggleSnow = () => {
|
||||
console.log('toggle snow');
|
||||
if (this.state.snow) {
|
||||
if (this.state.snowActive) {
|
||||
this.state.snow.stop();
|
||||
this.state.snowActive = false;
|
||||
} else {
|
||||
this.state.snow.start();
|
||||
this.state.snowActive = true;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
changeTheme(newTheme) {
|
||||
console.log('change theme en ', newTheme);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { withHotkeys } = this.props;
|
||||
var snowClasses = this.props.snowActive ? 'snow-button active' : 'snow-button ';
|
||||
|
||||
return (
|
||||
<div className='getting-started__footer'>
|
||||
<ul>
|
||||
{invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
|
||||
{withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>}
|
||||
<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
|
||||
<li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>
|
||||
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
|
||||
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
|
||||
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
|
||||
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
|
||||
<li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<div className='getting-started__footer'>
|
||||
|
||||
<div className='extras'>
|
||||
|
||||
{/*// TODO*/}
|
||||
{/*<button className='mod-theme btn btn-block'>*/}
|
||||
{/* {themeIsDark ? (*/}
|
||||
{/* <span*/}
|
||||
{/* onClick={this.changeTheme('light')}*/}
|
||||
{/* title='set light'*/}
|
||||
{/* >*/}
|
||||
{/* <i className='fa fa-pencil-o' />*/}
|
||||
{/* </span >*/}
|
||||
{/* ) : (*/}
|
||||
{/* <span*/}
|
||||
{/* onClick={this.changeTheme('dark')}*/}
|
||||
{/* title='set dark'*/}
|
||||
{/* >*/}
|
||||
{/* <i className='fa fa-pencil' />*/}
|
||||
{/* </span >*/}
|
||||
{/* )}*/}
|
||||
{/*</button >*/}
|
||||
|
||||
{this.state.enableChristmasSnow && (
|
||||
<div
|
||||
onClick={this.toggleSnow}
|
||||
className='christmas-snow'
|
||||
>
|
||||
<div className={snowClasses}>
|
||||
<i
|
||||
className='icon fa fa-snowflake-o'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</div >
|
||||
<div > Joyeuses fêtes!</div >
|
||||
{isStaff && (
|
||||
|
||||
<a href='/admin/tags?pending_review=1'>
|
||||
<i className='fa fa-fire' />
|
||||
Trending hashtags</a >
|
||||
|
||||
)}
|
||||
<br />
|
||||
<div className='external-utilities'>
|
||||
<a href='https://mastodon.cipherbliss.com/@tykayn'>
|
||||
<i className='fa fa-paper-plane' />
|
||||
contactez nous
|
||||
</a >
|
||||
<a href='https://liberapay.com/cipherbliss'><i className='fa fa-coffee' /> Supportez Cipherbliss</a >
|
||||
|
||||
<a href='https://peertube.cipherbliss.com'> <i className='fa fa-play ' /> Videos</a >
|
||||
<a href='https://framadate.org/'> <i className='fa fa-calendar' /> FramaDate</a >
|
||||
<a href='https://framapad.org/'> <i className='fa fa-file-text' /> Pad</a >
|
||||
<a href='https://framagit.org/tykayn/mastodon'> <i className='fa fa-gitlab' /> Source</a >
|
||||
<hr />
|
||||
<a href='/web/timelines/tag/vélo'> <i className='fa fa-bicycle' /> #vélo</a >
|
||||
<a href='/web/timelines/tag/openstreetmap'> <i className='fa fa-map-o' /> #OpenStreetMap</a >
|
||||
<a href='/web/timelines/tag/mastoart'> <i className='fa fa-paint-brush' /> #Mastoart</a >
|
||||
<a href='/web/timelines/tag/ironèmes'> <i className='fa fa-file-text-o' /> #ironèmes</a >
|
||||
</div >
|
||||
</div >
|
||||
)}
|
||||
</div >
|
||||
|
||||
<ul >
|
||||
|
||||
{invitesEnabled && <li ><a
|
||||
href='/invites'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='getting_started.invite'
|
||||
defaultMessage='Invite people'
|
||||
/> ·</a >
|
||||
</li >}
|
||||
{withHotkeys && <li ><Link to='/keyboard-shortcuts'>
|
||||
<FormattedMessage
|
||||
id='navigation_bar.keyboard_shortcuts'
|
||||
defaultMessage='Hotkeys'
|
||||
/> ·
|
||||
</Link >
|
||||
</li >}
|
||||
<li >
|
||||
<a href='/auth/edit'>
|
||||
<FormattedMessage
|
||||
id='getting_started.security'
|
||||
defaultMessage='Security'
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li >
|
||||
<a
|
||||
href='/about/more'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='navigation_bar.info'
|
||||
defaultMessage='About this server'
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li >
|
||||
<a
|
||||
href='https://joinmastodon.org/apps'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='navigation_bar.apps'
|
||||
defaultMessage='Mobile apps'
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li >
|
||||
<a
|
||||
href='/terms'
|
||||
target='_blank'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='getting_started.terms'
|
||||
defaultMessage='Terms of service'
|
||||
/> ·</a >
|
||||
</li >
|
||||
<li >
|
||||
<a
|
||||
href='/settings/applications'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='getting_started.developers'
|
||||
defaultMessage='Developers'
|
||||
/> ·
|
||||
</a >
|
||||
</li >
|
||||
<li >
|
||||
<a
|
||||
href='https://docs.joinmastodon.org'
|
||||
target='_blank'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='getting_started.documentation'
|
||||
defaultMessage='Documentation'
|
||||
/>
|
||||
</a > ·
|
||||
</li >
|
||||
<li >
|
||||
<a
|
||||
href='/auth/sign_out'
|
||||
onClick={this.handleLogoutClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='navigation_bar.logout'
|
||||
defaultMessage='Logout'
|
||||
/>
|
||||
</a >
|
||||
</li >
|
||||
</ul >
|
||||
|
||||
<p >
|
||||
<FormattedMessage
|
||||
id='getting_started.open_source_notice'
|
||||
defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.'
|
||||
values={{ github: <span><a href={source_url} rel='noopener noreferrer' target='_blank'>{repository}</a> (v{version})</span> }}
|
||||
defaultMessage='Mastodon is open source software. You can contribute or report issues on the forge at {forge}.'
|
||||
values={{
|
||||
forge: <span ><a
|
||||
href={source_url}
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>{repository}</a > (v{version})</span >,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</p >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default class ContactsList extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
showList : PropTypes.bool,
|
||||
contactList : PropTypes.array,
|
||||
conversationList: PropTypes.array,
|
||||
};
|
||||
static defaultProps = {
|
||||
showList : true,
|
||||
contactList: ['machin', 'bidule', 'chuck norris'],
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showList : true,
|
||||
contactList : ['machin', 'bidule', 'chuck norris'],
|
||||
conversationList: ['machin', 'bidule', 'chuck norris'],
|
||||
};
|
||||
}
|
||||
|
||||
submitCompose() {
|
||||
console.log('submit message');
|
||||
}
|
||||
|
||||
toggleList = () => {
|
||||
console.log('toggle');
|
||||
this.setState((state) => {
|
||||
console.log('state.showList', state.showList);
|
||||
return {
|
||||
showList: !state.showList,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
// return (
|
||||
// <div >
|
||||
// liste de contacts
|
||||
// </div >
|
||||
// );
|
||||
|
||||
const renderedList = this.state.contactList.forEach(elem => {
|
||||
return (
|
||||
<li className='contact-item'>
|
||||
{elem}
|
||||
</li >
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div className='messaging-container'>
|
||||
<div className='messaging-box'>
|
||||
<div className='title column-header'>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-envelope column-header__icon fa-fw'
|
||||
/>
|
||||
Messaging box
|
||||
|
||||
</div >
|
||||
<div className='user-list column-header'>
|
||||
<h2 className='title'>la liste de {this.state.contactList.lengh} contacts
|
||||
|
||||
<button
|
||||
className='btn btn-primary'
|
||||
onClick={this.toggleList}
|
||||
>
|
||||
<i className='fa fa-caret-up' />
|
||||
</button >
|
||||
</h2 >
|
||||
{this.state.showList && (
|
||||
<div className='contact-list-container'>
|
||||
<h3 >show list</h3 >
|
||||
<ul className='contact-list'>
|
||||
{renderedList}
|
||||
</ul >
|
||||
</div >
|
||||
)}
|
||||
</div >
|
||||
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
import React from 'react';
|
||||
|
||||
export default class ConversationItem extends React.PureComponent {
|
||||
|
||||
following = [];
|
||||
|
||||
render() {
|
||||
|
||||
const list = (
|
||||
<li className='conversations_item has-new-message'>
|
||||
<div className='title'>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-envelope column-header__icon fa-fw'
|
||||
/>
|
||||
Un Gens
|
||||
<span className='new-message-counter'>
|
||||
(3)</span >
|
||||
<button className='btn-small'>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-caret-down column-header__icon fa-fw'
|
||||
/>
|
||||
</button >
|
||||
</div >
|
||||
<div className='conversation_stream'>
|
||||
<div className='message theirs'>
|
||||
<p >oh hello there! 😋 </p >
|
||||
<div className='arrow-down' />
|
||||
</div >
|
||||
<div className='message mine'>
|
||||
<p >General Emoji</p >
|
||||
<div className='arrow-down' />
|
||||
</div >
|
||||
<div className='message theirs'>
|
||||
<p >we just achieved comedy</p >
|
||||
<div className='arrow-down' />
|
||||
</div >
|
||||
</div >
|
||||
<div className='conversation_input'>
|
||||
<form
|
||||
action='#'
|
||||
onSubmit={this.submitCompose()}
|
||||
>
|
||||
<textarea
|
||||
name='messager'
|
||||
id=''
|
||||
cols='15'
|
||||
rows='3'
|
||||
className='messager-textarea'
|
||||
placeholder='allez dis nous tout'
|
||||
|
||||
/>
|
||||
<input
|
||||
type='submit'
|
||||
name='submit'
|
||||
value='Send'
|
||||
/>
|
||||
</form >
|
||||
</div >
|
||||
</li >
|
||||
);
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import React from 'react';
|
||||
|
||||
export default class ConversationStream extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class='conversation-stream'>
|
||||
ConversationStream todo
|
||||
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const following = ['bidule', 'chose', 'truc'];
|
||||
|
||||
export default class Conversation extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
following: PropTypes.array,
|
||||
// conversations: PropTypes.array,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
following: following,
|
||||
};
|
||||
|
||||
openConversationWith(name) {
|
||||
console.log('openConversationWith name', name);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return this.props.following.map(elem =>
|
||||
|
||||
(<li className='user-item'>
|
||||
<div
|
||||
className='username'
|
||||
onClick={this.openConversationWith(elem)}
|
||||
>
|
||||
Machin {elem}
|
||||
</div >
|
||||
<div className='last-active'>3 min</div >
|
||||
</li >),
|
||||
);
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
import React from 'react';
|
||||
import ContactsList from './contacts-list';
|
||||
|
||||
export default class InstantMessaging extends React.PureComponent {
|
||||
|
||||
// static propTypes = {
|
||||
// following : PropTypes.array,
|
||||
// conversations: PropTypes.array,
|
||||
// };
|
||||
// static defaultProps = {
|
||||
// threadsCompile: true,
|
||||
// };
|
||||
// openConversationWith(account) {
|
||||
// let conversationFound = account;
|
||||
// if conversation exist, focus on it
|
||||
// if (conversationFound) {
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
// else, create conversation and focus on it
|
||||
// };
|
||||
|
||||
// submitCompose() {
|
||||
//
|
||||
// };
|
||||
|
||||
// constructor() {
|
||||
// super();
|
||||
//
|
||||
// this.props.conversations = [
|
||||
// {
|
||||
// withAccount: '@machin',
|
||||
// messages : [],
|
||||
// opened : true,
|
||||
// },
|
||||
// {
|
||||
// withAccount: '@chuck',
|
||||
// messages : [],
|
||||
// opened : false,
|
||||
// },
|
||||
// ];
|
||||
// this.props.following = [
|
||||
// { username: 'wulfila', handle: '@wulfila' },
|
||||
// { username: 'machin', handle: '@machin' },
|
||||
// { username: 'chuck norris', handle: '@chuck' },
|
||||
// ];
|
||||
//
|
||||
// }
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div >
|
||||
<div className='debug'>
|
||||
|
||||
messagerie todo
|
||||
|
||||
<ContactsList />
|
||||
</div >
|
||||
{/*<Conversation />*/}
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
};
|
|
@ -10,27 +10,154 @@ import TrendsContainer from 'mastodon/features/getting_started/containers/trends
|
|||
|
||||
const NavigationPanel = () => (
|
||||
<div className='navigation-panel'>
|
||||
<NavLink className='column-link column-link--transparent' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon className='column-link__icon' id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>
|
||||
<NavLink className='column-link column-link--transparent' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon className='column-link__icon' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>
|
||||
<div className='small-texts timelines'>
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/home'
|
||||
data-preview-title-id='column.home'
|
||||
data-preview-icon='home'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='home'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.home'
|
||||
defaultMessage='Home'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/public/local'
|
||||
data-preview-title-id='column.community'
|
||||
data-preview-icon='users'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.local_timeline'
|
||||
defaultMessage='Local'
|
||||
/></NavLink >
|
||||
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
exact
|
||||
to='/timelines/public'
|
||||
data-preview-title-id='column.public'
|
||||
data-preview-icon='globe'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='globe'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.federated_timeline'
|
||||
defaultMessage='Federated'
|
||||
/></NavLink >
|
||||
|
||||
</div >
|
||||
<div className='spacer'></div >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/notifications'
|
||||
data-preview-title-id='column.notifications'
|
||||
data-preview-icon='bell'
|
||||
><NotificationsCounterIcon
|
||||
className='column-link__icon'
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.notifications'
|
||||
defaultMessage='Notifications'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/direct'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='envelope'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.direct'
|
||||
defaultMessage='Direct messages'
|
||||
/></NavLink >
|
||||
<FollowRequestsNavLink />
|
||||
<NavLink className='column-link column-link--transparent' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>
|
||||
<NavLink className='column-link column-link--transparent' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon className='column-link__icon' id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>
|
||||
<NavLink className='column-link column-link--transparent' to='/timelines/direct'><Icon className='column-link__icon' id='envelope' fixedWidth /><FormattedMessage id='navigation_bar.direct' defaultMessage='Direct messages' /></NavLink>
|
||||
<NavLink className='column-link column-link--transparent' to='/favourites'><Icon className='column-link__icon' id='star' fixedWidth /><FormattedMessage id='navigation_bar.favourites' defaultMessage='Favourites' /></NavLink>
|
||||
<NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
|
||||
<NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
|
||||
{profile_directory && <NavLink className='column-link column-link--transparent' to='/directory'><Icon className='column-link__icon' id='address-book-o' fixedWidth /><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></NavLink>}
|
||||
|
||||
<div className='spacer'></div >
|
||||
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/favourites'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='star'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.favourites'
|
||||
defaultMessage='Favourites'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/bookmarks'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='bookmark'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.bookmarks'
|
||||
defaultMessage='Bookmarks'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/lists'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='list-ul'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.lists'
|
||||
defaultMessage='Lists'
|
||||
/></NavLink >
|
||||
{profile_directory &&
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/directory'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='address-book-o'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='getting_started.directory'
|
||||
defaultMessage='Profile directory'
|
||||
/></NavLink >}
|
||||
|
||||
<ListPanel />
|
||||
|
||||
<hr />
|
||||
|
||||
<a className='column-link column-link--transparent' href='/settings/preferences'><Icon className='column-link__icon' id='cog' fixedWidth /><FormattedMessage id='navigation_bar.preferences' defaultMessage='Preferences' /></a>
|
||||
<a className='column-link column-link--transparent' href='/relationships'><Icon className='column-link__icon' id='users' fixedWidth /><FormattedMessage id='navigation_bar.follows_and_followers' defaultMessage='Follows and followers' /></a>
|
||||
<a
|
||||
className='column-link column-link--transparent'
|
||||
href='/settings/preferences'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='cog'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.preferences'
|
||||
defaultMessage='Preferences'
|
||||
/></a >
|
||||
<a
|
||||
className='column-link column-link--transparent'
|
||||
href='/relationships'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='navigation_bar.follows_and_followers'
|
||||
defaultMessage='Follows and followers'
|
||||
/></a >
|
||||
|
||||
{showTrends && <div className='flex-spacer' />}
|
||||
{showTrends && <TrendsContainer />}
|
||||
</div>
|
||||
|
||||
</div >
|
||||
);
|
||||
|
||||
export default withRouter(NavigationPanel);
|
||||
|
|
|
@ -8,19 +8,81 @@ import Icon from 'mastodon/components/icon';
|
|||
import NotificationsCounterIcon from './notifications_counter_icon';
|
||||
|
||||
export const links = [
|
||||
<NavLink className='tabs-bar__link' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' data-preview-icon='bell' ><Icon id='search' fixedWidth /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><Icon id='bars' fixedWidth /></NavLink>,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
to='/timelines/home'
|
||||
data-preview-title-id='column.home'
|
||||
data-preview-icon='home'
|
||||
><Icon
|
||||
id='home'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.home'
|
||||
defaultMessage='Home'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
to='/notifications'
|
||||
data-preview-title-id='column.notifications'
|
||||
data-preview-icon='bell'
|
||||
><NotificationsCounterIcon /><FormattedMessage
|
||||
id='tabs_bar.notifications'
|
||||
defaultMessage='Notifications'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
to='/timelines/public/local'
|
||||
data-preview-title-id='column.community'
|
||||
data-preview-icon='users'
|
||||
><Icon
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.local_timeline'
|
||||
defaultMessage='Local'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
exact
|
||||
to='/timelines/public'
|
||||
data-preview-title-id='column.public'
|
||||
data-preview-icon='globe'
|
||||
><Icon
|
||||
id='globe'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.federated_timeline'
|
||||
defaultMessage='Federated'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link optional'
|
||||
to='/search'
|
||||
data-preview-title-id='tabs_bar.search'
|
||||
data-preview-icon='bell'
|
||||
><Icon
|
||||
id='search'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.search'
|
||||
defaultMessage='Search'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
style={{ flexGrow: '0', flexBasis: '30px' }}
|
||||
to='/getting-started'
|
||||
data-preview-title-id='getting_started.heading'
|
||||
data-preview-icon='bars'
|
||||
><Icon
|
||||
id='bars'
|
||||
fixedWidth
|
||||
/></NavLink >,
|
||||
];
|
||||
|
||||
export function getIndex (path) {
|
||||
export function getIndex(path) {
|
||||
return links.findIndex(link => link.props.to === path);
|
||||
}
|
||||
|
||||
export function getLink (index) {
|
||||
export function getLink(index) {
|
||||
return links[index].props.to;
|
||||
}
|
||||
|
||||
|
@ -29,13 +91,13 @@ export default @injectIntl
|
|||
class TabsBar extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
intl : PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
}
|
||||
};
|
||||
|
||||
setRef = ref => {
|
||||
this.node = ref;
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = (e) => {
|
||||
// Only apply optimization for touch devices, which we assume are slower
|
||||
|
@ -50,7 +112,6 @@ class TabsBar extends React.PureComponent {
|
|||
const nextTab = tabs.find(tab => tab.contains(e.target));
|
||||
const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
|
||||
|
||||
|
||||
if (currentTab !== nextTab) {
|
||||
if (currentTab) {
|
||||
currentTab.classList.remove('active');
|
||||
|
@ -67,19 +128,28 @@ class TabsBar extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { intl: { formatMessage } } = this.props;
|
||||
|
||||
return (
|
||||
<div className='tabs-bar__wrapper'>
|
||||
<nav className='tabs-bar' ref={this.setRef}>
|
||||
{links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
|
||||
</nav>
|
||||
|
||||
<nav
|
||||
className='tabs-bar'
|
||||
ref={this.setRef}
|
||||
>
|
||||
{links.map(link => React.cloneElement(link, {
|
||||
key : link.props.to,
|
||||
onClick : this.handleClick,
|
||||
'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }),
|
||||
}))}
|
||||
</nav >
|
||||
|
||||
<div id='tabs-bar__portal' />
|
||||
</div>
|
||||
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ class ReducedMotion extends React.Component {
|
|||
|
||||
static propTypes = {
|
||||
defaultStyle: PropTypes.object,
|
||||
style: PropTypes.object,
|
||||
children: PropTypes.func,
|
||||
}
|
||||
style : PropTypes.object,
|
||||
children : PropTypes.func,
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
|
@ -33,9 +33,12 @@ class ReducedMotion extends React.Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<Motion style={style} defaultStyle={defaultStyle}>
|
||||
<Motion
|
||||
style={style}
|
||||
defaultStyle={defaultStyle}
|
||||
>
|
||||
{children}
|
||||
</Motion>
|
||||
</Motion >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"account.block_domain": "Tout masquer venant de {domain}",
|
||||
"account.blocked": "Bloqué·e",
|
||||
"account.cancel_follow_request": "Annuler la demande de suivi",
|
||||
"account.direct": "Envoyer un message direct à @{name}",
|
||||
"account.direct": "Envoyer un message privé à @{name}",
|
||||
"account.domain_blocked": "Domaine caché",
|
||||
"account.edit_profile": "Modifier le profil",
|
||||
"account.endorse": "Recommander sur le profil",
|
||||
|
@ -50,7 +50,9 @@
|
|||
"bundle_modal_error.close": "Fermer",
|
||||
"bundle_modal_error.message": "Une erreur s’est produite lors du chargement de ce composant.",
|
||||
"bundle_modal_error.retry": "Réessayer",
|
||||
"column.bookmarks": "Marque pages",
|
||||
"column.blocks": "Comptes bloqués",
|
||||
"column.bookmarks": "Bookmarks",
|
||||
"column.community": "Fil public local",
|
||||
"column.direct": "Messages privés",
|
||||
"column.directory": "Parcourir les profils",
|
||||
|
@ -138,8 +140,9 @@
|
|||
"empty_column.account_timeline": "Aucun pouet ici !",
|
||||
"empty_column.account_unavailable": "Profil non disponible",
|
||||
"empty_column.blocks": "Vous n’avez bloqué aucun·e utilisateur·rice pour le moment.",
|
||||
"empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
|
||||
"empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !",
|
||||
"empty_column.direct": "Vous n’avez pas encore de messages directs. Lorsque vous en enverrez ou recevrez un, il s’affichera ici.",
|
||||
"empty_column.direct": "Vous n’avez pas encore de messages privés. Lorsque vous en enverrez ou recevrez un, il s’affichera ici.",
|
||||
"empty_column.domain_blocks": "Il n’y a aucun domaine caché pour le moment.",
|
||||
"empty_column.favourited_statuses": "Vous n’avez aucun pouet favoris pour le moment. Lorsque vous en mettrez un en favori, il apparaîtra ici.",
|
||||
"empty_column.favourites": "Personne n’a encore mis ce pouet en favori. Lorsque quelqu’un le fera, il apparaîtra ici.",
|
||||
|
@ -163,7 +166,7 @@
|
|||
"getting_started.documentation": "Documentation",
|
||||
"getting_started.heading": "Pour commencer",
|
||||
"getting_started.invite": "Inviter des gens",
|
||||
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer ou faire des rapports de bogues via {github} sur GitHub.",
|
||||
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer ou faire des rapports de bogues via {forge} sur GitHub.",
|
||||
"getting_started.security": "Sécurité",
|
||||
"getting_started.terms": "Conditions d’utilisation",
|
||||
"hashtag.column_header.tag_mode.all": "et {additional}",
|
||||
|
@ -204,7 +207,7 @@
|
|||
"keyboard_shortcuts.column": "pour focaliser un statut dans l’une des colonnes",
|
||||
"keyboard_shortcuts.compose": "pour focaliser la zone de rédaction",
|
||||
"keyboard_shortcuts.description": "Description",
|
||||
"keyboard_shortcuts.direct": "pour ouvrir la colonne des messages directs",
|
||||
"keyboard_shortcuts.direct": "pour ouvrir la colonne des messages privés",
|
||||
"keyboard_shortcuts.down": "pour descendre dans la liste",
|
||||
"keyboard_shortcuts.enter": "pour ouvrir le statut",
|
||||
"keyboard_shortcuts.favourite": "pour ajouter aux favoris",
|
||||
|
@ -219,6 +222,7 @@
|
|||
"keyboard_shortcuts.muted": "pour ouvrir la liste des utilisateur·rice·s muté·e·s",
|
||||
"keyboard_shortcuts.my_profile": "pour ouvrir votre profil",
|
||||
"keyboard_shortcuts.notifications": "pour ouvrir votre colonne de notifications",
|
||||
"keyboard_shortcuts.open_media": "pour ouvrir les médias",
|
||||
"keyboard_shortcuts.pinned": "pour ouvrir une liste des pouets épinglés",
|
||||
"keyboard_shortcuts.profile": "pour ouvrir le profil de l’auteur·rice",
|
||||
"keyboard_shortcuts.reply": "pour répondre",
|
||||
|
@ -251,9 +255,10 @@
|
|||
"mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
|
||||
"navigation_bar.apps": "Applications mobiles",
|
||||
"navigation_bar.blocks": "Comptes bloqués",
|
||||
"navigation_bar.bookmarks": "Bookmarks",
|
||||
"navigation_bar.community_timeline": "Fil public local",
|
||||
"navigation_bar.compose": "Rédiger un nouveau pouet",
|
||||
"navigation_bar.direct": "Messages directs",
|
||||
"navigation_bar.direct": "Messages privés",
|
||||
"navigation_bar.discover": "Découvrir",
|
||||
"navigation_bar.domain_blocks": "Domaines cachés",
|
||||
"navigation_bar.edit_profile": "Modifier le profil",
|
||||
|
@ -344,12 +349,13 @@
|
|||
"status.admin_account": "Ouvrir l’interface de modération pour @{name}",
|
||||
"status.admin_status": "Ouvrir ce statut dans l’interface de modération",
|
||||
"status.block": "Bloquer @{name}",
|
||||
"status.bookmark": "Marquer",
|
||||
"status.cancel_reblog_private": "Dé-booster",
|
||||
"status.cannot_reblog": "Ce pouet ne peut pas être partagé",
|
||||
"status.copy": "Copier le lien vers le pouet",
|
||||
"status.delete": "Effacer",
|
||||
"status.detailed_status": "Vue détaillée de la conversation",
|
||||
"status.direct": "Envoyer un message direct à @{name}",
|
||||
"status.direct": "Envoyer un message privé à @{name}",
|
||||
"status.embed": "Intégrer",
|
||||
"status.favourite": "Ajouter aux favoris",
|
||||
"status.filtered": "Filtré",
|
||||
|
@ -368,6 +374,7 @@
|
|||
"status.reblogged_by": "{name} a partagé :",
|
||||
"status.reblogs.empty": "Personne n’a encore partagé ce pouet. Lorsque quelqu’un le fera, il apparaîtra ici.",
|
||||
"status.redraft": "Effacer et ré-écrire",
|
||||
"status.remove_bookmark": "Enlever le marque-page",
|
||||
"status.reply": "Répondre",
|
||||
"status.replyAll": "Répondre au fil",
|
||||
"status.report": "Signaler @{name}",
|
||||
|
@ -383,9 +390,9 @@
|
|||
"status.unpin": "Retirer du profil",
|
||||
"suggestions.dismiss": "Rejeter la suggestion",
|
||||
"suggestions.header": "Vous pourriez être intéressé·e par…",
|
||||
"tabs_bar.federated_timeline": "Fil public global",
|
||||
"tabs_bar.federated_timeline": "Global",
|
||||
"tabs_bar.home": "Accueil",
|
||||
"tabs_bar.local_timeline": "Fil public local",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"tabs_bar.search": "Chercher",
|
||||
"time_remaining.days": "{number, plural, one {# day} other {# days}} restants",
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
import {
|
||||
REBLOG_REQUEST,
|
||||
REBLOG_FAIL,
|
||||
FAVOURITE_REQUEST,
|
||||
FAVOURITE_FAIL,
|
||||
UNFAVOURITE_SUCCESS,
|
||||
BOOKMARK_REQUEST,
|
||||
BOOKMARK_FAIL,
|
||||
BOOKMARK_REQUEST,
|
||||
FAVOURITE_FAIL,
|
||||
FAVOURITE_REQUEST,
|
||||
REBLOG_FAIL,
|
||||
REBLOG_REQUEST,
|
||||
UNFAVOURITE_SUCCESS,
|
||||
} from '../actions/interactions';
|
||||
import {
|
||||
STATUS_MUTE_SUCCESS,
|
||||
STATUS_UNMUTE_SUCCESS,
|
||||
STATUS_REVEAL,
|
||||
STATUS_HIDE,
|
||||
} from '../actions/statuses';
|
||||
import { STATUS_HIDE, STATUS_MUTE_SUCCESS, STATUS_REVEAL, STATUS_UNMUTE_SUCCESS } from '../actions/statuses';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
import { fromJS, Map as ImmutableMap } from 'immutable';
|
||||
|
||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||
|
||||
|
@ -33,7 +28,7 @@ const deleteStatus = (state, id, references) => {
|
|||
const initialState = ImmutableMap();
|
||||
|
||||
export default function statuses(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
switch (action.type) {
|
||||
case STATUS_IMPORT:
|
||||
return importStatus(state, action.status);
|
||||
case STATUSES_IMPORT:
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/** @license
|
||||
|
||||
DHTML Snowstorm! JavaScript-based snow for web pages
|
||||
Making it snow on the internets since 2003. You're welcome.
|
||||
-----------------------------------------------------------
|
||||
Version 1.44.20131208 (Previous rev: 1.44.20131125)
|
||||
Copyright (c) 2007, Scott Schiller. All rights reserved.
|
||||
Code provided under the BSD License
|
||||
http://schillmania.com/projects/snowstorm/license.txt
|
||||
*/
|
||||
var snowStorm=function(g,f){function k(a,d){isNaN(d)&&(d=0);return Math.random()*a+d}function x(){g.setTimeout(function(){a.start(!0)},20);a.events.remove(m?f:g,"mousemove",x)}function y(){(!a.excludeMobile||!D)&&x();a.events.remove(g,"load",y)}this.excludeMobile=this.autoStart=!0;this.flakesMax=128;this.flakesMaxActive=64;this.animationInterval=33;this.useGPU=!0;this.className=null;this.excludeMobile=!0;this.flakeBottom=null;this.followMouse=!0;this.snowColor="#fff";this.snowCharacter="•";this.snowStick=
|
||||
!0;this.targetElement=null;this.useMeltEffect=!0;this.usePixelPosition=this.usePositionFixed=this.useTwinkleEffect=!1;this.freezeOnBlur=!0;this.flakeRightOffset=this.flakeLeftOffset=0;this.flakeHeight=this.flakeWidth=8;this.vMaxX=5;this.vMaxY=4;this.zIndex=0;var a=this,q,m=navigator.userAgent.match(/msie/i),E=navigator.userAgent.match(/msie 6/i),D=navigator.userAgent.match(/mobile|opera m(ob|in)/i),r=m&&"BackCompat"===f.compatMode||E,h=null,n=null,l=null,p=null,s=null,z=null,A=null,v=1,t=!1,w=!1,
|
||||
u;a:{try{f.createElement("div").style.opacity="0.5"}catch(F){u=!1;break a}u=!0}var B=!1,C=f.createDocumentFragment();q=function(){function c(b){g.setTimeout(b,1E3/(a.animationInterval||20))}function d(a){return void 0!==h.style[a]?a:null}var e,b=g.requestAnimationFrame||g.webkitRequestAnimationFrame||g.mozRequestAnimationFrame||g.oRequestAnimationFrame||g.msRequestAnimationFrame||c;e=b?function(){return b.apply(g,arguments)}:null;var h;h=f.createElement("div");e={transform:{ie:d("-ms-transform"),
|
||||
moz:d("MozTransform"),opera:d("OTransform"),webkit:d("webkitTransform"),w3:d("transform"),prop:null},getAnimationFrame:e};e.transform.prop=e.transform.w3||e.transform.moz||e.transform.webkit||e.transform.ie||e.transform.opera;h=null;return e}();this.timer=null;this.flakes=[];this.active=this.disabled=!1;this.meltFrameCount=20;this.meltFrames=[];this.setXY=function(c,d,e){if(!c)return!1;a.usePixelPosition||w?(c.style.left=d-a.flakeWidth+"px",c.style.top=e-a.flakeHeight+"px"):r?(c.style.right=100-100*
|
||||
(d/h)+"%",c.style.top=Math.min(e,s-a.flakeHeight)+"px"):a.flakeBottom?(c.style.right=100-100*(d/h)+"%",c.style.top=Math.min(e,s-a.flakeHeight)+"px"):(c.style.right=100-100*(d/h)+"%",c.style.bottom=100-100*(e/l)+"%")};this.events=function(){function a(c){c=b.call(c);var d=c.length;e?(c[1]="on"+c[1],3<d&&c.pop()):3===d&&c.push(!1);return c}function d(a,b){var c=a.shift(),d=[f[b]];if(e)c[d](a[0],a[1]);else c[d].apply(c,a)}var e=!g.addEventListener&&g.attachEvent,b=Array.prototype.slice,f={add:e?"attachEvent":
|
||||
"addEventListener",remove:e?"detachEvent":"removeEventListener"};return{add:function(){d(a(arguments),"add")},remove:function(){d(a(arguments),"remove")}}}();this.randomizeWind=function(){var c;c=k(a.vMaxX,0.2);z=1===parseInt(k(2),10)?-1*c:c;A=k(a.vMaxY,0.2);if(this.flakes)for(c=0;c<this.flakes.length;c++)this.flakes[c].active&&this.flakes[c].setVelocities()};this.scrollHandler=function(){var c;p=a.flakeBottom?0:parseInt(g.scrollY||f.documentElement.scrollTop||(r?f.body.scrollTop:0),10);isNaN(p)&&
|
||||
(p=0);if(!t&&!a.flakeBottom&&a.flakes)for(c=0;c<a.flakes.length;c++)0===a.flakes[c].active&&a.flakes[c].stick()};this.resizeHandler=function(){g.innerWidth||g.innerHeight?(h=g.innerWidth-16-a.flakeRightOffset,l=a.flakeBottom||g.innerHeight):(h=(f.documentElement.clientWidth||f.body.clientWidth||f.body.scrollWidth)-(!m?8:0)-a.flakeRightOffset,l=a.flakeBottom||f.documentElement.clientHeight||f.body.clientHeight||f.body.scrollHeight);s=f.body.offsetHeight;n=parseInt(h/2,10)};this.resizeHandlerAlt=function(){h=
|
||||
a.targetElement.offsetWidth-a.flakeRightOffset;l=a.flakeBottom||a.targetElement.offsetHeight;n=parseInt(h/2,10);s=f.body.offsetHeight};this.freeze=function(){if(a.disabled)return!1;a.disabled=1;a.timer=null};this.resume=function(){if(a.disabled)a.disabled=0;else return!1;a.timerInit()};this.toggleSnow=function(){a.flakes.length?(a.active=!a.active,a.active?(a.show(),a.resume()):(a.stop(),a.freeze())):a.start()};this.stop=function(){var c;this.freeze();for(c=0;c<this.flakes.length;c++)this.flakes[c].o.style.display=
|
||||
"none";a.events.remove(g,"scroll",a.scrollHandler);a.events.remove(g,"resize",a.resizeHandler);a.freezeOnBlur&&(m?(a.events.remove(f,"focusout",a.freeze),a.events.remove(f,"focusin",a.resume)):(a.events.remove(g,"blur",a.freeze),a.events.remove(g,"focus",a.resume)))};this.show=function(){var a;for(a=0;a<this.flakes.length;a++)this.flakes[a].o.style.display="block"};this.SnowFlake=function(c,d,e){var b=this;this.type=c;this.x=d||parseInt(k(h-20),10);this.y=!isNaN(e)?e:-k(l)-12;this.vY=this.vX=null;
|
||||
this.vAmpTypes=[1,1.2,1.4,1.6,1.8];this.vAmp=this.vAmpTypes[this.type]||1;this.melting=!1;this.meltFrameCount=a.meltFrameCount;this.meltFrames=a.meltFrames;this.twinkleFrame=this.meltFrame=0;this.active=1;this.fontSize=10+10*(this.type/5);this.o=f.createElement("div");this.o.innerHTML=a.snowCharacter;a.className&&this.o.setAttribute("class",a.className);this.o.style.color=a.snowColor;this.o.style.position=t?"fixed":"absolute";a.useGPU&&q.transform.prop&&(this.o.style[q.transform.prop]="translate3d(0px, 0px, 0px)");
|
||||
this.o.style.width=a.flakeWidth+"px";this.o.style.height=a.flakeHeight+"px";this.o.style.fontFamily="arial,verdana";this.o.style.cursor="default";this.o.style.overflow="hidden";this.o.style.fontWeight="normal";this.o.style.zIndex=a.zIndex;C.appendChild(this.o);this.refresh=function(){if(isNaN(b.x)||isNaN(b.y))return!1;a.setXY(b.o,b.x,b.y)};this.stick=function(){r||a.targetElement!==f.documentElement&&a.targetElement!==f.body?b.o.style.top=l+p-a.flakeHeight+"px":a.flakeBottom?b.o.style.top=a.flakeBottom+
|
||||
"px":(b.o.style.display="none",b.o.style.bottom="0%",b.o.style.position="fixed",b.o.style.display="block")};this.vCheck=function(){0<=b.vX&&0.2>b.vX?b.vX=0.2:0>b.vX&&-0.2<b.vX&&(b.vX=-0.2);0<=b.vY&&0.2>b.vY&&(b.vY=0.2)};this.move=function(){var c=b.vX*v;b.x+=c;b.y+=b.vY*b.vAmp;b.x>=h||h-b.x<a.flakeWidth?b.x=0:0>c&&b.x-a.flakeLeftOffset<-a.flakeWidth&&(b.x=h-a.flakeWidth-1);b.refresh();l+p-b.y+a.flakeHeight<a.flakeHeight?(b.active=0,a.snowStick?b.stick():b.recycle()):(a.useMeltEffect&&(b.active&&3>
|
||||
b.type&&!b.melting&&0.998<Math.random())&&(b.melting=!0,b.melt()),a.useTwinkleEffect&&(0>b.twinkleFrame?0.97<Math.random()&&(b.twinkleFrame=parseInt(8*Math.random(),10)):(b.twinkleFrame--,u?b.o.style.opacity=b.twinkleFrame&&0===b.twinkleFrame%2?0:1:b.o.style.visibility=b.twinkleFrame&&0===b.twinkleFrame%2?"hidden":"visible")))};this.animate=function(){b.move()};this.setVelocities=function(){b.vX=z+k(0.12*a.vMaxX,0.1);b.vY=A+k(0.12*a.vMaxY,0.1)};this.setOpacity=function(a,b){if(!u)return!1;a.style.opacity=
|
||||
b};this.melt=function(){!a.useMeltEffect||!b.melting?b.recycle():b.meltFrame<b.meltFrameCount?(b.setOpacity(b.o,b.meltFrames[b.meltFrame]),b.o.style.fontSize=b.fontSize-b.fontSize*(b.meltFrame/b.meltFrameCount)+"px",b.o.style.lineHeight=a.flakeHeight+2+0.75*a.flakeHeight*(b.meltFrame/b.meltFrameCount)+"px",b.meltFrame++):b.recycle()};this.recycle=function(){b.o.style.display="none";b.o.style.position=t?"fixed":"absolute";b.o.style.bottom="auto";b.setVelocities();b.vCheck();b.meltFrame=0;b.melting=
|
||||
!1;b.setOpacity(b.o,1);b.o.style.padding="0px";b.o.style.margin="0px";b.o.style.fontSize=b.fontSize+"px";b.o.style.lineHeight=a.flakeHeight+2+"px";b.o.style.textAlign="center";b.o.style.verticalAlign="baseline";b.x=parseInt(k(h-a.flakeWidth-20),10);b.y=parseInt(-1*k(l),10)-a.flakeHeight;b.refresh();b.o.style.display="block";b.active=1};this.recycle();this.refresh()};this.snow=function(){var c=0,d=null,e,d=0;for(e=a.flakes.length;d<e;d++)1===a.flakes[d].active&&(a.flakes[d].move(),c++),a.flakes[d].melting&&
|
||||
a.flakes[d].melt();c<a.flakesMaxActive&&(d=a.flakes[parseInt(k(a.flakes.length),10)],0===d.active&&(d.melting=!0));a.timer&&q.getAnimationFrame(a.snow)};this.mouseMove=function(c){if(!a.followMouse)return!0;c=parseInt(c.clientX,10);c<n?v=-2+2*(c/n):(c-=n,v=2*(c/n))};this.createSnow=function(c,d){var e;for(e=0;e<c;e++)if(a.flakes[a.flakes.length]=new a.SnowFlake(parseInt(k(6),10)),d||e>a.flakesMaxActive)a.flakes[a.flakes.length-1].active=-1;a.targetElement.appendChild(C)};this.timerInit=function(){a.timer=
|
||||
!0;a.snow()};this.init=function(){var c;for(c=0;c<a.meltFrameCount;c++)a.meltFrames.push(1-c/a.meltFrameCount);a.randomizeWind();a.createSnow(a.flakesMax);a.events.add(g,"resize",a.resizeHandler);a.events.add(g,"scroll",a.scrollHandler);a.freezeOnBlur&&(m?(a.events.add(f,"focusout",a.freeze),a.events.add(f,"focusin",a.resume)):(a.events.add(g,"blur",a.freeze),a.events.add(g,"focus",a.resume)));a.resizeHandler();a.scrollHandler();a.followMouse&&a.events.add(m?f:g,"mousemove",a.mouseMove);a.animationInterval=
|
||||
Math.max(20,a.animationInterval);a.timerInit()};this.start=function(c){if(B){if(c)return!0}else B=!0;if("string"===typeof a.targetElement&&(c=a.targetElement,a.targetElement=f.getElementById(c),!a.targetElement))throw Error('Snowstorm: Unable to get targetElement "'+c+'"');a.targetElement||(a.targetElement=f.body||f.documentElement);a.targetElement!==f.documentElement&&a.targetElement!==f.body&&(a.resizeHandler=a.resizeHandlerAlt,a.usePixelPosition=!0);a.resizeHandler();a.usePositionFixed=a.usePositionFixed&&
|
||||
!r&&!a.flakeBottom;if(g.getComputedStyle)try{w="relative"===g.getComputedStyle(a.targetElement,null).getPropertyValue("position")}catch(d){w=!1}t=a.usePositionFixed;h&&(l&&!a.disabled)&&(a.init(),a.active=!0)};a.autoStart&&a.events.add(g,"load",y,!1);return this}(window, document);
|
|
@ -0,0 +1,30 @@
|
|||
@import 'bliss/variables';
|
||||
@import 'bliss/mixins';
|
||||
@import 'bliss/variables';
|
||||
@import 'fonts/roboto';
|
||||
@import 'fonts/roboto-mono';
|
||||
@import 'fonts/montserrat';
|
||||
|
||||
@import 'bliss/reset';
|
||||
@import 'bliss/messaging';
|
||||
@import 'bliss/basics';
|
||||
@import 'bliss/containers';
|
||||
@import 'bliss/lists';
|
||||
@import 'bliss/footer';
|
||||
@import 'bliss/compact_header';
|
||||
@import 'bliss/widgets';
|
||||
@import 'bliss/forms';
|
||||
@import 'bliss/accounts';
|
||||
@import 'bliss/statuses';
|
||||
@import 'bliss/boost';
|
||||
@import 'bliss/components';
|
||||
@import 'bliss/polls';
|
||||
@import 'bliss/introduction';
|
||||
@import 'bliss/modal';
|
||||
@import 'bliss/emoji_picker';
|
||||
@import 'bliss/about';
|
||||
@import 'bliss/tables';
|
||||
@import 'bliss/admin';
|
||||
@import 'bliss/dashboard';
|
||||
@import 'bliss/rtl';
|
||||
@import 'bliss/accessibility';
|
|
@ -0,0 +1,74 @@
|
|||
@mixin avatar-radius {
|
||||
border-radius: 4px;
|
||||
background: transparent no-repeat;
|
||||
background-position: 50%;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
@mixin avatar-size($size: 48px) {
|
||||
width: $size;
|
||||
height: $size;
|
||||
background-size: $size $size;
|
||||
}
|
||||
|
||||
@mixin search-input {
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
font-family: inherit;
|
||||
background: $ui-base-color;
|
||||
color: $darker-text-color;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin search-popout {
|
||||
background: $simple-background-color;
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
padding-bottom: 14px;
|
||||
margin-top: 10px;
|
||||
color: $light-text-color;
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: $light-text-color;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
em {
|
||||
font-weight: 500;
|
||||
color: $inverted-text-color;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,856 @@
|
|||
$maximum-width: 1235px;
|
||||
$fluid-breakpoint: $maximum-width + 20px;
|
||||
$column-breakpoint: 700px;
|
||||
$small-breakpoint: 960px;
|
||||
|
||||
.container {
|
||||
box-sizing: border-box;
|
||||
max-width: $maximum-width;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
|
||||
@media screen and (max-width: $fluid-breakpoint) {
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.rich-formatting {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
color: $darker-text-color;
|
||||
padding-right: 10px;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
p,
|
||||
li {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
margin-bottom: 12px;
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
strong,
|
||||
em {
|
||||
font-weight: 700;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 26px;
|
||||
line-height: 30px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
|
||||
small {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 22px;
|
||||
line-height: 26px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin-left: 20px;
|
||||
|
||||
&[type='a'] {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
&[type='i'] {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
li > ol,
|
||||
li > ul {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
|
||||
margin: 20px 0;
|
||||
|
||||
&.spacer {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.information-board {
|
||||
background: darken($ui-base-color, 4%);
|
||||
padding: 20px 0;
|
||||
|
||||
.container-alt {
|
||||
position: relative;
|
||||
padding-right: 280px + 15px;
|
||||
}
|
||||
|
||||
&__sections {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&__section {
|
||||
flex: 1 0 0;
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
color: $primary-text-color;
|
||||
text-align: right;
|
||||
padding: 10px 15px;
|
||||
|
||||
span,
|
||||
strong {
|
||||
display: block;
|
||||
}
|
||||
|
||||
span {
|
||||
&:last-child {
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 32px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: absolute;
|
||||
width: 280px;
|
||||
box-sizing: border-box;
|
||||
background: darken($ui-base-color, 8%);
|
||||
padding: 20px;
|
||||
padding-top: 10px;
|
||||
border-radius: 4px 4px 0 0;
|
||||
right: 0;
|
||||
bottom: -40px;
|
||||
|
||||
.panel-header {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
color: $darker-text-color;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 4%);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
a,
|
||||
span {
|
||||
font-weight: 400;
|
||||
color: darken($darker-text-color, 10%);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.owner {
|
||||
text-align: center;
|
||||
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 15px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
color: $primary-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
.display_name {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.username {
|
||||
display: block;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.landing-page {
|
||||
p,
|
||||
li {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
margin-bottom: 12px;
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
em {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 700;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 26px;
|
||||
line-height: 30px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
|
||||
small {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 22px;
|
||||
line-height: 26px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin-left: 20px;
|
||||
|
||||
&[type='a'] {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
&[type='i'] {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
li > ol,
|
||||
li > ul {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
|
||||
margin: 20px 0;
|
||||
|
||||
&.spacer {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__information,
|
||||
&__forms {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
&__call-to-action {
|
||||
background: darken($ui-base-color, 4%);
|
||||
border-radius: 4px;
|
||||
padding: 25px 40px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.row__information-board {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
|
||||
.information-board__section {
|
||||
flex: 1 0 auto;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.row__mascot {
|
||||
flex: 1;
|
||||
margin: 10px -50px 0 0;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__logo {
|
||||
margin-right: 20px;
|
||||
|
||||
img {
|
||||
height: 50px;
|
||||
width: auto;
|
||||
mix-blend-mode: lighten;
|
||||
}
|
||||
}
|
||||
|
||||
&__information {
|
||||
padding: 45px 40px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
|
||||
.account {
|
||||
border-bottom: 0;
|
||||
padding: 0;
|
||||
|
||||
&__display-name {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
div.account__display-name {
|
||||
&:hover {
|
||||
.display-name strong {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
&__avatar-wrapper {
|
||||
margin-left: 0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 44px 44px;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 15px;
|
||||
|
||||
&__account {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-breakpoint) {
|
||||
.contact {
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
padding: 25px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&__information,
|
||||
&__forms,
|
||||
#mastodon-timeline {
|
||||
box-sizing: border-box;
|
||||
background: $ui-base-color;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 6px rgba($black, 0.1);
|
||||
}
|
||||
|
||||
&__mascot {
|
||||
height: 104px;
|
||||
position: relative;
|
||||
left: -40px;
|
||||
bottom: 25px;
|
||||
|
||||
img {
|
||||
height: 190px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__short-description {
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $column-breakpoint) {
|
||||
.row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
p a {
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
color: $primary-text-color;
|
||||
margin-bottom: 0;
|
||||
|
||||
small {
|
||||
color: $darker-text-color;
|
||||
|
||||
span {
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__hero {
|
||||
margin-bottom: 10px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 840px) {
|
||||
.information-board {
|
||||
.container-alt {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: static;
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
|
||||
.panel-header {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 675px) {
|
||||
.header-wrapper {
|
||||
padding-top: 0;
|
||||
|
||||
&.compact {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.compact .hero .heading {
|
||||
text-align: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.header .container-alt,
|
||||
.features .container-alt {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.cta {
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.landing {
|
||||
margin-bottom: 100px;
|
||||
|
||||
@media screen and (max-width: 738px) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&__brand {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 50px;
|
||||
|
||||
svg {
|
||||
fill: $primary-text-color;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
padding: 0;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.directory {
|
||||
margin-top: 30px;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.hero-widget {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0;
|
||||
|
||||
h4 {
|
||||
padding: 10px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
&__text {
|
||||
border-radius: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
background: $ui-base-color;
|
||||
padding: 10px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
display: flex;
|
||||
|
||||
&__column {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.account {
|
||||
padding: 10px 0;
|
||||
border-bottom: 0;
|
||||
|
||||
.account__display-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 44px 44px;
|
||||
}
|
||||
}
|
||||
|
||||
&__counter {
|
||||
padding: 10px;
|
||||
|
||||
strong {
|
||||
font-family: $font-display, sans-serif;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.simple_form .user_agreement .label_input > label {
|
||||
font-weight: 400;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
.simple_form p.lead {
|
||||
color: $darker-text-color;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
&__grid {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
|
||||
grid-gap: 30px;
|
||||
|
||||
@media screen and (max-width: 738px) {
|
||||
grid-template-columns: minmax(0, 100%);
|
||||
grid-gap: 10px;
|
||||
|
||||
&__column-login {
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.box-widget {
|
||||
order: 2;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.hero-widget {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__column-registration {
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.directory {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
grid-gap: 0;
|
||||
|
||||
.hero-widget {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
|
||||
&__img,
|
||||
&__img img,
|
||||
&__footer {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-widget,
|
||||
.box-widget,
|
||||
.directory__tag {
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
.directory {
|
||||
margin-top: 0;
|
||||
|
||||
&__tag {
|
||||
margin-bottom: 0;
|
||||
|
||||
& > a,
|
||||
& > div {
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.brand {
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.brand__tagline {
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 50px;
|
||||
width: 300px;
|
||||
color: $ui-primary-color;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
position: static;
|
||||
width: auto;
|
||||
margin-top: 20px;
|
||||
color: $dark-text-color;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
$black-emojis: '8ball' 'ant' 'back' 'black_circle' 'black_heart' 'black_large_square' 'black_medium_small_square' 'black_medium_square' 'black_nib' 'black_small_square' 'bomb' 'bowling' 'bust_in_silhouette' 'busts_in_silhouette' 'camera' 'camera_with_flash' 'clubs' 'copyright' 'curly_loop' 'currency_exchange' 'dark_sunglasses' 'eight_pointed_black_star' 'electric_plug' 'end' 'female-guard' 'film_projector' 'fried_egg' 'gorilla' 'guardsman' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'hocho' 'hole' 'joystick' 'kaaba' 'lower_left_ballpoint_pen' 'lower_left_fountain_pen' 'male-guard' 'microphone' 'mortar_board' 'movie_camera' 'musical_score' 'on' 'registered' 'soon' 'spades' 'speaking_head_in_silhouette' 'spider' 'telephone_receiver' 'tm' 'top' 'tophat' 'turkey' 'vhs' 'video_camera' 'video_game' 'water_buffalo' 'waving_black_flag' 'wavy_dash';
|
||||
|
||||
%white-emoji-outline {
|
||||
filter: drop-shadow(1px 1px 0 $white) drop-shadow(-1px 1px 0 $white) drop-shadow(1px -1px 0 $white) drop-shadow(-1px -1px 0 $white);
|
||||
transform: scale(.71);
|
||||
}
|
||||
|
||||
.emojione {
|
||||
@each $emoji in $black-emojis {
|
||||
&[title=':#{$emoji}:'] {
|
||||
@extend %white-emoji-outline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
margin-right: 1ch;
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
.card {
|
||||
& > a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
.card__bar {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__img {
|
||||
height: 130px;
|
||||
position: relative;
|
||||
background: darken($ui-base-color, 12%);
|
||||
border-radius: 4px 4px 0 0;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
object-fit: cover;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__bar {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-radius: 0 0 4px 4px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
flex: 0 0 auto;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding-top: 2px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
margin: 0;
|
||||
border-radius: 4px;
|
||||
background: darken($ui-base-color, 8%);
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.display-name {
|
||||
margin-left: 15px;
|
||||
text-align: left;
|
||||
|
||||
strong {
|
||||
font-size: 15px;
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
padding: 30px 0;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
|
||||
a,
|
||||
.current,
|
||||
.newer,
|
||||
.older,
|
||||
.page,
|
||||
.gap {
|
||||
font-size: 14px;
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
display: inline-block;
|
||||
padding: 6px 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.current {
|
||||
background: $simple-background-color;
|
||||
border-radius: 100px;
|
||||
color: $inverted-text-color;
|
||||
cursor: default;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.gap {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.older,
|
||||
.newer {
|
||||
text-transform: uppercase;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
.older {
|
||||
float: left;
|
||||
padding-left: 0;
|
||||
|
||||
.fa {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.newer {
|
||||
float: right;
|
||||
padding-right: 0;
|
||||
|
||||
.fa {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: default;
|
||||
color: lighten($inverted-text-color, 10%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
padding: 30px 20px;
|
||||
|
||||
.page {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.newer,
|
||||
.older {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nothing-here {
|
||||
background: $ui-base-color;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
color: $light-text-color;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
min-height: 30vh;
|
||||
|
||||
&--under-tabs {
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
&--flexible {
|
||||
box-sizing: border-box;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.account-role,
|
||||
.simple_form .recommended {
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
cursor: default;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
font-weight: 500;
|
||||
color: $ui-secondary-color;
|
||||
background-color: rgba($ui-secondary-color, 0.1);
|
||||
border: 1px solid rgba($ui-secondary-color, 0.5);
|
||||
|
||||
&.moderator {
|
||||
color: $success-green;
|
||||
background-color: rgba($success-green, 0.1);
|
||||
border-color: rgba($success-green, 0.5);
|
||||
}
|
||||
|
||||
&.admin {
|
||||
color: lighten($error-red, 12%);
|
||||
background-color: rgba(lighten($error-red, 12%), 0.1);
|
||||
border-color: rgba(lighten($error-red, 12%), 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.account__header__fields {
|
||||
padding: 0;
|
||||
margin: 15px -15px -15px;
|
||||
border: 0 none;
|
||||
border-top: 1px solid lighten($ui-base-color, 12%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
|
||||
dl {
|
||||
display: flex;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||
}
|
||||
|
||||
dt,
|
||||
dd {
|
||||
box-sizing: border-box;
|
||||
padding: 14px;
|
||||
text-align: center;
|
||||
max-height: 48px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 500;
|
||||
width: 120px;
|
||||
flex: 0 0 auto;
|
||||
color: $secondary-text-color;
|
||||
background: rgba(darken($ui-base-color, 8%), 0.5);
|
||||
}
|
||||
|
||||
dd {
|
||||
flex: 1 1 auto;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.verified {
|
||||
border: 1px solid rgba($valid-value-color, 0.5);
|
||||
background: rgba($valid-value-color, 0.25);
|
||||
|
||||
a {
|
||||
color: $valid-value-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__mark {
|
||||
color: $valid-value-color;
|
||||
}
|
||||
}
|
||||
|
||||
dl:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.directory__tag .trends__item__current {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.pending-account {
|
||||
&__header {
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: $ui-secondary-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
color: $primary-text-color;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,718 @@
|
|||
$no-columns-breakpoint: 600px;
|
||||
$sidebar-width: 240px;
|
||||
$content-width: 840px;
|
||||
|
||||
.admin-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
||||
.sidebar-wrapper {
|
||||
flex: 1 1 $sidebar-width;
|
||||
height: 100%;
|
||||
background: $ui-base-color;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: $sidebar-width;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 40px auto;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
& > a:first-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
border-radius: 4px 0 0 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
transition: all 200ms linear;
|
||||
transition-property: color, background-color;
|
||||
border-radius: 4px 0 0 4px;
|
||||
|
||||
i.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $primary-text-color;
|
||||
background-color: darken($ui-base-color, 5%);
|
||||
transition: all 100ms linear;
|
||||
transition-property: color, background-color;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: darken($ui-base-color, 2%);
|
||||
border-radius: 4px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
background: darken($ui-base-color, 4%);
|
||||
border-radius: 0 0 0 4px;
|
||||
margin: 0;
|
||||
|
||||
a {
|
||||
border: 0;
|
||||
padding: 15px 35px;
|
||||
}
|
||||
}
|
||||
|
||||
.simple-navigation-active-leaf a {
|
||||
color: $primary-text-color;
|
||||
background-color: $ui-highlight-color;
|
||||
border-bottom: 0;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($ui-highlight-color, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > ul > .simple-navigation-active-leaf a {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex: 2 1 $content-width;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: $content-width;
|
||||
padding: 20px 15px;
|
||||
padding-top: 60px;
|
||||
padding-left: 25px;
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
max-width: none;
|
||||
padding: 15px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: $secondary-text-color;
|
||||
font-size: 24px;
|
||||
line-height: 28px;
|
||||
font-weight: 400;
|
||||
padding-bottom: 40px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: $secondary-text-color;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $darker-text-color;
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 8px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 16px;
|
||||
color: $secondary-text-color;
|
||||
line-height: 28px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.fields-group h6 {
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.directory__tag > a,
|
||||
.directory__tag > div {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.directory__tag .table-action-link .fa {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.directory__tag h4 {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: $primary-text-color;
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
& > p {
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: $secondary-text-color;
|
||||
margin-bottom: 20px;
|
||||
|
||||
strong {
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
|
||||
margin: 20px 0;
|
||||
|
||||
&.spacer {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
display: block;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
.sidebar-wrapper,
|
||||
.content-wrapper {
|
||||
flex: 0 0 auto;
|
||||
height: auto;
|
||||
overflow: initial;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr.spacer {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 20px 0;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.muted-hint {
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.positive-hint {
|
||||
color: $valid-value-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.negative-hint {
|
||||
color: $error-value-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.neutral-hint {
|
||||
color: $dark-text-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.warning-hint {
|
||||
color: $gold-star;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.filter-subset {
|
||||
flex: 0 0 auto;
|
||||
margin: 0 40px 10px 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 5px;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
border-bottom: 2px solid $ui-base-color;
|
||||
|
||||
&:hover {
|
||||
color: $primary-text-color;
|
||||
border-bottom: 2px solid lighten($ui-base-color, 5%);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
color: $highlight-text-color;
|
||||
border-bottom: 2px solid $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-accounts {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.report-accounts__item {
|
||||
display: flex;
|
||||
flex: 250px;
|
||||
flex-direction: column;
|
||||
margin: 0 5px;
|
||||
|
||||
& > strong {
|
||||
display: block;
|
||||
margin: 0 0 10px -5px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: $secondary-text-color;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account-card {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.report-status,
|
||||
.account-status {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.activity-stream {
|
||||
flex: 2 0 0;
|
||||
margin-right: 20px;
|
||||
max-width: calc(100% - 60px);
|
||||
|
||||
.entry {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.report-status__actions,
|
||||
.account-status__actions {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.icon-button {
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.simple_form.new_report_note,
|
||||
.simple_form.new_account_moderation_note {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.batch-form-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 5px;
|
||||
|
||||
#form_status_batch_action {
|
||||
margin: 0 5px 5px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input.button {
|
||||
margin: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
.media-spoiler-toggle-buttons {
|
||||
margin-left: auto;
|
||||
|
||||
.button {
|
||||
overflow: visible;
|
||||
margin: 0 0 5px 5px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.back-link {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 20px;
|
||||
line-height: 20px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background: $ui-base-color;
|
||||
color: $darker-text-color;
|
||||
border-radius: 4px 4px 0 0;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
margin-right: 10px;
|
||||
|
||||
.avatar {
|
||||
display: block;
|
||||
margin: 0;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
max-width: calc(100% - 90px);
|
||||
}
|
||||
|
||||
&__title {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&__timestamp {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
&__extras {
|
||||
background: lighten($ui-base-color, 6%);
|
||||
border-radius: 0 0 4px 4px;
|
||||
padding: 10px;
|
||||
color: $darker-text-color;
|
||||
font-family: $font-monospace, monospace;
|
||||
font-size: 12px;
|
||||
word-wrap: break-word;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 28px;
|
||||
margin-right: 10px;
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
&__icon__overlay {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.positive {
|
||||
background: $success-green;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
background: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
&.neutral {
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
.username,
|
||||
.target {
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.diff-old {
|
||||
color: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
.diff-neutral {
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
.diff-new {
|
||||
color: $success-green;
|
||||
}
|
||||
}
|
||||
|
||||
a.name-tag,
|
||||
.name-tag,
|
||||
a.inline-name-tag,
|
||||
.inline-name-tag {
|
||||
text-decoration: none;
|
||||
color: $secondary-text-color;
|
||||
|
||||
.username {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.suspended {
|
||||
.username {
|
||||
text-decoration: line-through;
|
||||
color: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.name-tag,
|
||||
.name-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.avatar {
|
||||
display: block;
|
||||
margin: 0;
|
||||
margin-right: 5px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.suspended {
|
||||
.avatar {
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.speech-bubble {
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid $ui-highlight-color;
|
||||
|
||||
&.positive {
|
||||
border-left-color: $success-green;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
border-left-color: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
&.warning {
|
||||
border-left-color: $gold-star;
|
||||
}
|
||||
|
||||
&__bubble {
|
||||
padding: 16px;
|
||||
padding-left: 14px;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
border-radius: 4px 4px 4px 0;
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
|
||||
a {
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__owner {
|
||||
padding: 8px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
time {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.report-card {
|
||||
background: $ui-base-color;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&__profile {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
|
||||
.account {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
|
||||
&__avatar-wrapper {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__stats {
|
||||
flex: 0 0 auto;
|
||||
font-weight: 500;
|
||||
color: $darker-text-color;
|
||||
text-transform: uppercase;
|
||||
text-align: right;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
color: lighten($darker-text-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.red {
|
||||
color: $error-value-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__summary {
|
||||
&__item {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
border-top: 1px solid darken($ui-base-color, 4%);
|
||||
|
||||
&:hover {
|
||||
background: lighten($ui-base-color, 2%);
|
||||
}
|
||||
|
||||
&__reported-by,
|
||||
&__assigned {
|
||||
padding: 15px;
|
||||
flex: 0 0 auto;
|
||||
box-sizing: border-box;
|
||||
width: 150px;
|
||||
color: $darker-text-color;
|
||||
|
||||
&,
|
||||
.username {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex: 1 1 auto;
|
||||
max-width: calc(100% - 300px);
|
||||
|
||||
&__icon {
|
||||
color: $dark-text-color;
|
||||
margin-right: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&__content a {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
text-decoration: none;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.one-line {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ellipsized-ip {
|
||||
display: inline-block;
|
||||
max-width: 120px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
@function hex-color($color) {
|
||||
@if type-of($color) == 'color' {
|
||||
$color: str-slice(ie-hex-str($color), 4);
|
||||
}
|
||||
|
||||
@return '%23' + unquote($color);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
background: darken($ui-base-color, 7%);
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
font-weight: 400;
|
||||
color: $primary-text-color;
|
||||
text-rendering: optimizelegibility;
|
||||
font-feature-settings: "kern";
|
||||
text-size-adjust: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
&.system-font {
|
||||
// system-ui => standard property (Chrome/Android WebView 56+, Opera 43+, Safari 11+)
|
||||
// -apple-system => Safari <11 specific
|
||||
// BlinkMacSystemFont => Chrome <56 on macOS specific
|
||||
// Segoe UI => Windows 7/8/10
|
||||
// Oxygen => KDE
|
||||
// Ubuntu => Unity/Ubuntu
|
||||
// Cantarell => GNOME
|
||||
// Fira Sans => Firefox OS
|
||||
// Droid Sans => Older Androids (<4.0)
|
||||
// Helvetica Neue => Older macOS <10.11
|
||||
// $font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0)
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", $font-sans-serif, sans-serif;
|
||||
}
|
||||
|
||||
&.app-body {
|
||||
padding: 0;
|
||||
|
||||
&.layout-single-column {
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
&.layout-multiple-columns {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.with-modals--active {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.lighter {
|
||||
background: $ui-base-color;
|
||||
}
|
||||
|
||||
&.with-modals {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
&--active {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.player {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.embed {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
margin: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
.container {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&.admin {
|
||||
background: darken($ui-base-color, 4%);
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.error {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
color: $darker-text-color;
|
||||
background: $ui-base-color;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.dialog {
|
||||
vertical-align: middle;
|
||||
margin: 20px;
|
||||
|
||||
&__illustration {
|
||||
img {
|
||||
display: block;
|
||||
max-width: 470px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-top: -120px;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.app-holder {
|
||||
&,
|
||||
& > div {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
outline: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-single-column .app-holder {
|
||||
&,
|
||||
& > div {
|
||||
min-height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-multiple-columns .app-holder {
|
||||
&,
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,34 @@
|
|||
.compact-header {
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
line-height: 28px;
|
||||
color: $darker-text-color;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 10px;
|
||||
word-wrap: break-word;
|
||||
|
||||
@media screen and (max-width: 740px) {
|
||||
text-align: center;
|
||||
padding: 20px 10px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
small {
|
||||
font-weight: 400;
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
img {
|
||||
display: inline-block;
|
||||
margin-bottom: -5px;
|
||||
margin-right: 15px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,815 @@
|
|||
.container-alt {
|
||||
width: 700px;
|
||||
margin: 0 auto;
|
||||
margin-top: 40px;
|
||||
|
||||
@media screen and (max-width: 740px) {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
margin: 100px auto 50px;
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
margin: 40px auto 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
fill: $primary-text-color;
|
||||
height: 42px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: $primary-text-color;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
padding: 12px 16px;
|
||||
line-height: 32px;
|
||||
font-family: $font-display, sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.compose-standalone {
|
||||
.compose-form {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 0;
|
||||
margin-top: 40px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account-header {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
box-sizing: border-box;
|
||||
padding: 20px 0;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: -30px;
|
||||
margin-top: 40px;
|
||||
|
||||
@media screen and (max-width: 440px) {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
margin-bottom: 10px;
|
||||
padding: 20px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 8px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
margin: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
flex: 1 1 auto;
|
||||
color: $secondary-text-color;
|
||||
width: calc(100% - 88px);
|
||||
|
||||
.username {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.logout-link {
|
||||
display: block;
|
||||
font-size: 32px;
|
||||
line-height: 40px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-3 {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-auto-columns: 25%;
|
||||
grid-auto-rows: max-content;
|
||||
|
||||
.column-0 {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.column-3 {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.landing-page__call-to-action {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.flash-message {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 738px) {
|
||||
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
|
||||
|
||||
.landing-page__call-to-action {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.row__information-board {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.row__mascot {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
grid-gap: 0;
|
||||
grid-template-columns: minmax(0, 100%);
|
||||
|
||||
.column-0 {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.column-3 {
|
||||
grid-column: 1;
|
||||
grid-row: 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.public-layout {
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
padding-top: 48px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
border-radius: 4px;
|
||||
height: 48px;
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
z-index: 110;
|
||||
}
|
||||
|
||||
& > div {
|
||||
flex: 1 1 33.3%;
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.nav-left {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.nav-center {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
height: 18px;
|
||||
width: auto;
|
||||
position: relative;
|
||||
bottom: -2px;
|
||||
fill: $primary-text-color;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: lighten($ui-base-color, 12%);
|
||||
}
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
color: $darker-text-color;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
&.optional {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
background: lighten($ui-base-color, 16%);
|
||||
margin: 8px;
|
||||
margin-left: 0;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
background: lighten($ui-base-color, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$no-columns-breakpoint: 600px;
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: minmax(300px, 3fr) minmax(298px, 1fr);
|
||||
grid-auto-columns: 25%;
|
||||
grid-auto-rows: max-content;
|
||||
|
||||
.column-0 {
|
||||
grid-row: 1;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
grid-template-columns: 100%;
|
||||
grid-gap: 0;
|
||||
|
||||
.column-1 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.public-account-header {
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
|
||||
&.inactive {
|
||||
opacity: 0.5;
|
||||
|
||||
.public-account-header__image,
|
||||
.avatar {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
.logo-button {
|
||||
background-color: $secondary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__image {
|
||||
border-radius: 4px 4px 0 0;
|
||||
overflow: hidden;
|
||||
height: 300px;
|
||||
position: relative;
|
||||
background: darken($ui-base-color, 12%);
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: inset 0 -1px 1px 1px rgba($base-shadow-color, 0.15);
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
&--no-bar {
|
||||
margin-bottom: 0;
|
||||
|
||||
.public-account-header__image,
|
||||
.public-account-header__image img {
|
||||
border-radius: 4px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
|
||||
&__image::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__image,
|
||||
&__image img {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__bar {
|
||||
position: relative;
|
||||
margin-top: -80px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: block;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
padding-left: 20px - 4px;
|
||||
flex: 0 0 auto;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border-radius: 50%;
|
||||
border: 4px solid lighten($ui-base-color, 4%);
|
||||
background: darken($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
margin-top: 0;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-radius: 0 0 4px 4px;
|
||||
padding: 5px;
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 7px 0;
|
||||
padding-left: 10px;
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
flex: 1 1 auto;
|
||||
margin-left: 20px;
|
||||
|
||||
&__name {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 8px;
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
line-height: 18px * 1.5;
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-shadow: 1px 1px 1px $base-shadow-color;
|
||||
|
||||
small {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: $primary-text-color;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
margin-left: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&__name {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
h1 {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
text-shadow: none;
|
||||
|
||||
small {
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
height: 58px;
|
||||
|
||||
.details-counters {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
.details-counters {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.counter {
|
||||
width: 33.3%;
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 auto;
|
||||
color: $darker-text-color;
|
||||
padding: 10px;
|
||||
border-right: 1px solid lighten($ui-base-color, 4%);
|
||||
cursor: default;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-bottom: 4px solid $ui-primary-color;
|
||||
opacity: 0.5;
|
||||
transition: all 400ms ease;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&::after {
|
||||
border-bottom: 4px solid $highlight-text-color;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.inactive::after {
|
||||
border-bottom-color: $secondary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::after {
|
||||
opacity: 1;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.counter-label {
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.counter-number {
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
margin-bottom: 5px;
|
||||
color: $primary-text-color;
|
||||
font-family: $font-display, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1 1 auto;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
padding: 7px 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__extra {
|
||||
display: none;
|
||||
margin-top: 4px;
|
||||
|
||||
.public-account-bio {
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
margin: 0 -5px;
|
||||
|
||||
.account__header__fields {
|
||||
border-top: 1px solid lighten($ui-base-color, 12%);
|
||||
}
|
||||
|
||||
.roles {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__links {
|
||||
margin-top: -15px;
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
padding: 15px;
|
||||
font-weight: 500;
|
||||
|
||||
strong {
|
||||
font-weight: 700;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
display: block;
|
||||
flex: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account__section-headline {
|
||||
border-radius: 4px 4px 0 0;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.detailed-status__meta {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.public-account-bio {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
box-shadow: none;
|
||||
margin-bottom: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.account__header__fields {
|
||||
margin: 0;
|
||||
border-top: 0;
|
||||
|
||||
a {
|
||||
color: lighten($ui-highlight-color, 8%);
|
||||
}
|
||||
|
||||
dl:first-child .verified {
|
||||
border-radius: 0 4px 0 0;
|
||||
}
|
||||
|
||||
.verified a {
|
||||
color: $valid-value-color;
|
||||
}
|
||||
}
|
||||
|
||||
.account__header__content {
|
||||
padding: 20px;
|
||||
padding-bottom: 0;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
&__extra,
|
||||
.roles {
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
.roles {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.static-icon-button {
|
||||
color: $action-button-color;
|
||||
font-size: 18px;
|
||||
|
||||
& > span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
min-width: 100%;
|
||||
margin: 0 -5px;
|
||||
|
||||
& > div {
|
||||
box-sizing: border-box;
|
||||
flex: 1 0 auto;
|
||||
width: 300px;
|
||||
padding: 0 5px;
|
||||
margin-bottom: 10px;
|
||||
max-width: 33.333%;
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
margin: 0;
|
||||
border-top: 1px solid lighten($ui-base-color, 8%);
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.card__bar {
|
||||
background: $ui-base-color;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
.dashboard__counters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -5px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
& > div {
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 33.333%;
|
||||
padding: 0 5px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
& > div,
|
||||
& > a {
|
||||
padding: 20px;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
& > a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__num,
|
||||
&__text {
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
font-size: 24px;
|
||||
line-height: 21px;
|
||||
color: $primary-text-color;
|
||||
font-family: $font-display, sans-serif;
|
||||
margin-bottom: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard__widgets {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -5px;
|
||||
|
||||
& > div {
|
||||
flex: 0 0 33.333%;
|
||||
margin-bottom: 20px;
|
||||
|
||||
& > div {
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
a:not(.name-tag) {
|
||||
color: $ui-secondary-color;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
.emoji-mart {
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
color: $inverted-text-color;
|
||||
|
||||
&,
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.emoji-mart-emoji {
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-bar {
|
||||
border: 0 solid darken($ui-secondary-color, 8%);
|
||||
|
||||
&:first-child {
|
||||
border-bottom-width: 1px;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
background: $ui-secondary-color;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-top-width: 1px;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-anchors {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 6px;
|
||||
color: $lighter-text-color;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.emoji-mart-anchor {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 12px 4px;
|
||||
overflow: hidden;
|
||||
transition: color .1s ease-out;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: darken($lighter-text-color, 4%);
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-anchor-selected {
|
||||
color: $highlight-text-color;
|
||||
|
||||
&:hover {
|
||||
color: darken($highlight-text-color, 4%);
|
||||
}
|
||||
|
||||
.emoji-mart-anchor-bar {
|
||||
bottom: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-anchor-bar {
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background-color: $highlight-text-color;
|
||||
}
|
||||
|
||||
.emoji-mart-anchors {
|
||||
i {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
max-width: 22px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
max-height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-scroll {
|
||||
overflow-y: scroll;
|
||||
height: 270px;
|
||||
max-height: 35vh;
|
||||
padding: 0 6px 6px;
|
||||
background: $simple-background-color;
|
||||
will-change: transform;
|
||||
|
||||
&::-webkit-scrollbar-track:hover,
|
||||
&::-webkit-scrollbar-track:active {
|
||||
background-color: rgba($base-overlay-background, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-search {
|
||||
padding: 10px;
|
||||
padding-right: 45px;
|
||||
background: $simple-background-color;
|
||||
|
||||
input {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
padding: 7px 9px;
|
||||
font-family: inherit;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background: rgba($ui-secondary-color, 0.3);
|
||||
color: $inverted-text-color;
|
||||
border: 1px solid $ui-secondary-color;
|
||||
border-radius: 4px;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-category .emoji-mart-emoji {
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
z-index: 0;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba($ui-secondary-color, 0.7);
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-category-label {
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
padding: 5px 6px;
|
||||
background: $simple-background-color;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-emoji {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-size: 0;
|
||||
|
||||
span {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-no-results {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
padding-top: 70px;
|
||||
color: $light-text-color;
|
||||
|
||||
.emoji-mart-category-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.emoji-mart-no-results-label {
|
||||
margin-top: .2em;
|
||||
}
|
||||
|
||||
.emoji-mart-emoji:hover::before {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-preview {
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
.public-layout {
|
||||
.footer {
|
||||
text-align: left;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 60px;
|
||||
font-size: 12px;
|
||||
color: lighten($ui-base-color, 34%);
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: 1fr 1fr 2fr 1fr 1fr;
|
||||
|
||||
.column-0 {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
min-width: 0;
|
||||
text-align: center;
|
||||
|
||||
h4 a {
|
||||
color: lighten($ui-base-color, 34%);
|
||||
}
|
||||
}
|
||||
|
||||
.column-3 {
|
||||
grid-column: 4;
|
||||
grid-row: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.column-4 {
|
||||
grid-column: 5;
|
||||
grid-row: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 690px) {
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
|
||||
.column-0,
|
||||
.column-1 {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.column-3,
|
||||
.column-4 {
|
||||
grid-column: 3;
|
||||
}
|
||||
|
||||
.column-4 {
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.column-1 {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
.column-0,
|
||||
.column-1,
|
||||
.column-3,
|
||||
.column-4 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
ul a {
|
||||
text-decoration: none;
|
||||
color: lighten($ui-base-color, 34%);
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.brand {
|
||||
svg {
|
||||
display: block;
|
||||
height: 36px;
|
||||
width: auto;
|
||||
margin: 0 auto;
|
||||
fill: lighten($ui-base-color, 34%);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
svg path {
|
||||
fill: lighten($ui-base-color, 38%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.getting-started__footer {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 1em;
|
||||
width: 32vw;
|
||||
z-index: 10;
|
||||
text-align: right;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0.15rem 0;
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
i {
|
||||
margin: 0.5ch;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-left: 2ch;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,971 @@
|
|||
$no-columns-breakpoint: 600px;
|
||||
table {
|
||||
thead {
|
||||
|
||||
th {
|
||||
font-weight: 800;
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
@extend .text-btn
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-striped {
|
||||
margin: 1rem 0;
|
||||
|
||||
tr {
|
||||
|
||||
&:odd {
|
||||
background: $ui-base-lighter-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.group-form {
|
||||
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: $font-monospace, monospace;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
max-width: 400px;
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.simple_form {
|
||||
.input {
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
|
||||
&.hidden {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.radio_buttons {
|
||||
.radio {
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.radio > label {
|
||||
position: relative;
|
||||
padding-left: 28px;
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.boolean {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
|
||||
.label_input > label {
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
padding-top: 5px;
|
||||
color: $primary-text-color;
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.label_input,
|
||||
.hint {
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.label_input__wrapper {
|
||||
position: static;
|
||||
}
|
||||
|
||||
label.checkbox {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
label a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.recommended {
|
||||
position: absolute;
|
||||
margin: 0 4px;
|
||||
margin-top: -2px;
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
margin: 0 -5px;
|
||||
|
||||
.input {
|
||||
box-sizing: border-box;
|
||||
flex: 1 1 auto;
|
||||
width: 50%;
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: $darker-text-color;
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
|
||||
code {
|
||||
border-radius: 3px;
|
||||
padding: 0.2em 0.4em;
|
||||
background: darken($ui-base-color, 12%);
|
||||
}
|
||||
}
|
||||
|
||||
span.hint {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
p.hint {
|
||||
margin-bottom: 15px;
|
||||
color: $darker-text-color;
|
||||
|
||||
&.subtle-hint {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input.with_floating_label {
|
||||
.label_input {
|
||||
display: flex;
|
||||
|
||||
& > label {
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
min-width: 150px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.select .hint {
|
||||
margin-top: 6px;
|
||||
margin-left: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.input.with_label {
|
||||
.label_input > label {
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
color: $primary-text-color;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
word-wrap: break-word;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
ul {
|
||||
flex: 390px;
|
||||
}
|
||||
}
|
||||
|
||||
.input.with_block_label {
|
||||
max-width: none;
|
||||
|
||||
& > label {
|
||||
font-family: inherit;
|
||||
font-size: 16px;
|
||||
color: $primary-text-color;
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
ul {
|
||||
columns: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.required abbr {
|
||||
text-decoration: none;
|
||||
color: lighten($error-value-color, 12%);
|
||||
}
|
||||
|
||||
.fields-group {
|
||||
margin-bottom: 25px;
|
||||
|
||||
.input:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fields-row {
|
||||
display: flex;
|
||||
margin: 0 -10px;
|
||||
padding-top: 5px;
|
||||
margin-bottom: 25px;
|
||||
|
||||
.input {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
&__column {
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
flex: 1 1 auto;
|
||||
min-height: 1px;
|
||||
|
||||
&-6 {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.fields-group:last-child,
|
||||
.fields-row__column.fields-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
|
||||
&__column {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.fields-group:last-child,
|
||||
.fields-row__column.fields-group,
|
||||
.fields-row__column {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input.radio_buttons .radio label {
|
||||
margin-bottom: 5px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
color: $primary-text-color;
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.check_boxes {
|
||||
.checkbox {
|
||||
label {
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
color: $primary-text-color;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
position: relative;
|
||||
padding-top: 5px;
|
||||
padding-left: 25px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input.static .label_input__wrapper {
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
border: 1px solid $dark-text-color;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
input[type=number],
|
||||
input[type=email],
|
||||
input[type=password],
|
||||
textarea {
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
color: $primary-text-color;
|
||||
display: block;
|
||||
width: 100%;
|
||||
outline: 0;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
background: darken($ui-base-color, 10%);
|
||||
border: 1px solid darken($ui-base-color, 14%);
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
|
||||
&:invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:focus:invalid:not(:placeholder-shown) {
|
||||
border-color: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
&:required:valid {
|
||||
border-color: $valid-value-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: darken($ui-base-color, 20%);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
border-color: $highlight-text-color;
|
||||
background: darken($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.input.field_with_errors {
|
||||
label {
|
||||
color: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
input[type=number],
|
||||
input[type=email],
|
||||
input[type=password],
|
||||
textarea,
|
||||
select {
|
||||
border-color: lighten($error-red, 12%);
|
||||
}
|
||||
|
||||
.error {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
color: lighten($error-red, 12%);
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.input.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
|
||||
&.actions--top {
|
||||
margin-top: 0;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
button,
|
||||
.button,
|
||||
.block-button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: $ui-highlight-color;
|
||||
color: $primary-text-color;
|
||||
font-size: 18px;
|
||||
line-height: inherit;
|
||||
height: auto;
|
||||
padding: 10px;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
outline: 0;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($ui-highlight-color, 5%);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: darken($ui-highlight-color, 5%);
|
||||
}
|
||||
|
||||
&:disabled:hover {
|
||||
background-color: $ui-primary-color;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
background: $error-value-color;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($error-value-color, 5%);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: darken($error-value-color, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
color: $primary-text-color;
|
||||
display: block;
|
||||
width: 100%;
|
||||
outline: 0;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
background: darken($ui-base-color, 10%) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>") no-repeat right 8px center / auto 16px;
|
||||
border: 1px solid darken($ui-base-color, 14%);
|
||||
border-radius: 4px;
|
||||
padding-left: 10px;
|
||||
padding-right: 30px;
|
||||
height: 41px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 15px !important;
|
||||
}
|
||||
|
||||
.label_input {
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__append {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 1px;
|
||||
padding: 10px;
|
||||
padding-bottom: 9px;
|
||||
font-size: 16px;
|
||||
color: $dark-text-color;
|
||||
font-family: inherit;
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
max-width: 140px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 1px;
|
||||
width: 5px;
|
||||
background-image: linear-gradient(to right, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__overlay-area {
|
||||
position: relative;
|
||||
|
||||
&__overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba($ui-base-color, 0.65);
|
||||
backdrop-filter: blur(2px);
|
||||
border-radius: 4px;
|
||||
|
||||
&__content {
|
||||
text-align: center;
|
||||
|
||||
&.rich-formatting {
|
||||
&,
|
||||
p {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.block-icon {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.flash-message {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
color: $darker-text-color;
|
||||
border-radius: 4px;
|
||||
padding: 15px 10px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
|
||||
&.notice {
|
||||
border: 1px solid rgba($valid-value-color, 0.5);
|
||||
background: rgba($valid-value-color, 0.25);
|
||||
color: $valid-value-color;
|
||||
}
|
||||
|
||||
&.alert {
|
||||
border: 1px solid rgba($error-value-color, 0.5);
|
||||
background: rgba($error-value-color, 0.25);
|
||||
color: $error-value-color;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: $primary-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.oauth-code {
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 10px;
|
||||
font-family: $font-monospace, monospace;
|
||||
background: $ui-base-color;
|
||||
color: $primary-text-color;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 740px) and (min-width: 441px) {
|
||||
margin-top: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quick-nav {
|
||||
list-style: none;
|
||||
margin-bottom: 25px;
|
||||
font-size: 14px;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: lighten($highlight-text-color, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.oauth-prompt,
|
||||
.follow-prompt {
|
||||
margin-bottom: 30px;
|
||||
color: $darker-text-color;
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: $secondary-text-color;
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 740px) and (min-width: 441px) {
|
||||
margin-top: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.qr-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
flex: 0 0 auto;
|
||||
background: $simple-background-color;
|
||||
padding: 4px;
|
||||
margin: 0 10px 20px 0;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
display: inline-block;
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.qr-alternative {
|
||||
margin-bottom: 20px;
|
||||
color: $secondary-text-color;
|
||||
flex: 150px;
|
||||
|
||||
samp {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-form {
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.simple_form,
|
||||
.table-form {
|
||||
.warning {
|
||||
box-sizing: border-box;
|
||||
background: rgba($error-value-color, 0.5);
|
||||
color: $primary-text-color;
|
||||
text-shadow: 1px 1px 0 rgba($base-shadow-color, 0.3);
|
||||
box-shadow: 0 2px 6px rgba($base-shadow-color, 0.4);
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
a {
|
||||
color: $primary-text-color;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-pagination {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.actions,
|
||||
.pagination {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding: 30px 0;
|
||||
padding-right: 20px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.post-follow-actions {
|
||||
text-align: center;
|
||||
color: $darker-text-color;
|
||||
|
||||
div {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.alternative-login {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
color: $primary-text-color;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.scope-danger {
|
||||
color: $warning-red;
|
||||
}
|
||||
|
||||
.form_admin_settings_site_short_description,
|
||||
.form_admin_settings_site_description,
|
||||
.form_admin_settings_site_extended_description,
|
||||
.form_admin_settings_site_terms,
|
||||
.form_admin_settings_custom_css,
|
||||
.form_admin_settings_closed_registrations_message {
|
||||
textarea {
|
||||
font-family: $font-monospace, monospace;
|
||||
}
|
||||
}
|
||||
|
||||
.input-copy {
|
||||
background: darken($ui-base-color, 10%);
|
||||
border: 1px solid darken($ui-base-color, 14%);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 4px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
transition: border-color 300ms linear;
|
||||
|
||||
&__wrapper {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
font-family: $font-monospace, monospace;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 0 0 auto;
|
||||
margin: 4px;
|
||||
text-transform: none;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
padding: 7px 18px;
|
||||
padding-bottom: 6px;
|
||||
width: auto;
|
||||
transition: background 300ms linear;
|
||||
}
|
||||
|
||||
&.copied {
|
||||
border-color: $valid-value-color;
|
||||
transition: none;
|
||||
|
||||
button {
|
||||
background: $valid-value-color;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.connection-prompt {
|
||||
margin-bottom: 25px;
|
||||
|
||||
.fa-link {
|
||||
background-color: darken($ui-base-color, 4%);
|
||||
border-radius: 100%;
|
||||
font-size: 24px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
&__column {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
flex-shrink: 1;
|
||||
max-width: 50%;
|
||||
|
||||
&-sep {
|
||||
align-self: center;
|
||||
flex-grow: 0;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
p {
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__connection {
|
||||
background-color: lighten($ui-base-color, 8%);
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
border-radius: 4px;
|
||||
padding: 25px 10px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&::after {
|
||||
background-color: darken($ui-base-color, 4%);
|
||||
content: '';
|
||||
display: block;
|
||||
height: 100%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&__row {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__publish-button-wrapper {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
.introduction {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@media screen and (max-width: 920px) {
|
||||
background: darken($ui-base-color, 8%);
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
&__pager {
|
||||
background: darken($ui-base-color, 8%);
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__pager,
|
||||
&__frame {
|
||||
border-radius: 10px;
|
||||
width: 50vw;
|
||||
min-width: 920px;
|
||||
|
||||
@media screen and (max-width: 920px) {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__frame-wrapper {
|
||||
opacity: 0;
|
||||
transition: opacity 500ms linear;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
transition: opacity 50ms linear;
|
||||
}
|
||||
}
|
||||
|
||||
&__frame {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__illustration {
|
||||
height: 50vh;
|
||||
|
||||
@media screen and (max-width: 630px) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
border-top: 2px solid $ui-highlight-color;
|
||||
|
||||
&--columnized {
|
||||
display: flex;
|
||||
|
||||
& > div {
|
||||
flex: 1 1 33.33%;
|
||||
text-align: center;
|
||||
padding: 25px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 630px) {
|
||||
display: block;
|
||||
padding: 15px 0;
|
||||
padding-bottom: 20px;
|
||||
|
||||
& > div {
|
||||
padding: 10px 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 24px;
|
||||
line-height: 1.5;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color: $darker-text-color;
|
||||
|
||||
code {
|
||||
display: inline-block;
|
||||
background: darken($ui-base-color, 8%);
|
||||
font-size: 15px;
|
||||
border: 1px solid lighten($ui-base-color, 8%);
|
||||
border-radius: 2px;
|
||||
padding: 1px 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&--centered {
|
||||
padding: 25px;
|
||||
padding-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 25px;
|
||||
|
||||
@media screen and (max-width: 630px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid $ui-highlight-color;
|
||||
background: transparent;
|
||||
margin: 0 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
&.active {
|
||||
cursor: default;
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__action {
|
||||
padding: 25px;
|
||||
padding-top: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
.no-list {
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.recovery-codes {
|
||||
list-style: none;
|
||||
margin: 0 auto;
|
||||
|
||||
li {
|
||||
font-size: 125%;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
$messagingBoxWidth: 15em;
|
||||
$messagingBoxHeight: 20em;
|
||||
|
||||
.fixed-box {
|
||||
border: solid 1px white;
|
||||
padding: 1em;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.airmail-border {
|
||||
border: 0.25em solid transparent;
|
||||
border-image: 4 repeating-linear-gradient(-45deg, red 0, red 1em, white 0, white 2em,
|
||||
#58a 0, #58a 3em, white 0, white 4em);
|
||||
|
||||
}
|
||||
|
||||
.status-direct,
|
||||
.item-list .conversation {
|
||||
@extend .airmail-border;
|
||||
}
|
||||
|
||||
.messaging-box {
|
||||
@extend .fixed-box;
|
||||
|
||||
right: 1em;
|
||||
width: $messagingBoxWidth;
|
||||
background: $ui-base-color;
|
||||
|
||||
.messager-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation {
|
||||
|
||||
|
||||
.conversation__content {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.conversation_reply,
|
||||
.icon-button {
|
||||
&:hover {
|
||||
color: $ui-highlight-color;
|
||||
background: mix($ui-base-color, $ui-secondary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.conversation_reply,
|
||||
.icon-button,
|
||||
.status__action-bar-dropdown {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
width: 18em;
|
||||
height: 3.2em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.conversations_list {
|
||||
right: 1em;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
background: gray;
|
||||
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
@extend .fixed-box;
|
||||
width: $messagingBoxWidth;
|
||||
right: $messagingBoxWidth + 5em;
|
||||
background: $ui-secondary-color;
|
||||
|
||||
&.has-new-message {
|
||||
background: $ui-highlight-color;
|
||||
color: $classic-primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation_created-at {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.conversation_stream {
|
||||
padding-top: 1em;
|
||||
height: $messagingBoxHeight;
|
||||
overflow: auto;
|
||||
background: $ui-secondary-color;
|
||||
|
||||
.message {
|
||||
-webkit-border-radius: 0.5rem;
|
||||
-moz-border-radius: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.5em 1em;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.mine {
|
||||
text-align: right;
|
||||
background: $classic-primary-color;
|
||||
float: right;
|
||||
|
||||
.arrow-down {
|
||||
border-top-color: $classic-primary-color;
|
||||
left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.theirs {
|
||||
text-align: left;
|
||||
background: $ui-highlight-color;
|
||||
float: left;
|
||||
|
||||
.arrow-down {
|
||||
border-top-color: $ui-highlight-color;
|
||||
right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 20px solid transparent;
|
||||
border-top: 20px solid $classic-primary-color;
|
||||
position: relative;
|
||||
bottom: -1em;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
.modal-layout {
|
||||
background: $ui-base-color url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color($ui-base-lighter-color)}"/></svg>') repeat-x bottom fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modal-layout__mastodon {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
max-height: 235px;
|
||||
background: url('../images/elephant_ui_plane.svg') no-repeat left bottom / contain;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.account-header {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
.poll {
|
||||
margin-top: 16px;
|
||||
font-size: 14px;
|
||||
|
||||
li {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
height: 18px + 12px;
|
||||
}
|
||||
|
||||
&__chart {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
background: darken($ui-primary-color, 14%);
|
||||
|
||||
&.leading {
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 6px 0;
|
||||
line-height: 18px;
|
||||
cursor: default;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
input[type=radio],
|
||||
input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.autossugest-input {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: $inverted-text-color;
|
||||
outline: 0;
|
||||
font-family: inherit;
|
||||
background: $simple-background-color;
|
||||
border: 1px solid darken($simple-background-color, 14%);
|
||||
border-radius: 4px;
|
||||
padding: 6px 10px;
|
||||
|
||||
&:focus {
|
||||
border-color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.editable {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
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: 50%;
|
||||
vertical-align: middle;
|
||||
|
||||
&.checkbox {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: $valid-value-color;
|
||||
background: $valid-value-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__number {
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
font-weight: 700;
|
||||
padding: 0 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 5px;
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
&__link {
|
||||
display: inline;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
color: $dark-text-color;
|
||||
text-decoration: underline;
|
||||
font-size: inherit;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: rgba($dark-text-color, .1);
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__poll-wrapper {
|
||||
border-top: 1px solid darken($simple-background-color, 8%);
|
||||
|
||||
ul {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.poll__footer {
|
||||
border-top: 1px solid darken($simple-background-color, 8%);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
button,
|
||||
select {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.button.button-secondary {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
padding: 6px 10px;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
color: $action-button-color;
|
||||
border-color: $action-button-color;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.poll__text {
|
||||
flex: 0 0 auto;
|
||||
width: calc(100% - (23px + 6px));
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
color: $inverted-text-color;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
outline: 0;
|
||||
font-family: inherit;
|
||||
background: $simple-background-color url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(darken($simple-background-color, 14%))}'/></svg>") no-repeat right 8px center / auto 16px;
|
||||
border: 1px solid darken($simple-background-color, 14%);
|
||||
border-radius: 4px;
|
||||
padding: 6px 10px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.icon-button.disabled {
|
||||
color: darken($simple-background-color, 14%);
|
||||
}
|
||||
}
|
||||
|
||||
.muted .poll {
|
||||
color: $dark-text-color;
|
||||
|
||||
&__chart {
|
||||
background: rgba(darken($ui-primary-color, 14%), 0.2);
|
||||
|
||||
&.leading {
|
||||
background: rgba($ui-highlight-color, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* http://meyerweb.com/eric/tools/css/reset/
|
||||
v2.0 | 20110126
|
||||
License: none (public domain)
|
||||
*/
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-color: lighten($ui-base-color, 4%) rgba($base-overlay-background, 0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border: 0px none $base-border-color;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: lighten($ui-base-color, 6%);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
border: 0px none $base-border-color;
|
||||
border-radius: 0;
|
||||
background: rgba($base-overlay-background, 0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background: $ui-base-color;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:active {
|
||||
background: $ui-base-color;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
body.rtl {
|
||||
direction: rtl;
|
||||
|
||||
.column-header > button {
|
||||
text-align: right;
|
||||
padding-left: 0;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.landing-page__logo {
|
||||
margin-right: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.landing-page .features-list .features-list__row .visual {
|
||||
margin-left: 0;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.column-link__icon,
|
||||
.column-header__icon {
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.compose-form .compose-form__buttons-wrapper .character-counter__wrapper {
|
||||
margin-right: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.navigation-bar__profile {
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.search__input {
|
||||
padding-right: 10px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.search__icon .fa {
|
||||
right: auto;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.columns-area {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.column-header__buttons {
|
||||
left: 0;
|
||||
right: auto;
|
||||
margin-left: 0;
|
||||
margin-right: -15px;
|
||||
}
|
||||
|
||||
.column-inline-form .icon-button {
|
||||
margin-left: 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.column-header__links .text-btn {
|
||||
margin-left: 10px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.account__avatar-wrapper {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.column-header__back-button {
|
||||
padding-left: 5px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.column-header__setting-arrows {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.setting-toggle__label {
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.status__avatar {
|
||||
left: auto;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.status,
|
||||
.activity-stream .status.light {
|
||||
padding-left: 10px;
|
||||
padding-right: 68px;
|
||||
}
|
||||
|
||||
.status__info .status__display-name,
|
||||
.activity-stream .status.light .status__display-name {
|
||||
padding-left: 25px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.activity-stream .pre-header {
|
||||
padding-right: 68px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.status__prepend {
|
||||
margin-left: 0;
|
||||
margin-right: 68px;
|
||||
}
|
||||
|
||||
.status__prepend-icon-wrapper {
|
||||
left: auto;
|
||||
right: -26px;
|
||||
}
|
||||
|
||||
.activity-stream .pre-header .pre-header__icon {
|
||||
left: auto;
|
||||
right: 42px;
|
||||
}
|
||||
|
||||
.account__avatar-overlay-overlay {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.column-back-button--slim-button {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.status__relative-time,
|
||||
.activity-stream .status.light .status__header .status__meta {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.status__action-bar {
|
||||
|
||||
&__counter {
|
||||
margin-right: 0;
|
||||
margin-left: 11px;
|
||||
|
||||
.status__action-bar-button {
|
||||
margin-right: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status__action-bar-button {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 18px;
|
||||
}
|
||||
|
||||
.status__action-bar-dropdown {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.privacy-dropdown__dropdown {
|
||||
margin-left: 0;
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.privacy-dropdown__option__icon {
|
||||
margin-left: 10px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.detailed-status__display-name .display-name {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.detailed-status__display-avatar {
|
||||
margin-right: 0;
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.detailed-status__favorites,
|
||||
.detailed-status__reblogs {
|
||||
margin-left: 0;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.fa-ul {
|
||||
margin-left: 2.14285714em;
|
||||
}
|
||||
|
||||
.fa-li {
|
||||
left: auto;
|
||||
right: -2.14285714em;
|
||||
}
|
||||
|
||||
.admin-wrapper {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.admin-wrapper .sidebar ul a i.fa,
|
||||
a.table-action-link i.fa {
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.simple_form .check_boxes .checkbox label {
|
||||
padding-left: 0;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.simple_form .input.with_label.boolean label.checkbox {
|
||||
padding-left: 25px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.simple_form .check_boxes .checkbox input[type="checkbox"],
|
||||
.simple_form .input.boolean input[type="checkbox"] {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.simple_form .input.radio_buttons .radio {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.simple_form .input.radio_buttons .radio > label {
|
||||
padding-right: 28px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.simple_form .input-with-append .input input {
|
||||
padding-left: 142px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.simple_form .input.boolean label.checkbox {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.simple_form .input.boolean .label_input,
|
||||
.simple_form .input.boolean .hint {
|
||||
padding-left: 0;
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
.simple_form .label_input__append {
|
||||
right: auto;
|
||||
left: 3px;
|
||||
|
||||
&::after {
|
||||
right: auto;
|
||||
left: 0;
|
||||
background-image: linear-gradient(to left, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
|
||||
}
|
||||
}
|
||||
|
||||
.simple_form select {
|
||||
background: darken($ui-base-color, 10%) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 12%))}'/></svg>") no-repeat left 8px center / auto 16px;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.filters .filter-subset {
|
||||
margin-right: 0;
|
||||
margin-left: 45px;
|
||||
}
|
||||
|
||||
.landing-page .header-wrapper .mascot {
|
||||
right: 60px;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.landing-page__call-to-action .row__information-board {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.landing-page .header .hero .floats .float-1 {
|
||||
left: -120px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.landing-page .header .hero .floats .float-2 {
|
||||
left: 210px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.landing-page .header .hero .floats .float-3 {
|
||||
left: 110px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.landing-page .header .links .brand img {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.landing-page .fa-external-link {
|
||||
padding-right: 5px;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.landing-page .features #mastodon-timeline {
|
||||
margin-right: 0;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 631px) {
|
||||
.column,
|
||||
.drawer {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 5px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.columns-area > div {
|
||||
.column,
|
||||
.drawer {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.public-layout {
|
||||
.header {
|
||||
.nav-button {
|
||||
margin-left: 8px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.public-account-header__tabs {
|
||||
margin-left: 0;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.landing-page__information {
|
||||
.account__display-name {
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.account__avatar-wrapper {
|
||||
margin-left: 12px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card__bar .display-name {
|
||||
margin-left: 0;
|
||||
margin-right: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.fa-chevron-left::before {
|
||||
content: "\F054";
|
||||
}
|
||||
|
||||
.fa-chevron-right::before {
|
||||
content: "\F053";
|
||||
}
|
||||
|
||||
.column-back-button__icon {
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.column-header__setting-arrows .column-header__setting-btn:last-child {
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.simple_form .input.radio_buttons .radio > label input {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
.activity-stream {
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&--under-tabs {
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
margin-bottom: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&--headless {
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
|
||||
.detailed-status,
|
||||
.status {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
div[data-component] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.entry {
|
||||
background: $ui-base-color;
|
||||
|
||||
.detailed-status,
|
||||
.status,
|
||||
.load-more {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.detailed-status,
|
||||
.status,
|
||||
.load-more {
|
||||
border-bottom: 0;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
.detailed-status,
|
||||
.status,
|
||||
.load-more {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.detailed-status,
|
||||
.status,
|
||||
.load-more {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 740px) {
|
||||
.detailed-status,
|
||||
.status,
|
||||
.load-more {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--highlighted .entry {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.button.logo-button {
|
||||
flex: 0 auto;
|
||||
font-size: 14px;
|
||||
background: $ui-highlight-color;
|
||||
color: $primary-text-color;
|
||||
text-transform: none;
|
||||
line-height: 36px;
|
||||
height: auto;
|
||||
padding: 3px 15px;
|
||||
border: 0;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: auto;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
fill: $primary-text-color;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: lighten($ui-highlight-color, 10%);
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&.disabled {
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: $ui-primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.button--destructive {
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: $error-red;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
svg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.embed,
|
||||
.public-layout {
|
||||
.detailed-status {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 15px 15px 15px (48px + 15px * 2);
|
||||
min-height: 48px + 2px;
|
||||
|
||||
&__avatar {
|
||||
left: 15px;
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
&__prepend {
|
||||
margin-left: 48px + 15px * 2;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
&__prepend-icon-wrapper {
|
||||
left: -32px;
|
||||
}
|
||||
|
||||
.media-gallery,
|
||||
&__action-bar,
|
||||
.video-player {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
.table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 8px;
|
||||
line-height: 18px;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid $ui-base-color;
|
||||
text-align: left;
|
||||
background: darken($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
& > thead > tr > th {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid $ui-base-color;
|
||||
border-top: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
& > tbody > tr > th {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
& > tbody > tr:nth-child(odd) > td,
|
||||
& > tbody > tr:nth-child(odd) > th {
|
||||
background: $ui-base-color;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.inline-table {
|
||||
& > tbody > tr:nth-child(odd) {
|
||||
& > td,
|
||||
& > th {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
& > tbody > tr:first-child {
|
||||
& > td,
|
||||
& > th {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.batch-table {
|
||||
& > thead > tr > th {
|
||||
background: $ui-base-color;
|
||||
border-top: 1px solid darken($ui-base-color, 8%);
|
||||
border-bottom: 1px solid darken($ui-base-color, 8%);
|
||||
|
||||
&:first-child {
|
||||
border-radius: 4px 0 0;
|
||||
border-left: 1px solid darken($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 4px 0 0;
|
||||
border-right: 1px solid darken($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--invites tbody td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
samp {
|
||||
font-family: $font-monospace, monospace;
|
||||
}
|
||||
|
||||
button.table-action-link {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button.table-action-link,
|
||||
a.table-action-link {
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
padding: 0 10px;
|
||||
color: $darker-text-color;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
i.fa {
|
||||
font-weight: 400;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.batch-table {
|
||||
&__toolbar,
|
||||
&__row {
|
||||
display: flex;
|
||||
|
||||
&__select {
|
||||
box-sizing: border-box;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
min-height: 100%;
|
||||
|
||||
input {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
&--aligned {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions,
|
||||
&__content {
|
||||
padding: 8px 0;
|
||||
padding-right: 16px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__toolbar {
|
||||
border: 1px solid darken($ui-base-color, 8%);
|
||||
background: $ui-base-color;
|
||||
border-radius: 4px 0 0;
|
||||
height: 47px;
|
||||
align-items: center;
|
||||
|
||||
&__actions {
|
||||
text-align: right;
|
||||
padding-right: 16px - 5px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__row {
|
||||
border: 1px solid darken($ui-base-color, 8%);
|
||||
border-top: 0;
|
||||
background: darken($ui-base-color, 4%);
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
&:first-child {
|
||||
border-top: 1px solid darken($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: darken($ui-base-color, 2%);
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
background: $ui-base-color;
|
||||
|
||||
&:hover {
|
||||
background: lighten($ui-base-color, 2%);
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 16px;
|
||||
|
||||
&--unpadded {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status__content {
|
||||
padding-top: 0;
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.nothing-here {
|
||||
border: 1px solid darken($ui-base-color, 8%);
|
||||
border-top: 0;
|
||||
box-shadow: none;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
border-top: 1px solid darken($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 870px) {
|
||||
.accounts-table tbody td.optional {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Commonly used web colors
|
||||
$black: #000000; // Black
|
||||
$white: #ffffff; // White
|
||||
$success-green: #79bd9a !default; // Padua
|
||||
$error-red: #df405a !default; // Cerise
|
||||
$warning-red: #ff5050 !default; // Sunset Orange
|
||||
$gold-star: #ca8f04 !default; // Dark Goldenrod
|
||||
|
||||
// Values from the classic Mastodon UI
|
||||
$classic-base-color: #282c37; // Midnight Express
|
||||
$classic-primary-color: #9baec8; // Echo Blue
|
||||
$classic-secondary-color: #d9e1e8; // Pattens Blue
|
||||
$classic-highlight-color: #2b90d9; // Summer Sky
|
||||
|
||||
// Variables for defaults in UI
|
||||
$base-shadow-color: $black !default;
|
||||
$base-overlay-background: $black !default;
|
||||
$base-border-color: $white !default;
|
||||
$simple-background-color: $white !default;
|
||||
$valid-value-color: $success-green !default;
|
||||
$error-value-color: $error-red !default;
|
||||
|
||||
// Tell UI to use selected colors
|
||||
$ui-base-color: $classic-base-color !default; // Darkest
|
||||
$ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
|
||||
$ui-primary-color: $classic-primary-color !default; // Lighter
|
||||
$ui-secondary-color: $classic-secondary-color !default; // Lightest
|
||||
$ui-highlight-color: $classic-highlight-color !default;
|
||||
|
||||
// Variables for texts
|
||||
$primary-text-color: $white !default;
|
||||
$darker-text-color: $ui-primary-color !default;
|
||||
$dark-text-color: $ui-base-lighter-color !default;
|
||||
$secondary-text-color: $ui-secondary-color !default;
|
||||
$highlight-text-color: $ui-highlight-color !default;
|
||||
$action-button-color: $ui-base-lighter-color !default;
|
||||
// For texts on inverted backgrounds
|
||||
$inverted-text-color: $ui-base-color !default;
|
||||
$lighter-text-color: $ui-base-lighter-color !default;
|
||||
$light-text-color: $ui-primary-color !default;
|
||||
|
||||
// Language codes that uses CJK fonts
|
||||
$cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
|
||||
|
||||
// Variables for components
|
||||
$media-modal-media-max-width: 100%;
|
||||
// put margins on top and bottom of image to avoid the screen covered by image.
|
||||
$media-modal-media-max-height: 80%;
|
||||
|
||||
$no-gap-breakpoint: 415px;
|
||||
|
||||
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
||||
$font-display: 'mastodon-font-display' !default;
|
||||
$font-monospace: 'mastodon-font-monospace' !default;
|
|
@ -0,0 +1,568 @@
|
|||
.hero-widget {
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
|
||||
&__img {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 4px 4px 0 0;
|
||||
background: $base-shadow-color;
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
background: $ui-base-color;
|
||||
padding: 20px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
font-size: 15px;
|
||||
color: $darker-text-color;
|
||||
line-height: 20px;
|
||||
word-wrap: break-word;
|
||||
font-weight: 400;
|
||||
|
||||
.emojione {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -3px 0 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
em {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 700;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
|
||||
a {
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.endorsements-widget {
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
h4 {
|
||||
padding: 10px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
.account {
|
||||
padding: 10px 0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.account__display-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 44px 44px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box-widget {
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
background: $ui-base-color;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
}
|
||||
|
||||
.contact-widget,
|
||||
.landing-page__information.contact-widget {
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
min-height: 100%;
|
||||
border-radius: 4px;
|
||||
background: $ui-base-color;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
}
|
||||
|
||||
.contact-widget {
|
||||
font-size: 15px;
|
||||
color: $darker-text-color;
|
||||
line-height: 20px;
|
||||
word-wrap: break-word;
|
||||
font-weight: 400;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__mail {
|
||||
margin-top: 10px;
|
||||
|
||||
a {
|
||||
color: $primary-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.moved-account-widget {
|
||||
padding: 15px;
|
||||
padding-bottom: 20px;
|
||||
border-radius: 4px;
|
||||
background: $ui-base-color;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
color: $secondary-text-color;
|
||||
font-weight: 400;
|
||||
margin-bottom: 10px;
|
||||
|
||||
strong,
|
||||
a {
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&.mention {
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
margin-bottom: 15px;
|
||||
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__card {
|
||||
.detailed-status__display-avatar {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.detailed-status__display-name {
|
||||
margin-bottom: 0;
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.memoriam-widget {
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
background: $base-shadow-color;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
border-radius: 4px;
|
||||
padding: 60px 15px;
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
|
||||
h1 {
|
||||
color: $primary-text-color;
|
||||
font-size: 36px;
|
||||
line-height: 1.1;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
margin-top: 0;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.directory {
|
||||
background: $ui-base-color;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
|
||||
&__tag {
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
|
||||
& > a,
|
||||
& > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: $ui-base-color;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.2);
|
||||
}
|
||||
|
||||
& > a {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
&.active > a {
|
||||
background: $ui-highlight-color;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.disabled > div {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
h4 {
|
||||
flex: 1 1 auto;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: $primary-text-color;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.fa {
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
small {
|
||||
display: block;
|
||||
font-weight: 400;
|
||||
font-size: 15px;
|
||||
margin-top: 8px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.active h4 {
|
||||
&,
|
||||
.fa,
|
||||
small,
|
||||
.trends__item__current {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-stack {
|
||||
flex: 0 0 auto;
|
||||
width: (36px + 4px) * 3;
|
||||
}
|
||||
|
||||
&.active .avatar-stack .account__avatar {
|
||||
border-color: $ui-highlight-color;
|
||||
}
|
||||
|
||||
.trends__item__current {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-stack {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.account__avatar {
|
||||
flex: 0 0 auto;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-left: -10px;
|
||||
background: darken($ui-base-color, 8%);
|
||||
border: 2px solid $ui-base-color;
|
||||
|
||||
&:nth-child(1) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accounts-table {
|
||||
width: 100%;
|
||||
|
||||
.account {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
thead th {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
color: $darker-text-color;
|
||||
font-weight: 700;
|
||||
padding: 10px;
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
tbody td {
|
||||
padding: 15px 0;
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&__count {
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: $primary-text-color;
|
||||
|
||||
small {
|
||||
display: block;
|
||||
color: $darker-text-color;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&__comment {
|
||||
width: 50%;
|
||||
vertical-align: initial !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
tbody td.optional {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.moved-account-widget,
|
||||
.memoriam-widget,
|
||||
.box-widget,
|
||||
.contact-widget,
|
||||
.landing-page__information.contact-widget,
|
||||
.directory,
|
||||
.page-header {
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
$maximum-width: 1235px;
|
||||
$fluid-breakpoint: $maximum-width + 20px;
|
||||
|
||||
.statuses-grid {
|
||||
min-height: 600px;
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
width: 100% !important; // Masonry layout is unnecessary at this width
|
||||
}
|
||||
|
||||
&__item {
|
||||
width: (960px - 20px) / 3;
|
||||
|
||||
@media screen and (max-width: $fluid-breakpoint) {
|
||||
width: (940px - 20px) / 3;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
.detailed-status {
|
||||
border-radius: 4px;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
border-top: 1px solid lighten($ui-base-color, 16%);
|
||||
}
|
||||
|
||||
&.compact {
|
||||
.detailed-status__meta {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.status__content {
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
|
||||
.emojione {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -3px 0 0;
|
||||
}
|
||||
|
||||
.status__content__spoiler-link {
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery,
|
||||
.status-card,
|
||||
.video-player {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notice-widget {
|
||||
margin-bottom: 10px;
|
||||
color: $darker-text-color;
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
color: $ui-highlight-color;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// christmas snow
|
||||
.round-button {
|
||||
cursor: pointer;
|
||||
margin: 0.5em;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.snow-button {
|
||||
-webkit-border-radius: 100%;
|
||||
-moz-border-radius: 100%;
|
||||
border-radius: 100%;
|
||||
background: $ui-secondary-color;
|
||||
@extend .round-button;
|
||||
|
||||
.icon {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $ui-primary-color;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
@import 'large_center';
|
||||
// custom sheet made on https://www.cipherbliss.com
|
||||
// Commonly used web colors
|
||||
$black: #fff; // Black
|
||||
$white: #000; // White
|
||||
$success-green: #6bbd77 !default; // Padua
|
||||
$error-red: #d4839b !default; // Cerise
|
||||
$warning-red: #528dc8 !default; // Sunset Orange
|
||||
$gold-star: #00ec84 !default; // Dark Goldenrod
|
||||
|
||||
// User Interface Colors
|
||||
$ui-base-color: #313644; // Midnight Express
|
||||
$ui-base-lighter-color: #bdd2d6;
|
||||
$ui-primary-color: #a1ccff; // Echo Blue
|
||||
$ui-secondary-color: #7fc0ff; // Pattens Blue
|
||||
$ui-highlight-color: #00a7d1; // Summer Sky
|
||||
|
||||
// Variables for components
|
||||
$media-modal-media-max-width: 100%;
|
||||
// put margins on top and bottom of image to avoid the screen covered by image.
|
||||
$media-modal-media-max-height: 80%;
|
||||
// fix
|
||||
|
||||
.media-gallery {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
// then we import the rest of the world
|
||||
@import 'application';
|
||||
@import 'bliss/messaging';
|
||||
|
||||
.debug, .well {
|
||||
padding: 0.5rem;
|
||||
border: solid 1px greenyellow;
|
||||
background: yellow;
|
||||
color: #222;
|
||||
|
||||
}
|
||||
|
||||
.spacer {
|
||||
display: block;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.small-texts {
|
||||
.timelines {
|
||||
.column-link {
|
||||
|
||||
|
||||
}
|
||||
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
.column {
|
||||
width: 35em;
|
||||
}
|
||||
|
||||
.drawer__inner__mastodon > img {
|
||||
padding: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.compose-form__publish-button-wrapper .button {
|
||||
background: #a1bded
|
||||
}
|
||||
|
||||
div[aria-label="Accueil"],
|
||||
div[aria-label="Notifications"] {
|
||||
width: 27em;
|
||||
}
|
||||
|
||||
.media-gallery {
|
||||
width: 115%;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.media-gallery,
|
||||
.status-card.compact.interactive {
|
||||
margin-left: -4.4em;
|
||||
}
|
||||
|
||||
.status-card__image-image {
|
||||
width: 100% !important;
|
||||
}
|
|
@ -5,7 +5,7 @@ $white: #ffffff;
|
|||
$classic-base-color: #282c37;
|
||||
$classic-primary-color: #9baec8;
|
||||
$classic-secondary-color: #d9e1e8;
|
||||
$classic-highlight-color: #2b90d9;
|
||||
$classic-highlight-color: #6274d9;
|
||||
|
||||
// Differences
|
||||
$success-green: lighten(#3c754d, 8%);
|
||||
|
@ -21,7 +21,7 @@ $ui-highlight-color: #2b90d9;
|
|||
|
||||
$primary-text-color: $black !default;
|
||||
$darker-text-color: $classic-base-color !default;
|
||||
$dark-text-color: #444b5d;
|
||||
$dark-text-color: #45505d;
|
||||
$action-button-color: #606984;
|
||||
|
||||
$inverted-text-color: $black !default;
|
||||
|
|
|
@ -41,6 +41,28 @@ $inverted-text-color: $ui-base-color !default;
|
|||
$lighter-text-color: $ui-base-lighter-color !default;
|
||||
$light-text-color: $ui-primary-color !default;
|
||||
|
||||
// custom sheet made on https://www.cipherbliss.com
|
||||
// Commonly used web colors
|
||||
$black: #fff; // Black
|
||||
$white: #000; // White
|
||||
$success-green: #6bbd77 !default; // Padua
|
||||
$error-red: #d4839b !default; // Cerise
|
||||
$warning-red: #528dc8 !default; // Sunset Orange
|
||||
$gold-star: #00ec84 !default; // Dark Goldenrod
|
||||
|
||||
// User Interface Colors
|
||||
$ui-base-color: #313644; // Midnight Express
|
||||
$ui-base-lighter-color: #bdd2d6;
|
||||
$ui-primary-color: #a1ccff; // Echo Blue
|
||||
$ui-secondary-color: #7fc0ff; // Pattens Blue
|
||||
$ui-highlight-color: #00a7d1; // Summer Sky
|
||||
|
||||
// Variables for components
|
||||
$media-modal-media-max-width: 100%;
|
||||
// put margins on top and bottom of image to avoid the screen covered by image.
|
||||
$media-modal-media-max-height: 80%;
|
||||
// fix
|
||||
|
||||
// Language codes that uses CJK fonts
|
||||
$cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: user_groups
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# name :string not null
|
||||
# createdAt :datetime
|
||||
# visibility :string
|
||||
# account_id :integer
|
||||
# creator_id :integer
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class UserGroup < ApplicationRecord
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
class UserGroupSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :createAt, :visibility, :members
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StatusLengthValidator < ActiveModel::Validator
|
||||
MAX_CHARS = 500
|
||||
MAX_CHARS = 7777
|
||||
|
||||
def validate(status)
|
||||
return unless status.local? && !status.reblog?
|
||||
|
|
|
@ -20,3 +20,11 @@
|
|||
- else
|
||||
= table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}")
|
||||
= table_link_to 'globe', t('admin.accounts.public'), ActivityPub::TagManager.instance.url_for(account)
|
||||
%td
|
||||
= number_with_delimiter account.statuses_count
|
||||
%td
|
||||
= number_with_delimiter account.following_count
|
||||
%td
|
||||
= number_with_delimiter account.followers_count
|
||||
%td
|
||||
= table_link_to 'trash', t('admin.accounts.web'), web_path("accounts/#{account.id}/action/new?type=suspend")
|
||||
|
|
|
@ -43,7 +43,11 @@
|
|||
%th= t('admin.accounts.role')
|
||||
%th= t('admin.accounts.most_recent_ip')
|
||||
%th= t('admin.accounts.most_recent_activity')
|
||||
%th
|
||||
%th links
|
||||
%th statuses
|
||||
%th following
|
||||
%th followers
|
||||
%th nuke
|
||||
%tbody
|
||||
= render @accounts
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
%hr.spacer/
|
||||
|
||||
.fields-group
|
||||
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
|
||||
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked'), recommended: true
|
||||
|
||||
.fields-group
|
||||
= f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
.container.group-form
|
||||
|
||||
%p
|
||||
WORK IN PROGRESS
|
||||
|
||||
= simple_form_for(@user_group) do |f|
|
||||
= f.error_notification
|
||||
|
||||
.form-inputs
|
||||
= f.input :name
|
||||
= f.input :createdAt
|
||||
= f.input :visibility
|
||||
= f.input :account_id
|
||||
|
||||
.form-actions
|
||||
= f.button :submit
|
|
@ -0,0 +1,10 @@
|
|||
- content_for :page_title do
|
||||
= 'Modifier le groupe'
|
||||
|
||||
%h1 Editing user_group
|
||||
|
||||
= render 'form'
|
||||
|
||||
= link_to 'Show', @user_group
|
||||
\|
|
||||
= link_to 'Back', user_groups_path
|
|
@ -0,0 +1,34 @@
|
|||
- content_for :page_title do
|
||||
= 'Groupes'
|
||||
.container
|
||||
.page-header
|
||||
%h1 Listing user_groups
|
||||
|
||||
%table.table-responsive.table-striped
|
||||
%thead
|
||||
%tr
|
||||
%th Name
|
||||
%th Createat
|
||||
%th Visibility
|
||||
%th Creator ID
|
||||
%th Statuses count
|
||||
%th
|
||||
%th
|
||||
%th
|
||||
|
||||
%tbody
|
||||
- @user_groups.each do |user_group|
|
||||
%tr
|
||||
%td= user_group.name
|
||||
%td= user_group.created_at
|
||||
%td= user_group.visibility
|
||||
%td= user_group.account_id
|
||||
%td= "0"
|
||||
%td= link_to 'Show', user_group
|
||||
%td= link_to 'Edit', edit_user_group_path(user_group)
|
||||
%td= link_to 'Destroy', user_group, :method => :delete, :data => { :confirm => 'Are you sure?' }
|
||||
|
||||
%br
|
||||
|
||||
.text-btn
|
||||
= link_to 'New User group', new_user_group_path
|
|
@ -0,0 +1,8 @@
|
|||
- content_for :page_title do
|
||||
= 'Nouveau groupe'
|
||||
.container
|
||||
%h1 New user_group
|
||||
|
||||
= render 'form'
|
||||
|
||||
= link_to 'Back', user_groups_path
|
|
@ -0,0 +1,18 @@
|
|||
%p#notice= notice
|
||||
|
||||
%p
|
||||
%b Name:
|
||||
= @user_group.name
|
||||
%p
|
||||
%b Createat:
|
||||
= @user_group.created_at
|
||||
%p
|
||||
%b Visibility:
|
||||
= @user_group.visibility
|
||||
%p
|
||||
%b Creator:
|
||||
= @user_group.account_id
|
||||
|
||||
= link_to 'Edit', edit_user_group_path(@user_group)
|
||||
\|
|
||||
= link_to 'Back', user_groups_path
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
module Mastodon
|
||||
module Version
|
||||
module_function
|
||||
def source_base_url
|
||||
'https://framagit.org/tykayn/mastodon'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1137,7 +1137,7 @@ fr:
|
|||
title: "%{instance} Conditions d’utilisation et politique de confidentialité"
|
||||
themes:
|
||||
contrast: Mastodon (Contraste élevé)
|
||||
default: Mastodon (Sombre)
|
||||
default: Bliss (Sombre)
|
||||
mastodon-light: Mastodon (Clair)
|
||||
time:
|
||||
formats:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# important settings can be changed from the admin interface.
|
||||
|
||||
defaults: &defaults
|
||||
site_title: Mastodon
|
||||
site_title: Mastodon Bliss
|
||||
site_short_description: ''
|
||||
site_description: ''
|
||||
site_extended_description: ''
|
||||
|
@ -53,6 +53,7 @@ defaults: &defaults
|
|||
must_be_following_dm: false
|
||||
reserved_usernames:
|
||||
- admin
|
||||
- tykayn
|
||||
- support
|
||||
- help
|
||||
- root
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
default: styles/application.scss
|
||||
default: styles/bliss.scss
|
||||
custom: styles/custom.scss
|
||||
mastodon: styles/custom.scss
|
||||
contrast: styles/contrast.scss
|
||||
mastodon-light: styles/mastodon-light.scss
|
||||
|
|
|
@ -25,7 +25,7 @@ function formatPublicPath(host = '', path = '') {
|
|||
}
|
||||
|
||||
const output = {
|
||||
path: resolve('public', settings.public_output_path),
|
||||
path : resolve('public', settings.public_output_path),
|
||||
publicPath: formatPublicPath(env.CDN_HOST, settings.public_output_path),
|
||||
};
|
||||
|
||||
|
|
|
@ -48,5 +48,3 @@ setLocale({messages, localeData});
|
|||
});
|
||||
|
||||
module.exports = outPaths;
|
||||
|
||||
|
||||
|
|
|
@ -26,16 +26,16 @@ if (process.env.S3_ENABLED === 'true') {
|
|||
}
|
||||
|
||||
module.exports = merge(sharedConfig, {
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
stats: 'normal',
|
||||
bail: true,
|
||||
mode : 'production',
|
||||
devtool : 'source-map',
|
||||
stats : 'normal',
|
||||
bail : true,
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimize : true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
cache: true,
|
||||
parallel: true,
|
||||
cache : true,
|
||||
parallel : true,
|
||||
sourceMap: true,
|
||||
}),
|
||||
],
|
||||
|
@ -44,21 +44,21 @@ module.exports = merge(sharedConfig, {
|
|||
plugins: [
|
||||
new CompressionPlugin({
|
||||
filename: '[path].gz[query]',
|
||||
cache: true,
|
||||
test: /\.(js|css|html|json|ico|svg|eot|otf|ttf|map)$/,
|
||||
cache : true,
|
||||
test : /\.(js|css|html|json|ico|svg|eot|otf|ttf|map)$/,
|
||||
}),
|
||||
new BundleAnalyzerPlugin({ // generates report.html
|
||||
analyzerMode: 'static',
|
||||
openAnalyzer: false,
|
||||
logLevel: 'silent', // do not bother Webpacker, who runs with --json and parses stdout
|
||||
logLevel : 'silent', // do not bother Webpacker, who runs with --json and parses stdout
|
||||
}),
|
||||
new OfflinePlugin({
|
||||
publicPath: output.publicPath, // sw.js must be served from the root to avoid scope issues
|
||||
publicPath : output.publicPath, // sw.js must be served from the root to avoid scope issues
|
||||
safeToUseOptionalCaches: true,
|
||||
caches: {
|
||||
main: [':rest:'],
|
||||
caches : {
|
||||
main : [':rest:'],
|
||||
additional: [':externals:'],
|
||||
optional: [
|
||||
optional : [
|
||||
'**/locale_*.js', // don't fetch every locale; the user only needs one
|
||||
'**/*_polyfills-*.js', // the user may not need polyfills
|
||||
'**/*.woff2', // the user may have system-fonts enabled
|
||||
|
@ -71,11 +71,11 @@ module.exports = merge(sharedConfig, {
|
|||
'**/*.ogg',
|
||||
],
|
||||
},
|
||||
externals: [
|
||||
externals : [
|
||||
'/emoji/1f602.svg', // used for emoji picker dropdown
|
||||
'/emoji/sheet_10.png', // used in emoji-mart
|
||||
],
|
||||
excludes: [
|
||||
excludes : [
|
||||
'**/*.gz',
|
||||
'**/*.map',
|
||||
'stats.json',
|
||||
|
@ -86,12 +86,12 @@ module.exports = merge(sharedConfig, {
|
|||
'**/*-webfont-*.svg',
|
||||
'**/*.woff',
|
||||
],
|
||||
ServiceWorker: {
|
||||
entry: `imports-loader?ATTACHMENT_HOST=>${encodeURIComponent(JSON.stringify(attachmentHost))}!${encodeURI(path.join(__dirname, '../../app/javascript/mastodon/service_worker/entry.js'))}`,
|
||||
cacheName: 'mastodon',
|
||||
output: '../assets/sw.js',
|
||||
ServiceWorker : {
|
||||
entry : `imports-loader?ATTACHMENT_HOST=>${encodeURIComponent(JSON.stringify(attachmentHost))}!${encodeURI(path.join(__dirname, '../../app/javascript/mastodon/service_worker/entry.js'))}`,
|
||||
cacheName : 'mastodon',
|
||||
output : '../assets/sw.js',
|
||||
publicPath: '/sw.js',
|
||||
minify: true,
|
||||
minify : true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
|
|
@ -31,31 +31,31 @@ module.exports = {
|
|||
Object.keys(themes).reduce((themePaths, name) => {
|
||||
themePaths[name] = resolve(join(settings.source_path, themes[name]));
|
||||
return themePaths;
|
||||
}, {})
|
||||
}, {}),
|
||||
),
|
||||
|
||||
output: {
|
||||
filename: 'js/[name]-[chunkhash].js',
|
||||
chunkFilename: 'js/[name]-[chunkhash].chunk.js',
|
||||
filename : 'js/[name]-[chunkhash].js',
|
||||
chunkFilename : 'js/[name]-[chunkhash].chunk.js',
|
||||
hotUpdateChunkFilename: 'js/[id]-[hash].hot-update.js',
|
||||
path: output.path,
|
||||
publicPath: output.publicPath,
|
||||
path : output.path,
|
||||
publicPath : output.publicPath,
|
||||
},
|
||||
|
||||
optimization: {
|
||||
runtimeChunk: {
|
||||
runtimeChunk : {
|
||||
name: 'common',
|
||||
},
|
||||
splitChunks: {
|
||||
splitChunks : {
|
||||
cacheGroups: {
|
||||
default: false,
|
||||
vendors: false,
|
||||
common: {
|
||||
name: 'common',
|
||||
chunks: 'all',
|
||||
common : {
|
||||
name : 'common',
|
||||
chunks : 'all',
|
||||
minChunks: 2,
|
||||
minSize: 0,
|
||||
test: /^(?!.*[\\\/]node_modules[\\\/]react-intl[\\\/]).+$/,
|
||||
minSize : 0,
|
||||
test : /^(?!.*[\\\/]node_modules[\\\/]react-intl[\\\/]).+$/,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -73,17 +73,17 @@ module.exports = {
|
|||
// temporary fix for https://github.com/ReactTraining/react-router/issues/5576
|
||||
// to reduce bundle size
|
||||
resource.request = resource.request.replace(/^history/, 'history/es');
|
||||
}
|
||||
},
|
||||
),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'css/[name]-[contenthash:8].css',
|
||||
filename : 'css/[name]-[contenthash:8].css',
|
||||
chunkFilename: 'css/[name]-[contenthash:8].chunk.css',
|
||||
}),
|
||||
new AssetsManifestPlugin({
|
||||
integrity: false,
|
||||
integrity : false,
|
||||
entrypoints: true,
|
||||
writeToDisk: true,
|
||||
publicPath: true,
|
||||
publicPath : true,
|
||||
}),
|
||||
new CopyPlugin([
|
||||
{ from: 'node_modules/tesseract.js/dist/worker.min.js', to: 'ocr' },
|
||||
|
@ -93,7 +93,7 @@ module.exports = {
|
|||
|
||||
resolve: {
|
||||
extensions: settings.extensions,
|
||||
modules: [
|
||||
modules : [
|
||||
resolve(settings.source_path),
|
||||
'node_modules',
|
||||
],
|
||||
|
|
|
@ -41,7 +41,6 @@ const validateLanguages = (languages, validators) => {
|
|||
console.error(`
|
||||
Error: Specified invalid LANGUAGES:
|
||||
${invalidLanguages.map(({ language, error }) => `* ${language}: ${error.message}`).join('\n')}
|
||||
|
||||
Use yarn "manage:translations -- --help" for usage information
|
||||
`);
|
||||
process.exit(1);
|
||||
|
@ -49,12 +48,9 @@ Use yarn "manage:translations -- --help" for usage information
|
|||
};
|
||||
|
||||
const usage = `Usage: yarn manage:translations [OPTIONS] [LANGUAGES]
|
||||
|
||||
Manage JavaScript translation files in Mastodon. Generates and update translations in translationsDirectory: ${translationsDirectory}
|
||||
|
||||
LANGUAGES
|
||||
The RFC5646 language tag for the language you want to test or fix. If you want to input multiple languages, separate them with space.
|
||||
|
||||
Available languages:
|
||||
${availableLanguages.join(', ')}
|
||||
`;
|
||||
|
@ -62,10 +58,10 @@ ${availableLanguages.join(', ')}
|
|||
const { argv } = require('yargs')
|
||||
.usage(usage)
|
||||
.option('f', {
|
||||
alias: 'force',
|
||||
default: false,
|
||||
alias : 'force',
|
||||
default : false,
|
||||
describe: 'force using the provided languages. create files if not exists.',
|
||||
type: 'boolean',
|
||||
type : 'boolean',
|
||||
});
|
||||
|
||||
// check if message directory exists
|
||||
|
@ -93,7 +89,7 @@ manageTranslations({
|
|||
detectDuplicateIds: false,
|
||||
singleMessagesFile: true,
|
||||
languages,
|
||||
jsonOptions: {
|
||||
jsonOptions : {
|
||||
trailingNewline: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class AddCommentsToDomainBlocks < ActiveRecord::Migration[5.2]
|
||||
class AddCommentsToDomainBlocks < ActiveRecordinteger::Migration[5.2]
|
||||
def change
|
||||
add_column :domain_blocks, :private_comment, :text
|
||||
add_column :domain_blocks, :public_comment, :text
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
class CreateUserGroups < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :user_groups do |t|
|
||||
t.string :name, null: false
|
||||
t.datetime :createdAt
|
||||
t.string :visibility
|
||||
t.integer :account_id
|
||||
t.integer :creator_id
|
||||
t.integer :statuses_count
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
36
db/schema.rb
36
db/schema.rb
|
@ -200,11 +200,11 @@ ActiveRecord::Schema.define(version: 2019_12_12_003415) do
|
|||
t.bigint "user_id"
|
||||
t.string "dump_file_name"
|
||||
t.string "dump_content_type"
|
||||
t.bigint "dump_file_size"
|
||||
t.datetime "dump_updated_at"
|
||||
t.boolean "processed", default: false, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "dump_file_size"
|
||||
end
|
||||
|
||||
create_table "blocks", force: :cascade do |t|
|
||||
|
@ -703,6 +703,30 @@ ActiveRecord::Schema.define(version: 2019_12_12_003415) do
|
|||
t.index ["tag_id", "status_id"], name: "index_statuses_tags_on_tag_id_and_status_id", unique: true
|
||||
end
|
||||
|
||||
create_table "stream_entries", force: :cascade do |t|
|
||||
t.bigint "activity_id"
|
||||
t.string "activity_type"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.boolean "hidden", default: false, null: false
|
||||
t.bigint "account_id"
|
||||
t.index ["account_id", "activity_type", "id"], name: "index_stream_entries_on_account_id_and_activity_type_and_id"
|
||||
t.index ["activity_id", "activity_type"], name: "index_stream_entries_on_activity_id_and_activity_type"
|
||||
end
|
||||
|
||||
create_table "subscriptions", force: :cascade do |t|
|
||||
t.string "callback_url", default: "", null: false
|
||||
t.string "secret"
|
||||
t.datetime "expires_at"
|
||||
t.boolean "confirmed", default: false, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "last_successful_delivery_at"
|
||||
t.string "domain"
|
||||
t.bigint "account_id", null: false
|
||||
t.index ["account_id", "callback_url"], name: "index_subscriptions_on_account_id_and_callback_url", unique: true
|
||||
end
|
||||
|
||||
create_table "tags", force: :cascade do |t|
|
||||
t.string "name", default: "", null: false
|
||||
t.datetime "created_at", null: false
|
||||
|
@ -728,6 +752,16 @@ ActiveRecord::Schema.define(version: 2019_12_12_003415) do
|
|||
t.index ["uri"], name: "index_tombstones_on_uri"
|
||||
end
|
||||
|
||||
create_table "user_groups", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.datetime "createdAt"
|
||||
t.string "visibility"
|
||||
t.integer "account_id"
|
||||
t.integer "creator_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "user_invite_requests", force: :cascade do |t|
|
||||
t.bigint "user_id"
|
||||
t.text "text"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -67,8 +67,8 @@
|
|||
"@babel/preset-env": "^7.7.6",
|
||||
"@babel/preset-react": "^7.7.4",
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"@gamestdio/websocket": "^0.3.2",
|
||||
"@clusterws/cws": "^0.16.0",
|
||||
"@gamestdio/websocket": "^0.3.2",
|
||||
"array-includes": "^3.1.0",
|
||||
"arrow-key-navigation": "^1.1.0",
|
||||
"autoprefixer": "^9.7.3",
|
||||
|
@ -118,8 +118,12 @@
|
|||
"offline-plugin": "^5.0.7",
|
||||
"path-complete-extname": "^1.0.0",
|
||||
"pg": "^6.4.0",
|
||||
"postcss": "^7.0.24",
|
||||
"postcss-flexbugs-fixes": "^4.1.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-object-fit-images": "^1.1.2",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"punycode": "^2.1.0",
|
||||
"rails-ujs": "^5.2.4",
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
module.exports = ({ env }) => ({
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
'postcss-object-fit-images': {},
|
||||
cssnano: env === 'production' ? {} : false,
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('postcss-import'),
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env')({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009'
|
||||
},
|
||||
});
|
||||
stage: 3
|
||||
})
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
yarn install v1.16.0
|
||||
warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json.
|
||||
[1/6] Validating package.json...
|
||||
[2/6] Resolving packages...
|
||||
success Already up-to-date.
|
||||
Done in 0.51s.
|
||||
yarn install v1.16.0
|
||||
warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json.
|
||||
[1/6] Validating package.json...
|
||||
[2/6] Resolving packages...
|
||||
success Already up-to-date.
|
||||
Done in 0.49s.
|
||||
Everything's up-to-date. Nothing to do
|
||||
Rendering errors/500.html.haml within layouts/error
|
||||
Rendered errors/500.html.haml within layouts/error (604.9ms)
|
||||
[Webpacker] Everything's up-to-date. Nothing to do
|
||||
[Webpacker] Everything's up-to-date. Nothing to do
|
|
@ -0,0 +1,141 @@
|
|||
require 'rails_helper'
|
||||
|
||||
# This spec was generated by rspec-rails when you ran the scaffold generator.
|
||||
# It demonstrates how one might use RSpec to specify the controller code that
|
||||
# was generated by Rails when you ran the scaffold generator.
|
||||
#
|
||||
# It assumes that the implementation code is generated by the rails scaffold
|
||||
# generator. If you are using any extension libraries to generate different
|
||||
# controller code, this generated spec may or may not pass.
|
||||
#
|
||||
# It only uses APIs available in rails and/or rspec-rails. There are a number
|
||||
# of tools you can use to make these specs even more expressive, but we're
|
||||
# sticking to rails and rspec-rails APIs to keep things simple and stable.
|
||||
#
|
||||
# Compared to earlier versions of this generator, there is very limited use of
|
||||
# stubs and message expectations in this spec. Stubs are only used when there
|
||||
# is no simpler way to get a handle on the object needed for the example.
|
||||
# Message expectations are only used when there is no simpler way to specify
|
||||
# that an instance is receiving a specific message.
|
||||
#
|
||||
# Also compared to earlier versions of this generator, there are no longer any
|
||||
# expectations of assigns and templates rendered. These features have been
|
||||
# removed from Rails core in Rails 5, but can be added back in via the
|
||||
# `rails-controller-testing` gem.
|
||||
|
||||
RSpec.describe UserGroupsController, type: :controller do
|
||||
|
||||
# This should return the minimal set of attributes required to create a valid
|
||||
# UserGroup. As you add validations to UserGroup, be sure to
|
||||
# adjust the attributes here as well.
|
||||
let(:valid_attributes) {
|
||||
skip("Add a hash of attributes valid for your model")
|
||||
}
|
||||
|
||||
let(:invalid_attributes) {
|
||||
skip("Add a hash of attributes invalid for your model")
|
||||
}
|
||||
|
||||
# This should return the minimal set of values that should be in the session
|
||||
# in order to pass any filters (e.g. authentication) defined in
|
||||
# UserGroupsController. Be sure to keep this updated too.
|
||||
let(:valid_session) { {} }
|
||||
|
||||
describe "GET #index" do
|
||||
it "returns a success response" do
|
||||
UserGroup.create! valid_attributes
|
||||
get :index, params: {}, session: valid_session
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #show" do
|
||||
it "returns a success response" do
|
||||
user_group = UserGroup.create! valid_attributes
|
||||
get :show, params: {id: user_group.to_param}, session: valid_session
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #new" do
|
||||
it "returns a success response" do
|
||||
get :new, params: {}, session: valid_session
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #edit" do
|
||||
it "returns a success response" do
|
||||
user_group = UserGroup.create! valid_attributes
|
||||
get :edit, params: {id: user_group.to_param}, session: valid_session
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST #create" do
|
||||
context "with valid params" do
|
||||
it "creates a new UserGroup" do
|
||||
expect {
|
||||
post :create, params: {user_group: valid_attributes}, session: valid_session
|
||||
}.to change(UserGroup, :count).by(1)
|
||||
end
|
||||
|
||||
it "redirects to the created user_group" do
|
||||
post :create, params: {user_group: valid_attributes}, session: valid_session
|
||||
expect(response).to redirect_to(UserGroup.last)
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid params" do
|
||||
it "returns a success response (i.e. to display the 'new' template)" do
|
||||
post :create, params: {user_group: invalid_attributes}, session: valid_session
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT #update" do
|
||||
context "with valid params" do
|
||||
let(:new_attributes) {
|
||||
skip("Add a hash of attributes valid for your model")
|
||||
}
|
||||
|
||||
it "updates the requested user_group" do
|
||||
user_group = UserGroup.create! valid_attributes
|
||||
put :update, params: {id: user_group.to_param, user_group: new_attributes}, session: valid_session
|
||||
user_group.reload
|
||||
skip("Add assertions for updated state")
|
||||
end
|
||||
|
||||
it "redirects to the user_group" do
|
||||
user_group = UserGroup.create! valid_attributes
|
||||
put :update, params: {id: user_group.to_param, user_group: valid_attributes}, session: valid_session
|
||||
expect(response).to redirect_to(user_group)
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid params" do
|
||||
it "returns a success response (i.e. to display the 'edit' template)" do
|
||||
user_group = UserGroup.create! valid_attributes
|
||||
put :update, params: {id: user_group.to_param, user_group: invalid_attributes}, session: valid_session
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE #destroy" do
|
||||
it "destroys the requested user_group" do
|
||||
user_group = UserGroup.create! valid_attributes
|
||||
expect {
|
||||
delete :destroy, params: {id: user_group.to_param}, session: valid_session
|
||||
}.to change(UserGroup, :count).by(-1)
|
||||
end
|
||||
|
||||
it "redirects to the user_groups list" do
|
||||
user_group = UserGroup.create! valid_attributes
|
||||
delete :destroy, params: {id: user_group.to_param}, session: valid_session
|
||||
expect(response).to redirect_to(user_groups_url)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
Fabricator(:user_group) do
|
||||
name "MyString"
|
||||
createAt "2019-08-27 14:04:56"
|
||||
visibility "MyString"
|
||||
members ""
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
require 'rails_helper'
|
||||
|
||||
# Specs in this file have access to a helper object that includes
|
||||
# the UserGroupsHelper. For example:
|
||||
#
|
||||
# describe UserGroupsHelper do
|
||||
# describe "string concat" do
|
||||
# it "concats two strings with spaces" do
|
||||
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
RSpec.describe UserGroupsHelper, type: :helper do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe UserGroup, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe "UserGroups", type: :request do
|
||||
describe "GET /user_groups" do
|
||||
it "works! (now write some real specs)" do
|
||||
get user_groups_path
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe UserGroupsController, type: :routing do
|
||||
describe "routing" do
|
||||
it "routes to #index" do
|
||||
expect(:get => "/user_groups").to route_to("user_groups#index")
|
||||
end
|
||||
|
||||
it "routes to #new" do
|
||||
expect(:get => "/user_groups/new").to route_to("user_groups#new")
|
||||
end
|
||||
|
||||
it "routes to #show" do
|
||||
expect(:get => "/user_groups/1").to route_to("user_groups#show", :id => "1")
|
||||
end
|
||||
|
||||
it "routes to #edit" do
|
||||
expect(:get => "/user_groups/1/edit").to route_to("user_groups#edit", :id => "1")
|
||||
end
|
||||
|
||||
|
||||
it "routes to #create" do
|
||||
expect(:post => "/user_groups").to route_to("user_groups#create")
|
||||
end
|
||||
|
||||
it "routes to #update via PUT" do
|
||||
expect(:put => "/user_groups/1").to route_to("user_groups#update", :id => "1")
|
||||
end
|
||||
|
||||
it "routes to #update via PATCH" do
|
||||
expect(:patch => "/user_groups/1").to route_to("user_groups#update", :id => "1")
|
||||
end
|
||||
|
||||
it "routes to #destroy" do
|
||||
expect(:delete => "/user_groups/1").to route_to("user_groups#destroy", :id => "1")
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue