Merge branch 'master' of https://framagit.org/tykayn/mastodon into merged-master

This commit is contained in:
Baptiste Lemoine 2020-07-17 20:49:44 +02:00
commit c3f46d6b28
138 changed files with 30903 additions and 810 deletions

8
.gitignore vendored
View File

@ -67,3 +67,11 @@ yarn-debug.log
# Ignore Docker option files # Ignore Docker option files
docker-compose.override.yml docker-compose.override.yml
/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
.yarn-integrity

1
.nope_browserlistrc Normal file
View File

@ -0,0 +1 @@
defaults

View File

@ -1,4 +1,4 @@
![Mastodon](https://i.imgur.com/NhZc40l.png) ![Mastodon](https://i.imgur.com/NhZc40l.png) - [mastodon.CipherBliss.com](https://mastodon.CipherBliss.com) version
======== ========
[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases] [![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases]

View File

@ -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.

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/

View File

@ -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

View File

@ -0,0 +1,2 @@
module UserGroupsHelper
end

View File

@ -118,20 +118,20 @@ export function fetchAccount(id) {
dispatch(fetchAccountFail(id, error)); dispatch(fetchAccountFail(id, error));
}); });
}; };
}; }
export function fetchAccountRequest(id) { export function fetchAccountRequest(id) {
return { return {
type: ACCOUNT_FETCH_REQUEST, type: ACCOUNT_FETCH_REQUEST,
id, id,
}; };
}; }
export function fetchAccountSuccess() { export function fetchAccountSuccess() {
return { return {
type: ACCOUNT_FETCH_SUCCESS, type: ACCOUNT_FETCH_SUCCESS,
}; };
}; }
export function fetchAccountFail(id, error) { export function fetchAccountFail(id, error) {
return { return {
@ -140,7 +140,7 @@ export function fetchAccountFail(id, error) {
error, error,
skipAlert: true, skipAlert: true,
}; };
}; }
export function followAccount(id, reblogs = true) { export function followAccount(id, reblogs = true) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -155,7 +155,7 @@ export function followAccount(id, reblogs = true) {
dispatch(followAccountFail(error, locked)); dispatch(followAccountFail(error, locked));
}); });
}; };
}; }
export function unfollowAccount(id) { export function unfollowAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -167,7 +167,7 @@ export function unfollowAccount(id) {
dispatch(unfollowAccountFail(error)); dispatch(unfollowAccountFail(error));
}); });
}; };
}; }
export function followAccountRequest(id, locked) { export function followAccountRequest(id, locked) {
return { return {
@ -176,7 +176,7 @@ export function followAccountRequest(id, locked) {
locked, locked,
skipLoading: true, skipLoading: true,
}; };
}; }
export function followAccountSuccess(relationship, alreadyFollowing) { export function followAccountSuccess(relationship, alreadyFollowing) {
return { return {
@ -185,7 +185,7 @@ export function followAccountSuccess(relationship, alreadyFollowing) {
alreadyFollowing, alreadyFollowing,
skipLoading: true, skipLoading: true,
}; };
}; }
export function followAccountFail(error, locked) { export function followAccountFail(error, locked) {
return { return {
@ -194,7 +194,7 @@ export function followAccountFail(error, locked) {
locked, locked,
skipLoading: true, skipLoading: true,
}; };
}; }
export function unfollowAccountRequest(id) { export function unfollowAccountRequest(id) {
return { return {
@ -202,7 +202,7 @@ export function unfollowAccountRequest(id) {
id, id,
skipLoading: true, skipLoading: true,
}; };
}; }
export function unfollowAccountSuccess(relationship, statuses) { export function unfollowAccountSuccess(relationship, statuses) {
return { return {
@ -211,7 +211,7 @@ export function unfollowAccountSuccess(relationship, statuses) {
statuses, statuses,
skipLoading: true, skipLoading: true,
}; };
}; }
export function unfollowAccountFail(error) { export function unfollowAccountFail(error) {
return { return {
@ -219,7 +219,7 @@ export function unfollowAccountFail(error) {
error, error,
skipLoading: true, skipLoading: true,
}; };
}; }
export function blockAccount(id) { export function blockAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -232,7 +232,7 @@ export function blockAccount(id) {
dispatch(blockAccountFail(id, error)); dispatch(blockAccountFail(id, error));
}); });
}; };
}; }
export function unblockAccount(id) { export function unblockAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -244,14 +244,14 @@ export function unblockAccount(id) {
dispatch(unblockAccountFail(id, error)); dispatch(unblockAccountFail(id, error));
}); });
}; };
}; }
export function blockAccountRequest(id) { export function blockAccountRequest(id) {
return { return {
type: ACCOUNT_BLOCK_REQUEST, type: ACCOUNT_BLOCK_REQUEST,
id, id,
}; };
}; }
export function blockAccountSuccess(relationship, statuses) { export function blockAccountSuccess(relationship, statuses) {
return { return {
@ -259,35 +259,35 @@ export function blockAccountSuccess(relationship, statuses) {
relationship, relationship,
statuses, statuses,
}; };
}; }
export function blockAccountFail(error) { export function blockAccountFail(error) {
return { return {
type: ACCOUNT_BLOCK_FAIL, type: ACCOUNT_BLOCK_FAIL,
error, error,
}; };
}; }
export function unblockAccountRequest(id) { export function unblockAccountRequest(id) {
return { return {
type: ACCOUNT_UNBLOCK_REQUEST, type: ACCOUNT_UNBLOCK_REQUEST,
id, id,
}; };
}; }
export function unblockAccountSuccess(relationship) { export function unblockAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNBLOCK_SUCCESS, type: ACCOUNT_UNBLOCK_SUCCESS,
relationship, relationship,
}; };
}; }
export function unblockAccountFail(error) { export function unblockAccountFail(error) {
return { return {
type: ACCOUNT_UNBLOCK_FAIL, type: ACCOUNT_UNBLOCK_FAIL,
error, error,
}; };
}; }
export function muteAccount(id, notifications) { export function muteAccount(id, notifications) {
@ -301,7 +301,7 @@ export function muteAccount(id, notifications) {
dispatch(muteAccountFail(id, error)); dispatch(muteAccountFail(id, error));
}); });
}; };
}; }
export function unmuteAccount(id) { export function unmuteAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -313,14 +313,14 @@ export function unmuteAccount(id) {
dispatch(unmuteAccountFail(id, error)); dispatch(unmuteAccountFail(id, error));
}); });
}; };
}; }
export function muteAccountRequest(id) { export function muteAccountRequest(id) {
return { return {
type: ACCOUNT_MUTE_REQUEST, type: ACCOUNT_MUTE_REQUEST,
id, id,
}; };
}; }
export function muteAccountSuccess(relationship, statuses) { export function muteAccountSuccess(relationship, statuses) {
return { return {
@ -328,35 +328,35 @@ export function muteAccountSuccess(relationship, statuses) {
relationship, relationship,
statuses, statuses,
}; };
}; }
export function muteAccountFail(error) { export function muteAccountFail(error) {
return { return {
type: ACCOUNT_MUTE_FAIL, type: ACCOUNT_MUTE_FAIL,
error, error,
}; };
}; }
export function unmuteAccountRequest(id) { export function unmuteAccountRequest(id) {
return { return {
type: ACCOUNT_UNMUTE_REQUEST, type: ACCOUNT_UNMUTE_REQUEST,
id, id,
}; };
}; }
export function unmuteAccountSuccess(relationship) { export function unmuteAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNMUTE_SUCCESS, type: ACCOUNT_UNMUTE_SUCCESS,
relationship, relationship,
}; };
}; }
export function unmuteAccountFail(error) { export function unmuteAccountFail(error) {
return { return {
type: ACCOUNT_UNMUTE_FAIL, type: ACCOUNT_UNMUTE_FAIL,
error, error,
}; };
}; }
export function fetchFollowers(id) { export function fetchFollowers(id) {
@ -373,14 +373,14 @@ export function fetchFollowers(id) {
dispatch(fetchFollowersFail(id, error)); dispatch(fetchFollowersFail(id, error));
}); });
}; };
}; }
export function fetchFollowersRequest(id) { export function fetchFollowersRequest(id) {
return { return {
type: FOLLOWERS_FETCH_REQUEST, type: FOLLOWERS_FETCH_REQUEST,
id, id,
}; };
}; }
export function fetchFollowersSuccess(id, accounts, next) { export function fetchFollowersSuccess(id, accounts, next) {
return { return {
@ -389,16 +389,16 @@ export function fetchFollowersSuccess(id, accounts, next) {
accounts, accounts,
next, next,
}; };
}; }
export function fetchFollowersFail(id, error) { export function fetchFollowersFail(id, error) {
return { return {
type: FOLLOWERS_FETCH_FAIL, type : FOLLOWERS_FETCH_FAIL,
id, id,
error, error,
skipNotFound: true, skipNotFound: true,
}; };
}; }
export function expandFollowers(id) { export function expandFollowers(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -420,14 +420,14 @@ export function expandFollowers(id) {
dispatch(expandFollowersFail(id, error)); dispatch(expandFollowersFail(id, error));
}); });
}; };
}; }
export function expandFollowersRequest(id) { export function expandFollowersRequest(id) {
return { return {
type: FOLLOWERS_EXPAND_REQUEST, type: FOLLOWERS_EXPAND_REQUEST,
id, id,
}; };
}; }
export function expandFollowersSuccess(id, accounts, next) { export function expandFollowersSuccess(id, accounts, next) {
return { return {
@ -436,7 +436,7 @@ export function expandFollowersSuccess(id, accounts, next) {
accounts, accounts,
next, next,
}; };
}; }
export function expandFollowersFail(id, error) { export function expandFollowersFail(id, error) {
return { return {
@ -444,7 +444,7 @@ export function expandFollowersFail(id, error) {
id, id,
error, error,
}; };
}; }
export function fetchFollowing(id) { export function fetchFollowing(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -460,14 +460,14 @@ export function fetchFollowing(id) {
dispatch(fetchFollowingFail(id, error)); dispatch(fetchFollowingFail(id, error));
}); });
}; };
}; }
export function fetchFollowingRequest(id) { export function fetchFollowingRequest(id) {
return { return {
type: FOLLOWING_FETCH_REQUEST, type: FOLLOWING_FETCH_REQUEST,
id, id,
}; };
}; }
export function fetchFollowingSuccess(id, accounts, next) { export function fetchFollowingSuccess(id, accounts, next) {
return { return {
@ -476,16 +476,16 @@ export function fetchFollowingSuccess(id, accounts, next) {
accounts, accounts,
next, next,
}; };
}; }
export function fetchFollowingFail(id, error) { export function fetchFollowingFail(id, error) {
return { return {
type: FOLLOWING_FETCH_FAIL, type : FOLLOWING_FETCH_FAIL,
id, id,
error, error,
skipNotFound: true, skipNotFound: true,
}; };
}; }
export function expandFollowing(id) { export function expandFollowing(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -507,14 +507,14 @@ export function expandFollowing(id) {
dispatch(expandFollowingFail(id, error)); dispatch(expandFollowingFail(id, error));
}); });
}; };
}; }
export function expandFollowingRequest(id) { export function expandFollowingRequest(id) {
return { return {
type: FOLLOWING_EXPAND_REQUEST, type: FOLLOWING_EXPAND_REQUEST,
id, id,
}; };
}; }
export function expandFollowingSuccess(id, accounts, next) { export function expandFollowingSuccess(id, accounts, next) {
return { return {
@ -523,7 +523,7 @@ export function expandFollowingSuccess(id, accounts, next) {
accounts, accounts,
next, next,
}; };
}; }
export function expandFollowingFail(id, error) { export function expandFollowingFail(id, error) {
return { return {
@ -531,7 +531,7 @@ export function expandFollowingFail(id, error) {
id, id,
error, error,
}; };
}; }
export function fetchRelationships(accountIds) { export function fetchRelationships(accountIds) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -550,7 +550,7 @@ export function fetchRelationships(accountIds) {
dispatch(fetchRelationshipsFail(error)); dispatch(fetchRelationshipsFail(error));
}); });
}; };
}; }
export function fetchRelationshipsRequest(ids) { export function fetchRelationshipsRequest(ids) {
return { return {
@ -558,7 +558,7 @@ export function fetchRelationshipsRequest(ids) {
ids, ids,
skipLoading: true, skipLoading: true,
}; };
}; }
export function fetchRelationshipsSuccess(relationships) { export function fetchRelationshipsSuccess(relationships) {
return { return {
@ -566,16 +566,16 @@ export function fetchRelationshipsSuccess(relationships) {
relationships, relationships,
skipLoading: true, skipLoading: true,
}; };
}; }
export function fetchRelationshipsFail(error) { export function fetchRelationshipsFail(error) {
return { return {
type: RELATIONSHIPS_FETCH_FAIL, type : RELATIONSHIPS_FETCH_FAIL,
error, error,
skipLoading: true, skipLoading : true,
skipNotFound: true, skipNotFound: true,
}; };
}; }
export function fetchFollowRequests() { export function fetchFollowRequests() {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -587,13 +587,13 @@ export function fetchFollowRequests() {
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
}).catch(error => dispatch(fetchFollowRequestsFail(error))); }).catch(error => dispatch(fetchFollowRequestsFail(error)));
}; };
}; }
export function fetchFollowRequestsRequest() { export function fetchFollowRequestsRequest() {
return { return {
type: FOLLOW_REQUESTS_FETCH_REQUEST, type: FOLLOW_REQUESTS_FETCH_REQUEST,
}; };
}; }
export function fetchFollowRequestsSuccess(accounts, next) { export function fetchFollowRequestsSuccess(accounts, next) {
return { return {
@ -601,14 +601,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
accounts, accounts,
next, next,
}; };
}; }
export function fetchFollowRequestsFail(error) { export function fetchFollowRequestsFail(error) {
return { return {
type: FOLLOW_REQUESTS_FETCH_FAIL, type: FOLLOW_REQUESTS_FETCH_FAIL,
error, error,
}; };
}; }
export function expandFollowRequests() { export function expandFollowRequests() {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -626,13 +626,13 @@ export function expandFollowRequests() {
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
}).catch(error => dispatch(expandFollowRequestsFail(error))); }).catch(error => dispatch(expandFollowRequestsFail(error)));
}; };
}; }
export function expandFollowRequestsRequest() { export function expandFollowRequestsRequest() {
return { return {
type: FOLLOW_REQUESTS_EXPAND_REQUEST, type: FOLLOW_REQUESTS_EXPAND_REQUEST,
}; };
}; }
export function expandFollowRequestsSuccess(accounts, next) { export function expandFollowRequestsSuccess(accounts, next) {
return { return {
@ -640,14 +640,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
accounts, accounts,
next, next,
}; };
}; }
export function expandFollowRequestsFail(error) { export function expandFollowRequestsFail(error) {
return { return {
type: FOLLOW_REQUESTS_EXPAND_FAIL, type: FOLLOW_REQUESTS_EXPAND_FAIL,
error, error,
}; };
}; }
export function authorizeFollowRequest(id) { export function authorizeFollowRequest(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -658,21 +658,21 @@ export function authorizeFollowRequest(id) {
.then(() => dispatch(authorizeFollowRequestSuccess(id))) .then(() => dispatch(authorizeFollowRequestSuccess(id)))
.catch(error => dispatch(authorizeFollowRequestFail(id, error))); .catch(error => dispatch(authorizeFollowRequestFail(id, error)));
}; };
}; }
export function authorizeFollowRequestRequest(id) { export function authorizeFollowRequestRequest(id) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST, type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
id, id,
}; };
}; }
export function authorizeFollowRequestSuccess(id) { export function authorizeFollowRequestSuccess(id) {
return { return {
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS, type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
id, id,
}; };
}; }
export function authorizeFollowRequestFail(id, error) { export function authorizeFollowRequestFail(id, error) {
return { return {
@ -680,7 +680,7 @@ export function authorizeFollowRequestFail(id, error) {
id, id,
error, error,
}; };
}; }
export function rejectFollowRequest(id) { export function rejectFollowRequest(id) {
@ -692,21 +692,21 @@ export function rejectFollowRequest(id) {
.then(() => dispatch(rejectFollowRequestSuccess(id))) .then(() => dispatch(rejectFollowRequestSuccess(id)))
.catch(error => dispatch(rejectFollowRequestFail(id, error))); .catch(error => dispatch(rejectFollowRequestFail(id, error)));
}; };
}; }
export function rejectFollowRequestRequest(id) { export function rejectFollowRequestRequest(id) {
return { return {
type: FOLLOW_REQUEST_REJECT_REQUEST, type: FOLLOW_REQUEST_REJECT_REQUEST,
id, id,
}; };
}; }
export function rejectFollowRequestSuccess(id) { export function rejectFollowRequestSuccess(id) {
return { return {
type: FOLLOW_REQUEST_REJECT_SUCCESS, type: FOLLOW_REQUEST_REJECT_SUCCESS,
id, id,
}; };
}; }
export function rejectFollowRequestFail(id, error) { export function rejectFollowRequestFail(id, error) {
return { return {
@ -714,7 +714,7 @@ export function rejectFollowRequestFail(id, error) {
id, id,
error, error,
}; };
}; }
export function pinAccount(id) { export function pinAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -726,7 +726,7 @@ export function pinAccount(id) {
dispatch(pinAccountFail(error)); dispatch(pinAccountFail(error));
}); });
}; };
}; }
export function unpinAccount(id) { export function unpinAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -738,46 +738,46 @@ export function unpinAccount(id) {
dispatch(unpinAccountFail(error)); dispatch(unpinAccountFail(error));
}); });
}; };
}; }
export function pinAccountRequest(id) { export function pinAccountRequest(id) {
return { return {
type: ACCOUNT_PIN_REQUEST, type: ACCOUNT_PIN_REQUEST,
id, id,
}; };
}; }
export function pinAccountSuccess(relationship) { export function pinAccountSuccess(relationship) {
return { return {
type: ACCOUNT_PIN_SUCCESS, type: ACCOUNT_PIN_SUCCESS,
relationship, relationship,
}; };
}; }
export function pinAccountFail(error) { export function pinAccountFail(error) {
return { return {
type: ACCOUNT_PIN_FAIL, type: ACCOUNT_PIN_FAIL,
error, error,
}; };
}; }
export function unpinAccountRequest(id) { export function unpinAccountRequest(id) {
return { return {
type: ACCOUNT_UNPIN_REQUEST, type: ACCOUNT_UNPIN_REQUEST,
id, id,
}; };
}; }
export function unpinAccountSuccess(relationship) { export function unpinAccountSuccess(relationship) {
return { return {
type: ACCOUNT_UNPIN_SUCCESS, type: ACCOUNT_UNPIN_SUCCESS,
relationship, relationship,
}; };
}; }
export function unpinAccountFail(error) { export function unpinAccountFail(error) {
return { return {
type: ACCOUNT_UNPIN_FAIL, type: ACCOUNT_UNPIN_FAIL,
error, error,
}; };
}; }

View File

@ -17,13 +17,13 @@ export function dismissAlert(alert) {
type: ALERT_DISMISS, type: ALERT_DISMISS,
alert, alert,
}; };
}; }
export function clearAlert() { export function clearAlert() {
return { return {
type: ALERT_CLEAR, type: ALERT_CLEAR,
}; };
}; }
export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage, message_values = undefined) { export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage, message_values = undefined) {
return { return {
@ -32,7 +32,7 @@ export function showAlert(title = messages.unexpectedTitle, message = messages.u
message, message,
message_values, message_values,
}; };
}; }
export function showAlertForError(error, skipNotFound = false) { export function showAlertForError(error, skipNotFound = false) {
if (error.response) { if (error.response) {

View File

@ -3,21 +3,21 @@ import { normalizeAnnouncement } from './importer/normalizer';
export const ANNOUNCEMENTS_FETCH_REQUEST = 'ANNOUNCEMENTS_FETCH_REQUEST'; export const ANNOUNCEMENTS_FETCH_REQUEST = 'ANNOUNCEMENTS_FETCH_REQUEST';
export const ANNOUNCEMENTS_FETCH_SUCCESS = 'ANNOUNCEMENTS_FETCH_SUCCESS'; export const ANNOUNCEMENTS_FETCH_SUCCESS = 'ANNOUNCEMENTS_FETCH_SUCCESS';
export const ANNOUNCEMENTS_FETCH_FAIL = 'ANNOUNCEMENTS_FETCH_FAIL'; export const ANNOUNCEMENTS_FETCH_FAIL = 'ANNOUNCEMENTS_FETCH_FAIL';
export const ANNOUNCEMENTS_UPDATE = 'ANNOUNCEMENTS_UPDATE'; export const ANNOUNCEMENTS_UPDATE = 'ANNOUNCEMENTS_UPDATE';
export const ANNOUNCEMENTS_DELETE = 'ANNOUNCEMENTS_DELETE'; export const ANNOUNCEMENTS_DELETE = 'ANNOUNCEMENTS_DELETE';
export const ANNOUNCEMENTS_DISMISS_REQUEST = 'ANNOUNCEMENTS_DISMISS_REQUEST'; export const ANNOUNCEMENTS_DISMISS_REQUEST = 'ANNOUNCEMENTS_DISMISS_REQUEST';
export const ANNOUNCEMENTS_DISMISS_SUCCESS = 'ANNOUNCEMENTS_DISMISS_SUCCESS'; export const ANNOUNCEMENTS_DISMISS_SUCCESS = 'ANNOUNCEMENTS_DISMISS_SUCCESS';
export const ANNOUNCEMENTS_DISMISS_FAIL = 'ANNOUNCEMENTS_DISMISS_FAIL'; export const ANNOUNCEMENTS_DISMISS_FAIL = 'ANNOUNCEMENTS_DISMISS_FAIL';
export const ANNOUNCEMENTS_REACTION_ADD_REQUEST = 'ANNOUNCEMENTS_REACTION_ADD_REQUEST'; export const ANNOUNCEMENTS_REACTION_ADD_REQUEST = 'ANNOUNCEMENTS_REACTION_ADD_REQUEST';
export const ANNOUNCEMENTS_REACTION_ADD_SUCCESS = 'ANNOUNCEMENTS_REACTION_ADD_SUCCESS'; export const ANNOUNCEMENTS_REACTION_ADD_SUCCESS = 'ANNOUNCEMENTS_REACTION_ADD_SUCCESS';
export const ANNOUNCEMENTS_REACTION_ADD_FAIL = 'ANNOUNCEMENTS_REACTION_ADD_FAIL'; export const ANNOUNCEMENTS_REACTION_ADD_FAIL = 'ANNOUNCEMENTS_REACTION_ADD_FAIL';
export const ANNOUNCEMENTS_REACTION_REMOVE_REQUEST = 'ANNOUNCEMENTS_REACTION_REMOVE_REQUEST'; export const ANNOUNCEMENTS_REACTION_REMOVE_REQUEST = 'ANNOUNCEMENTS_REACTION_REMOVE_REQUEST';
export const ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS = 'ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS'; export const ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS = 'ANNOUNCEMENTS_REACTION_REMOVE_SUCCESS';
export const ANNOUNCEMENTS_REACTION_REMOVE_FAIL = 'ANNOUNCEMENTS_REACTION_REMOVE_FAIL'; export const ANNOUNCEMENTS_REACTION_REMOVE_FAIL = 'ANNOUNCEMENTS_REACTION_REMOVE_FAIL';
export const ANNOUNCEMENTS_REACTION_UPDATE = 'ANNOUNCEMENTS_REACTION_UPDATE'; export const ANNOUNCEMENTS_REACTION_UPDATE = 'ANNOUNCEMENTS_REACTION_UPDATE';
@ -56,7 +56,7 @@ export const fetchAnnouncementsFail= error => ({
}); });
export const updateAnnouncements = announcement => ({ export const updateAnnouncements = announcement => ({
type: ANNOUNCEMENTS_UPDATE, type : ANNOUNCEMENTS_UPDATE,
announcement: normalizeAnnouncement(announcement), announcement: normalizeAnnouncement(announcement),
}); });
@ -72,17 +72,17 @@ export const dismissAnnouncement = announcementId => (dispatch, getState) => {
export const dismissAnnouncementRequest = announcementId => ({ export const dismissAnnouncementRequest = announcementId => ({
type: ANNOUNCEMENTS_DISMISS_REQUEST, type: ANNOUNCEMENTS_DISMISS_REQUEST,
id: announcementId, id : announcementId,
}); });
export const dismissAnnouncementSuccess = announcementId => ({ export const dismissAnnouncementSuccess = announcementId => ({
type: ANNOUNCEMENTS_DISMISS_SUCCESS, type: ANNOUNCEMENTS_DISMISS_SUCCESS,
id: announcementId, id : announcementId,
}); });
export const dismissAnnouncementFail = (announcementId, error) => ({ export const dismissAnnouncementFail = (announcementId, error) => ({
type: ANNOUNCEMENTS_DISMISS_FAIL, type: ANNOUNCEMENTS_DISMISS_FAIL,
id: announcementId, id : announcementId,
error, error,
}); });

View File

@ -7,8 +7,7 @@ import { useEmoji } from './emojis';
import resizeImage from '../utils/resize_image'; import resizeImage from '../utils/resize_image';
import { importFetchedAccounts } from './importer'; import { importFetchedAccounts } from './importer';
import { updateTimeline } from './timelines'; import { updateTimeline } from './timelines';
import { showAlertForError } from './alerts'; import { showAlert, showAlertForError } from './alerts';
import { showAlert } from './alerts';
import { defineMessages } from 'react-intl'; import { defineMessages } from 'react-intl';
let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags; let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags;
@ -81,7 +80,7 @@ export function changeCompose(text) {
type: COMPOSE_CHANGE, type: COMPOSE_CHANGE,
text: text, text: text,
}; };
}; }
export function replyCompose(status, routerHistory) { export function replyCompose(status, routerHistory) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -92,19 +91,19 @@ export function replyCompose(status, routerHistory) {
ensureComposeIsVisible(getState, routerHistory); ensureComposeIsVisible(getState, routerHistory);
}; };
}; }
export function cancelReplyCompose() { export function cancelReplyCompose() {
return { return {
type: COMPOSE_REPLY_CANCEL, type: COMPOSE_REPLY_CANCEL,
}; };
}; }
export function resetCompose() { export function resetCompose() {
return { return {
type: COMPOSE_RESET, type: COMPOSE_RESET,
}; };
}; }
export function mentionCompose(account, routerHistory) { export function mentionCompose(account, routerHistory) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -115,7 +114,7 @@ export function mentionCompose(account, routerHistory) {
ensureComposeIsVisible(getState, routerHistory); ensureComposeIsVisible(getState, routerHistory);
}; };
}; }
export function directCompose(account, routerHistory) { export function directCompose(account, routerHistory) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -126,7 +125,7 @@ export function directCompose(account, routerHistory) {
ensureComposeIsVisible(getState, routerHistory); ensureComposeIsVisible(getState, routerHistory);
}; };
}; }
export function submitCompose(routerHistory) { export function submitCompose(routerHistory) {
return function (dispatch, getState) { return function (dispatch, getState) {
@ -184,27 +183,27 @@ export function submitCompose(routerHistory) {
dispatch(submitComposeFail(error)); dispatch(submitComposeFail(error));
}); });
}; };
}; }
export function submitComposeRequest() { export function submitComposeRequest() {
return { return {
type: COMPOSE_SUBMIT_REQUEST, type: COMPOSE_SUBMIT_REQUEST,
}; };
}; }
export function submitComposeSuccess(status) { export function submitComposeSuccess(status) {
return { return {
type: COMPOSE_SUBMIT_SUCCESS, type: COMPOSE_SUBMIT_SUCCESS,
status: status, status: status,
}; };
}; }
export function submitComposeFail(error) { export function submitComposeFail(error) {
return { return {
type: COMPOSE_SUBMIT_FAIL, type: COMPOSE_SUBMIT_FAIL,
error: error, error: error,
}; };
}; }
export function uploadCompose(files) { export function uploadCompose(files) {
return function (dispatch, getState) { return function (dispatch, getState) {
@ -236,7 +235,7 @@ export function uploadCompose(files) {
total += file.size - f.size; total += file.size - f.size;
return api(getState).post('/api/v2/media', data, { return api(getState).post('/api/v2/media', data, {
onUploadProgress: function({ loaded }){ onUploadProgress: function ({ loaded }) {
progress[i] = loaded; progress[i] = loaded;
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total)); dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
}, },
@ -261,9 +260,9 @@ export function uploadCompose(files) {
} }
}); });
}).catch(error => dispatch(uploadComposeFail(error))); }).catch(error => dispatch(uploadComposeFail(error)));
}; }
}; };
}; }
export const uploadThumbnail = (id, file) => (dispatch, getState) => { export const uploadThumbnail = (id, file) => (dispatch, getState) => {
dispatch(uploadThumbnailRequest()); dispatch(uploadThumbnailRequest());
@ -318,7 +317,7 @@ export function changeUploadCompose(id, params) {
dispatch(changeUploadComposeFail(id, error)); dispatch(changeUploadComposeFail(id, error));
}); });
}; };
}; }
export function changeUploadComposeRequest() { export function changeUploadComposeRequest() {
return { return {
@ -333,7 +332,7 @@ export function changeUploadComposeSuccess(media) {
media: media, media: media,
skipLoading: true, skipLoading: true,
}; };
}; }
export function changeUploadComposeFail(error) { export function changeUploadComposeFail(error) {
return { return {
@ -341,14 +340,14 @@ export function changeUploadComposeFail(error) {
error: error, error: error,
skipLoading: true, skipLoading: true,
}; };
}; }
export function uploadComposeRequest() { export function uploadComposeRequest() {
return { return {
type: COMPOSE_UPLOAD_REQUEST, type: COMPOSE_UPLOAD_REQUEST,
skipLoading: true, skipLoading: true,
}; };
}; }
export function uploadComposeProgress(loaded, total) { export function uploadComposeProgress(loaded, total) {
return { return {
@ -356,7 +355,7 @@ export function uploadComposeProgress(loaded, total) {
loaded: loaded, loaded: loaded,
total: total, total: total,
}; };
}; }
export function uploadComposeSuccess(media, file) { export function uploadComposeSuccess(media, file) {
return { return {
@ -365,7 +364,7 @@ export function uploadComposeSuccess(media, file) {
file: file, file: file,
skipLoading: true, skipLoading: true,
}; };
}; }
export function uploadComposeFail(error) { export function uploadComposeFail(error) {
return { return {
@ -373,14 +372,14 @@ export function uploadComposeFail(error) {
error: error, error: error,
skipLoading: true, skipLoading: true,
}; };
}; }
export function undoUploadCompose(media_id) { export function undoUploadCompose(media_id) {
return { return {
type: COMPOSE_UPLOAD_UNDO, type: COMPOSE_UPLOAD_UNDO,
media_id: media_id, media_id: media_id,
}; };
}; }
export function clearComposeSuggestions() { export function clearComposeSuggestions() {
if (cancelFetchComposeSuggestionsAccounts) { if (cancelFetchComposeSuggestionsAccounts) {
@ -389,7 +388,7 @@ export function clearComposeSuggestions() {
return { return {
type: COMPOSE_SUGGESTIONS_CLEAR, type: COMPOSE_SUGGESTIONS_CLEAR,
}; };
}; }
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => { const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
if (cancelFetchComposeSuggestionsAccounts) { if (cancelFetchComposeSuggestionsAccounts) {
@ -463,7 +462,7 @@ export function fetchComposeSuggestions(token) {
break; break;
} }
}; };
}; }
export function readyComposeSuggestionsEmojis(token, emojis) { export function readyComposeSuggestionsEmojis(token, emojis) {
return { return {
@ -471,7 +470,7 @@ export function readyComposeSuggestionsEmojis(token, emojis) {
token, token,
emojis, emojis,
}; };
}; }
export function readyComposeSuggestionsAccounts(token, accounts) { export function readyComposeSuggestionsAccounts(token, accounts) {
return { return {
@ -479,7 +478,7 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
token, token,
accounts, accounts,
}; };
}; }
export const readyComposeSuggestionsTags = (token, tags) => ({ export const readyComposeSuggestionsTags = (token, tags) => ({
type: COMPOSE_SUGGESTIONS_READY, type: COMPOSE_SUGGESTIONS_READY,
@ -512,7 +511,7 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
path, path,
}); });
}; };
}; }
export function updateSuggestionTags(token) { export function updateSuggestionTags(token) {
return { return {
@ -560,39 +559,39 @@ export function mountCompose() {
return { return {
type: COMPOSE_MOUNT, type: COMPOSE_MOUNT,
}; };
}; }
export function unmountCompose() { export function unmountCompose() {
return { return {
type: COMPOSE_UNMOUNT, type: COMPOSE_UNMOUNT,
}; };
}; }
export function changeComposeSensitivity() { export function changeComposeSensitivity() {
return { return {
type: COMPOSE_SENSITIVITY_CHANGE, type: COMPOSE_SENSITIVITY_CHANGE,
}; };
}; }
export function changeComposeSpoilerness() { export function changeComposeSpoilerness() {
return { return {
type: COMPOSE_SPOILERNESS_CHANGE, type: COMPOSE_SPOILERNESS_CHANGE,
}; };
}; }
export function changeComposeSpoilerText(text) { export function changeComposeSpoilerText(text) {
return { return {
type: COMPOSE_SPOILER_TEXT_CHANGE, type: COMPOSE_SPOILER_TEXT_CHANGE,
text, text,
}; };
}; }
export function changeComposeVisibility(value) { export function changeComposeVisibility(value) {
return { return {
type: COMPOSE_VISIBILITY_CHANGE, type: COMPOSE_VISIBILITY_CHANGE,
value, value,
}; };
}; }
export function insertEmojiCompose(position, emoji, needsSpace) { export function insertEmojiCompose(position, emoji, needsSpace) {
return { return {
@ -601,33 +600,33 @@ export function insertEmojiCompose(position, emoji, needsSpace) {
emoji, emoji,
needsSpace, needsSpace,
}; };
}; }
export function changeComposing(value) { export function changeComposing(value) {
return { return {
type: COMPOSE_COMPOSING_CHANGE, type: COMPOSE_COMPOSING_CHANGE,
value, value,
}; };
}; }
export function addPoll() { export function addPoll() {
return { return {
type: COMPOSE_POLL_ADD, type: COMPOSE_POLL_ADD,
}; };
}; }
export function removePoll() { export function removePoll() {
return { return {
type: COMPOSE_POLL_REMOVE, type: COMPOSE_POLL_REMOVE,
}; };
}; }
export function addPollOption(title) { export function addPollOption(title) {
return { return {
type: COMPOSE_POLL_OPTION_ADD, type: COMPOSE_POLL_OPTION_ADD,
title, title,
}; };
}; }
export function changePollOption(index, title) { export function changePollOption(index, title) {
return { return {
@ -635,14 +634,14 @@ export function changePollOption(index, title) {
index, index,
title, title,
}; };
}; }
export function removePollOption(index) { export function removePollOption(index) {
return { return {
type: COMPOSE_POLL_OPTION_REMOVE, type: COMPOSE_POLL_OPTION_REMOVE,
index, index,
}; };
}; }
export function changePollSettings(expiresIn, isMultiple) { export function changePollSettings(expiresIn, isMultiple) {
return { return {
@ -650,4 +649,4 @@ export function changePollSettings(expiresIn, isMultiple) {
expiresIn, expiresIn,
isMultiple, isMultiple,
}; };
}; }

View File

@ -1,23 +1,19 @@
import api, { getLinks } from '../api'; import api, { getLinks } from '../api';
import { import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer';
importFetchedAccounts,
importFetchedStatuses,
importFetchedStatus,
} from './importer';
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT'; export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT'; export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST'; export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST';
export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS'; export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL'; export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL';
export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE'; export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE';
export const CONVERSATIONS_READ = 'CONVERSATIONS_READ'; export const CONVERSATIONS_READ = 'CONVERSATIONS_READ';
export const CONVERSATIONS_DELETE_REQUEST = 'CONVERSATIONS_DELETE_REQUEST'; export const CONVERSATIONS_DELETE_REQUEST = 'CONVERSATIONS_DELETE_REQUEST';
export const CONVERSATIONS_DELETE_SUCCESS = 'CONVERSATIONS_DELETE_SUCCESS'; export const CONVERSATIONS_DELETE_SUCCESS = 'CONVERSATIONS_DELETE_SUCCESS';
export const CONVERSATIONS_DELETE_FAIL = 'CONVERSATIONS_DELETE_FAIL'; export const CONVERSATIONS_DELETE_FAIL = 'CONVERSATIONS_DELETE_FAIL';
export const mountConversations = () => ({ export const mountConversations = () => ({
type: CONVERSATIONS_MOUNT, type: CONVERSATIONS_MOUNT,
@ -30,7 +26,7 @@ export const unmountConversations = () => ({
export const markConversationRead = conversationId => (dispatch, getState) => { export const markConversationRead = conversationId => (dispatch, getState) => {
dispatch({ dispatch({
type: CONVERSATIONS_READ, type: CONVERSATIONS_READ,
id: conversationId, id : conversationId,
}); });
api(getState).post(`/api/v1/conversations/${conversationId}/read`); api(getState).post(`/api/v1/conversations/${conversationId}/read`);

View File

@ -24,7 +24,7 @@ export const fetchAccountIdentityProofsSuccess = (accountId, identity_proofs) =>
}); });
export const fetchAccountIdentityProofsFail = (accountId, err) => ({ export const fetchAccountIdentityProofsFail = (accountId, err) => ({
type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL, type : IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
accountId, accountId,
err, err,
skipNotFound: true, skipNotFound: true,

View File

@ -3,33 +3,34 @@ import openDB from '../storage/db';
import { evictStatus } from '../storage/modifier'; import { evictStatus } from '../storage/modifier';
import { deleteFromTimelines } from './timelines'; import { deleteFromTimelines } from './timelines';
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer'; import { importAccount, importFetchedStatus, importFetchedStatuses, importStatus } from './importer';
import { ensureComposeIsVisible } from './compose'; import { ensureComposeIsVisible } from './compose';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'; export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS'; export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
export const STATUS_FETCH_FAIL = 'STATUS_FETCH_FAIL'; export const STATUS_FETCH_FAIL = 'STATUS_FETCH_FAIL';
export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST'; export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST';
export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS'; export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
export const STATUS_DELETE_FAIL = 'STATUS_DELETE_FAIL'; export const STATUS_DELETE_FAIL = 'STATUS_DELETE_FAIL';
export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST'; export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS'; export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL'; export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL';
export const STATUS_MUTE_REQUEST = 'STATUS_MUTE_REQUEST'; export const STATUS_MUTE_REQUEST = 'STATUS_MUTE_REQUEST';
export const STATUS_MUTE_SUCCESS = 'STATUS_MUTE_SUCCESS'; export const STATUS_MUTE_SUCCESS = 'STATUS_MUTE_SUCCESS';
export const STATUS_MUTE_FAIL = 'STATUS_MUTE_FAIL'; export const STATUS_MUTE_FAIL = 'STATUS_MUTE_FAIL';
export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST'; export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS'; export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL'; export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL';
export const STATUS_REVEAL = 'STATUS_REVEAL'; export const STATUS_REVEAL = 'STATUS_REVEAL';
export const STATUS_HIDE = 'STATUS_HIDE';
export const STATUS_COLLAPSE = 'STATUS_COLLAPSE'; export const STATUS_COLLAPSE = 'STATUS_COLLAPSE';
export const STATUS_HIDE = 'STATUS_HIDE';
export const REDRAFT = 'REDRAFT'; export const REDRAFT = 'REDRAFT';
export function fetchStatusRequest(id, skipLoading) { export function fetchStatusRequest(id, skipLoading) {
@ -38,7 +39,7 @@ export function fetchStatusRequest(id, skipLoading) {
id, id,
skipLoading, skipLoading,
}; };
}; }
function getFromDB(dispatch, getState, accountIndex, index, id) { function getFromDB(dispatch, getState, accountIndex, index, id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -114,24 +115,24 @@ export function fetchStatus(id) {
dispatch(fetchStatusFail(id, error, skipLoading)); dispatch(fetchStatusFail(id, error, skipLoading));
}); });
}; };
}; }
export function fetchStatusSuccess(skipLoading) { export function fetchStatusSuccess(skipLoading) {
return { return {
type: STATUS_FETCH_SUCCESS, type: STATUS_FETCH_SUCCESS,
skipLoading, skipLoading,
}; };
}; }
export function fetchStatusFail(id, error, skipLoading) { export function fetchStatusFail(id, error, skipLoading) {
return { return {
type: STATUS_FETCH_FAIL, type : STATUS_FETCH_FAIL,
id, id,
error, error,
skipLoading, skipLoading,
skipAlert: true, skipAlert: true,
}; };
}; }
export function redraft(status, raw_text) { export function redraft(status, raw_text) {
return { return {
@ -139,7 +140,7 @@ export function redraft(status, raw_text) {
status, status,
raw_text, raw_text,
}; };
}; }
export function deleteStatus(id, routerHistory, withRedraft = false) { export function deleteStatus(id, routerHistory, withRedraft = false) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -164,29 +165,29 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
dispatch(deleteStatusFail(id, error)); dispatch(deleteStatusFail(id, error));
}); });
}; };
}; }
export function deleteStatusRequest(id) { export function deleteStatusRequest(id) {
return { return {
type: STATUS_DELETE_REQUEST, type: STATUS_DELETE_REQUEST,
id: id, id : id,
}; };
}; }
export function deleteStatusSuccess(id) { export function deleteStatusSuccess(id) {
return { return {
type: STATUS_DELETE_SUCCESS, type: STATUS_DELETE_SUCCESS,
id: id, id : id,
}; };
}; }
export function deleteStatusFail(id, error) { export function deleteStatusFail(id, error) {
return { return {
type: STATUS_DELETE_FAIL, type : STATUS_DELETE_FAIL,
id: id, id : id,
error: error, error: error,
}; };
}; }
export function fetchContext(id) { export function fetchContext(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -204,33 +205,33 @@ export function fetchContext(id) {
dispatch(fetchContextFail(id, error)); dispatch(fetchContextFail(id, error));
}); });
}; };
}; }
export function fetchContextRequest(id) { export function fetchContextRequest(id) {
return { return {
type: CONTEXT_FETCH_REQUEST, type: CONTEXT_FETCH_REQUEST,
id, id,
}; };
}; }
export function fetchContextSuccess(id, ancestors, descendants) { export function fetchContextSuccess(id, ancestors, descendants) {
return { return {
type: CONTEXT_FETCH_SUCCESS, type : CONTEXT_FETCH_SUCCESS,
id, id,
ancestors, ancestors,
descendants, descendants,
statuses: ancestors.concat(descendants), statuses: ancestors.concat(descendants),
}; };
}; }
export function fetchContextFail(id, error) { export function fetchContextFail(id, error) {
return { return {
type: CONTEXT_FETCH_FAIL, type : CONTEXT_FETCH_FAIL,
id, id,
error, error,
skipAlert: true, skipAlert: true,
}; };
}; }
export function muteStatus(id) { export function muteStatus(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -242,21 +243,21 @@ export function muteStatus(id) {
dispatch(muteStatusFail(id, error)); dispatch(muteStatusFail(id, error));
}); });
}; };
}; }
export function muteStatusRequest(id) { export function muteStatusRequest(id) {
return { return {
type: STATUS_MUTE_REQUEST, type: STATUS_MUTE_REQUEST,
id, id,
}; };
}; }
export function muteStatusSuccess(id) { export function muteStatusSuccess(id) {
return { return {
type: STATUS_MUTE_SUCCESS, type: STATUS_MUTE_SUCCESS,
id, id,
}; };
}; }
export function muteStatusFail(id, error) { export function muteStatusFail(id, error) {
return { return {
@ -264,7 +265,7 @@ export function muteStatusFail(id, error) {
id, id,
error, error,
}; };
}; }
export function unmuteStatus(id) { export function unmuteStatus(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -276,21 +277,21 @@ export function unmuteStatus(id) {
dispatch(unmuteStatusFail(id, error)); dispatch(unmuteStatusFail(id, error));
}); });
}; };
}; }
export function unmuteStatusRequest(id) { export function unmuteStatusRequest(id) {
return { return {
type: STATUS_UNMUTE_REQUEST, type: STATUS_UNMUTE_REQUEST,
id, id,
}; };
}; }
export function unmuteStatusSuccess(id) { export function unmuteStatusSuccess(id) {
return { return {
type: STATUS_UNMUTE_SUCCESS, type: STATUS_UNMUTE_SUCCESS,
id, id,
}; };
}; }
export function unmuteStatusFail(id, error) { export function unmuteStatusFail(id, error) {
return { return {
@ -298,7 +299,7 @@ export function unmuteStatusFail(id, error) {
id, id,
error, error,
}; };
}; }
export function hideStatus(ids) { export function hideStatus(ids) {
if (!Array.isArray(ids)) { if (!Array.isArray(ids)) {
@ -309,7 +310,7 @@ export function hideStatus(ids) {
type: STATUS_HIDE, type: STATUS_HIDE,
ids, ids,
}; };
}; }
export function revealStatus(ids) { export function revealStatus(ids) {
if (!Array.isArray(ids)) { if (!Array.isArray(ids)) {
@ -320,7 +321,7 @@ export function revealStatus(ids) {
type: STATUS_REVEAL, type: STATUS_REVEAL,
ids, ids,
}; };
}; }
export function toggleStatusCollapse(id, isCollapsed) { export function toggleStatusCollapse(id, isCollapsed) {
return { return {

View File

@ -1,7 +1,7 @@
import { importFetchedStatus, importFetchedStatuses } from './importer'; import { importFetchedStatus, importFetchedStatuses } from './importer';
import { submitMarkers } from './markers'; import { submitMarkers } from './markers';
import api, { getLinks } from 'mastodon/api'; import api, { getLinks } from 'mastodon/api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import compareId from 'mastodon/compare_id'; import compareId from 'mastodon/compare_id';
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state'; import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
@ -42,7 +42,7 @@ export function updateTimeline(timeline, status, accept) {
dispatch(submitMarkers()); dispatch(submitMarkers());
} }
}; };
}; }
export function deleteFromTimelines(id) { export function deleteFromTimelines(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -58,13 +58,13 @@ export function deleteFromTimelines(id) {
reblogOf, reblogOf,
}); });
}; };
}; }
export function clearTimeline(timeline) { export function clearTimeline(timeline) {
return (dispatch) => { return (dispatch) => {
dispatch({ type: TIMELINE_CLEAR, timeline }); dispatch({ type: TIMELINE_CLEAR, timeline });
}; };
}; }
const noOp = () => {}; const noOp = () => {};
@ -113,7 +113,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
done(); done();
}); });
}; };
}; }
export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done); export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia }, done); export const expandPublicTimeline = ({ maxId, onlyMedia, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia }, done);
@ -138,7 +138,7 @@ export function expandTimelineRequest(timeline, isLoadingMore) {
timeline, timeline,
skipLoading: !isLoadingMore, skipLoading: !isLoadingMore,
}; };
}; }
export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore, usePendingItems) { export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore, usePendingItems) {
return { return {
@ -151,17 +151,17 @@ export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadi
usePendingItems, usePendingItems,
skipLoading: !isLoadingMore, skipLoading: !isLoadingMore,
}; };
}; }
export function expandTimelineFail(timeline, error, isLoadingMore) { export function expandTimelineFail(timeline, error, isLoadingMore) {
return { return {
type: TIMELINE_EXPAND_FAIL, type : TIMELINE_EXPAND_FAIL,
timeline, timeline,
error, error,
skipLoading: !isLoadingMore, skipLoading : !isLoadingMore,
skipNotFound: timeline.startsWith('account:'), skipNotFound: timeline.startsWith('account:'),
}; };
}; }
export function scrollTopTimeline(timeline, top) { export function scrollTopTimeline(timeline, top) {
return { return {
@ -169,14 +169,14 @@ export function scrollTopTimeline(timeline, top) {
timeline, timeline,
top, top,
}; };
}; }
export function connectTimeline(timeline) { export function connectTimeline(timeline) {
return { return {
type: TIMELINE_CONNECT, type: TIMELINE_CONNECT,
timeline, timeline,
}; };
}; }
export const disconnectTimeline = timeline => ({ export const disconnectTimeline = timeline => ({
type: TIMELINE_DISCONNECT, type: TIMELINE_DISCONNECT,

View File

@ -9,4 +9,4 @@ export function start() {
} catch (e) { } catch (e) {
// If called twice // If called twice
} }
}; }

View File

@ -12,7 +12,7 @@ import classNames from 'classnames';
const textAtCursorMatchesToken = (str, caretPosition) => { const textAtCursorMatchesToken = (str, caretPosition) => {
let word; let word;
let left = str.slice(0, caretPosition).search(/\S+$/); let left = str.slice(0, caretPosition).search(/\S+$/);
let right = str.slice(caretPosition).search(/\s/); let right = str.slice(caretPosition).search(/\s/);
if (right < 0) { if (right < 0) {
@ -37,34 +37,37 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
export default class AutosuggestTextarea extends ImmutablePureComponent { export default class AutosuggestTextarea extends ImmutablePureComponent {
static propTypes = { static propTypes = {
value: PropTypes.string, value : PropTypes.string,
suggestions: ImmutablePropTypes.list, suggestions : ImmutablePropTypes.list,
disabled: PropTypes.bool, disabled : PropTypes.bool,
placeholder: PropTypes.string, placeholder : PropTypes.string,
onSuggestionSelected: PropTypes.func.isRequired, onSuggestionSelected : PropTypes.func.isRequired,
onSuggestionsClearRequested: PropTypes.func.isRequired, onSuggestionsClearRequested: PropTypes.func.isRequired,
onSuggestionsFetchRequested: PropTypes.func.isRequired, onSuggestionsFetchRequested: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired, onChange : PropTypes.func.isRequired,
onKeyUp: PropTypes.func, onKeyUp : PropTypes.func,
onKeyDown: PropTypes.func, onKeyDown : PropTypes.func,
onPaste: PropTypes.func.isRequired, onPaste : PropTypes.func.isRequired,
autoFocus: PropTypes.bool, autoFocus : PropTypes.bool,
directMessage : PropTypes.bool,
directMessageRecipient : PropTypes.string,
}; };
static defaultProps = { static defaultProps = {
autoFocus: true, autoFocus : true,
directMessage: false,
}; };
state = { state = {
suggestionsHidden: true, suggestionsHidden : true,
focused: false, focused : false,
selectedSuggestion: 0, selectedSuggestion: 0,
lastToken: null, lastToken : null,
tokenStart: 0, tokenStart : 0,
}; };
onChange = (e) => { 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) { if (token !== null && this.state.lastToken !== token) {
this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart }); this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
@ -75,7 +78,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
} }
this.props.onChange(e); this.props.onChange(e);
} };
onKeyDown = (e) => { onKeyDown = (e) => {
const { suggestions, disabled } = this.props; const { suggestions, disabled } = this.props;
@ -92,7 +95,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
return; return;
} }
switch(e.key) { switch (e.key) {
case 'Escape': case 'Escape':
if (suggestions.size === 0 || suggestionsHidden) { if (suggestions.size === 0 || suggestionsHidden) {
document.querySelector('.ui').parentElement.focus(); document.querySelector('.ui').parentElement.focus();
@ -124,7 +127,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
e.stopPropagation(); e.stopPropagation();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion)); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
} }
break; break;
} }
@ -133,27 +135,27 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
} }
this.props.onKeyDown(e); this.props.onKeyDown(e);
} };
onBlur = () => { onBlur = () => {
this.setState({ suggestionsHidden: true, focused: false }); this.setState({ suggestionsHidden: true, focused: false });
} };
onFocus = (e) => { onFocus = (e) => {
this.setState({ focused: true }); this.setState({ focused: true });
if (this.props.onFocus) { if (this.props.onFocus) {
this.props.onFocus(e); this.props.onFocus(e);
} }
} };
onSuggestionClick = (e) => { onSuggestionClick = (e) => {
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index')); const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
e.preventDefault(); e.preventDefault();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
this.textarea.focus(); this.textarea.focus();
} };
componentWillReceiveProps (nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) { if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
this.setState({ suggestionsHidden: false }); this.setState({ suggestionsHidden: false });
} }
@ -161,14 +163,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
setTextarea = (c) => { setTextarea = (c) => {
this.textarea = c; this.textarea = c;
} };
onPaste = (e) => { onPaste = (e) => {
if (e.clipboardData && e.clipboardData.files.length === 1) { if (e.clipboardData && e.clipboardData.files.length === 1) {
this.props.onPaste(e.clipboardData.files); this.props.onPaste(e.clipboardData.files);
e.preventDefault(); e.preventDefault();
} }
} };
renderSuggestion = (suggestion, i) => { renderSuggestion = (suggestion, i) => {
const { selectedSuggestion } = this.state; const { selectedSuggestion } = this.state;
@ -176,23 +178,30 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
if (suggestion.type === 'emoji') { if (suggestion.type === 'emoji') {
inner = <AutosuggestEmoji emoji={suggestion} />; inner = <AutosuggestEmoji emoji={suggestion} />;
key = suggestion.id; key = suggestion.id;
} else if (suggestion.type === 'hashtag') { } else if (suggestion.type === 'hashtag') {
inner = <AutosuggestHashtag tag={suggestion} />; inner = <AutosuggestHashtag tag={suggestion} />;
key = suggestion.name; key = suggestion.name;
} else if (suggestion.type === 'account') { } else if (suggestion.type === 'account') {
inner = <AutosuggestAccountContainer id={suggestion.id} />; inner = <AutosuggestAccountContainer id={suggestion.id} />;
key = suggestion.id; key = suggestion.id;
} }
return ( 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} {inner}
</div> </div >
); );
} };
render () { render() {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props; const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
const { suggestionsHidden } = this.state; const { suggestionsHidden } = this.state;
const style = { direction: 'ltr' }; const style = { direction: 'ltr' };
@ -202,10 +211,13 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
} }
return [ return [
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'> <div
className='compose-form__autosuggest-wrapper'
key='autosuggest-wrapper'
>
<div className='autosuggest-textarea'> <div className='autosuggest-textarea'>
<label> <label >
<span style={{ display: 'none' }}>{placeholder}</span> <span style={{ display: 'none' }}>{placeholder}</span >
<Textarea <Textarea
ref={this.setTextarea} ref={this.setTextarea}
@ -223,16 +235,21 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
style={style} style={style}
aria-autocomplete='list' aria-autocomplete='list'
/> />
</label> </label >
</div> </div >
{children} {children}
</div>, </div >,
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'> <div
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}> className='autosuggest-textarea__suggestions-wrapper'
key='suggestions-wrapper'
>
<div
className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}
>
{suggestions.map(this.renderSuggestion)} {suggestions.map(this.renderSuggestion)}
</div> </div >
</div>, </div >,
]; ];
} }

View File

@ -2,13 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import classNames from 'classnames'; import classNames from 'classnames';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
const messages = defineMessages({ const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, show : { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' }, hide : { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' }, moveLeft : { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' }, moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
}); });
@ -20,20 +20,19 @@ class ColumnHeader extends React.PureComponent {
}; };
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired, intl : PropTypes.object.isRequired,
title: PropTypes.node, title : PropTypes.node,
icon: PropTypes.string, icon : PropTypes.string,
active: PropTypes.bool, active : PropTypes.bool,
multiColumn: PropTypes.bool, multiColumn : PropTypes.bool,
extraButton: PropTypes.node, extraButton : PropTypes.node,
showBackButton: PropTypes.bool, showBackButton: PropTypes.bool,
children: PropTypes.node, children : PropTypes.node,
pinned: PropTypes.bool, pinned : PropTypes.bool,
placeholder: PropTypes.bool, placeholder : PropTypes.bool,
onPin: PropTypes.func, onPin : PropTypes.func,
onMove: PropTypes.func, onMove : PropTypes.func,
onClick: PropTypes.func, onClick : PropTypes.func,
appendContent: PropTypes.node,
}; };
state = { state = {
@ -47,32 +46,32 @@ class ColumnHeader extends React.PureComponent {
} else { } else {
this.context.router.history.goBack(); this.context.router.history.goBack();
} }
} };
handleToggleClick = (e) => { handleToggleClick = (e) => {
e.stopPropagation(); e.stopPropagation();
this.setState({ collapsed: !this.state.collapsed, animating: true }); this.setState({ collapsed: !this.state.collapsed, animating: true });
} };
handleTitleClick = () => { handleTitleClick = () => {
this.props.onClick(); this.props.onClick();
} };
handleMoveLeft = () => { handleMoveLeft = () => {
this.props.onMove(-1); this.props.onMove(-1);
} };
handleMoveRight = () => { handleMoveRight = () => {
this.props.onMove(1); this.props.onMove(1);
} };
handleBackClick = () => { handleBackClick = () => {
this.historyBack(); this.historyBack();
} };
handleTransitionEnd = () => { handleTransitionEnd = () => {
this.setState({ animating: false }); this.setState({ animating: false });
} };
handlePin = () => { handlePin = () => {
if (!this.props.pinned) { if (!this.props.pinned) {
@ -80,10 +79,10 @@ class ColumnHeader extends React.PureComponent {
} }
this.props.onPin(); this.props.onPin();
} };
render () { render() {
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent } = this.props; const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props;
const { collapsed, animating } = this.state; const { collapsed, animating } = this.state;
const wrapperClassName = classNames('column-header__wrapper', { const wrapperClassName = classNames('column-header__wrapper', {
@ -107,31 +106,82 @@ class ColumnHeader extends React.PureComponent {
if (children) { if (children) {
extraContent = ( extraContent = (
<div key='extra-content' className='column-header__collapsible__extra'> <div
key='extra-content'
className='column-header__collapsible__extra'
>
{children} {children}
</div> </div >
); );
} }
if (multiColumn && pinned) { 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 = ( moveButtons = (
<div key='move-buttons' className='column-header__setting-arrows'> <div
<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> key='move-buttons'
<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> className='column-header__setting-arrows'
</div> >
<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) { } 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)) { if (!pinned && (multiColumn || showBackButton)) {
backButton = ( backButton = (
<button onClick={this.handleBackClick} className='column-header__back-button'> <button
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> onClick={this.handleBackClick}
<FormattedMessage id='column_back_button.label' defaultMessage='Back' /> className='column-header__back-button'
</button> >
<Icon
id='chevron-left'
className='column-back-button__icon'
fixedWidth
/>
<FormattedMessage
id='column_back_button.label'
defaultMessage='Back'
/>
</button >
); );
} }
@ -145,7 +195,16 @@ class ColumnHeader extends React.PureComponent {
} }
if (children || (multiColumn && this.props.onPin)) { 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; const hasTitle = icon && title;
@ -155,9 +214,13 @@ class ColumnHeader extends React.PureComponent {
<h1 className={buttonClassName}> <h1 className={buttonClassName}>
{hasTitle && ( {hasTitle && (
<button onClick={this.handleTitleClick}> <button onClick={this.handleTitleClick}>
<Icon id={icon} fixedWidth className='column-header__icon' /> <Icon
id={icon}
fixedWidth
className='column-header__icon'
/>
{title} {title}
</button> </button >
)} )}
{!hasTitle && backButton} {!hasTitle && backButton}
@ -166,17 +229,19 @@ class ColumnHeader extends React.PureComponent {
{hasTitle && backButton} {hasTitle && backButton}
{extraButton} {extraButton}
{collapseButton} {collapseButton}
</div> </div >
</h1> </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'> <div className='column-header__collapsible-inner'>
{(!collapsed || animating) && collapsedContent} {(!collapsed || animating) && collapsedContent}
</div> </div >
</div> </div >
</div >
{appendContent}
</div>
); );
if (multiColumn || placeholder) { if (multiColumn || placeholder) {

View File

@ -6,12 +6,12 @@ import { autoPlayGif } from 'mastodon/initial_state';
export default class DisplayName extends React.PureComponent { export default class DisplayName extends React.PureComponent {
static propTypes = { static propTypes = {
account: ImmutablePropTypes.map.isRequired, account : ImmutablePropTypes.map.isRequired,
others: ImmutablePropTypes.list, others : ImmutablePropTypes.list,
localDomain: PropTypes.string, localDomain: PropTypes.string,
}; };
_updateEmojis () { _updateEmojis() {
const node = this.node; const node = this.node;
if (!node || autoPlayGif) { if (!node || autoPlayGif) {
@ -32,33 +32,40 @@ export default class DisplayName extends React.PureComponent {
} }
} }
componentDidMount () { componentDidMount() {
this._updateEmojis(); this._updateEmojis();
} }
componentDidUpdate () { componentDidUpdate() {
this._updateEmojis(); this._updateEmojis();
} }
handleEmojiMouseEnter = ({ target }) => { handleEmojiMouseEnter = ({ target }) => {
target.src = target.getAttribute('data-original'); target.src = target.getAttribute('data-original');
} };
handleEmojiMouseLeave = ({ target }) => { handleEmojiMouseLeave = ({ target }) => {
target.src = target.getAttribute('data-static'); target.src = target.getAttribute('data-static');
} };
setRef = (c) => { setRef = (c) => {
this.node = c; this.node = c;
} };
render () { render() {
const { others, localDomain } = this.props; const { others, localDomain } = this.props;
let displayName, suffix, account; let displayName, suffix, account;
if (others && others.size > 1) { 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) { if (others.size - 2 > 0) {
suffix = `+${others.size - 2}`; suffix = `+${others.size - 2}`;
@ -76,14 +83,22 @@ export default class DisplayName extends React.PureComponent {
acct = `${acct}@${localDomain}`; acct = `${acct}@${localDomain}`;
} }
displayName = <bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>; displayName = (<bdi ><strong
suffix = <span className='display-name__account'>@{acct}</span>; className='display-name__html'
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
/>
</bdi >);
suffix = <span className='display-name__account'>@{acct}</span >;
} }
return ( return (
<span className='display-name' ref={this.setRef}> <span
className='display-name'
ref={this.setRef}
>
{displayName} {suffix} {displayName} {suffix}
</span> </span >
); );
} }

View File

@ -19,7 +19,7 @@ class Account extends ImmutablePureComponent {
handleDomainUnblock = () => { handleDomainUnblock = () => {
this.props.onUnblockDomain(this.props.domain); this.props.onUnblockDomain(this.props.domain);
} };
render () { render () {
const { domain, intl } = this.props; const { domain, intl } = this.props;

View File

@ -0,0 +1,133 @@
import { debounce } from 'lodash';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import StatusContainer from '../containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component';
import LoadGap from './load_gap';
import ScrollableList from './scrollable_list';
import RegenerationIndicator from 'mastodon/components/regeneration_indicator';
export default class Gallery extends ImmutablePureComponent {
static propTypes = {
scrollKey : PropTypes.string.isRequired,
statusIds : ImmutablePropTypes.list.isRequired,
featuredStatusIds : ImmutablePropTypes.list,
onLoadMore : PropTypes.func,
onScrollToTop : PropTypes.func,
onScroll : PropTypes.func,
trackScroll : PropTypes.bool,
shouldUpdateScroll: PropTypes.func,
isLoading : PropTypes.bool,
isPartial : PropTypes.bool,
hasMore : PropTypes.bool,
prepend : PropTypes.node,
emptyMessage : PropTypes.node,
alwaysPrepend : PropTypes.bool,
timelineId : PropTypes.string,
};
static defaultProps = {
trackScroll: true,
};
getFeaturedStatusCount = () => {
return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
};
getCurrentStatusIndex = (id, featured) => {
if (featured) {
return this.props.featuredStatusIds.indexOf(id);
} else {
return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount();
}
};
handleMoveUp = (id, featured) => {
const elementIndex = this.getCurrentStatusIndex(id, featured) - 1;
this._selectChild(elementIndex, true);
};
handleMoveDown = (id, featured) => {
const elementIndex = this.getCurrentStatusIndex(id, featured) + 1;
this._selectChild(elementIndex, false);
};
handleLoadOlder = debounce(() => {
this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined);
}, 300, { leading: true });
_selectChild(index, align_top) {
const container = this.node.node;
const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
if (element) {
if (align_top && container.scrollTop > element.offsetTop) {
element.scrollIntoView(true);
} else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
element.scrollIntoView(false);
}
element.focus();
}
}
setRef = c => {
this.node = c;
};
render() {
const { statusIds, featuredStatusIds, shouldUpdateScroll, onLoadMore, timelineId, ...other } = this.props;
const { isLoading, isPartial } = other;
if (isPartial) {
return <RegenerationIndicator />;
}
let scrollableContent = (isLoading || statusIds.size > 0) ? (
statusIds.map((statusId, index) => statusId === null ? (
<LoadGap
key={'gap:' + statusIds.get(index + 1)}
disabled={isLoading}
maxId={index > 0 ? statusIds.get(index - 1) : null}
onClick={onLoadMore}
/>
) : (
<StatusContainer
key={statusId}
id={statusId}
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
contextType={timelineId}
showThread
/>
))
) : null;
if (scrollableContent && featuredStatusIds) {
scrollableContent = featuredStatusIds.map(statusId => (
<StatusContainer
key={`f-${statusId}`}
id={statusId}
featured
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
contextType={timelineId}
showThread
/>
)).concat(scrollableContent);
}
return (
<ScrollableList
{...other} showLoading={isLoading && statusIds.size === 0}
onLoadMore={onLoadMore && this.handleLoadOlder}
shouldUpdateScroll={shouldUpdateScroll}
ref={this.setRef}
>
{scrollableContent}
</ScrollableList >
);
}
}

View File

@ -6,41 +6,41 @@ import Icon from 'mastodon/components/icon';
export default class IconButton extends React.PureComponent { export default class IconButton extends React.PureComponent {
static propTypes = { static propTypes = {
className: PropTypes.string, className : PropTypes.string,
title: PropTypes.string.isRequired, title : PropTypes.string.isRequired,
icon: PropTypes.string.isRequired, icon : PropTypes.string.isRequired,
onClick: PropTypes.func, onClick : PropTypes.func,
onMouseDown: PropTypes.func, onMouseDown: PropTypes.func,
onKeyDown: PropTypes.func, onKeyDown : PropTypes.func,
onKeyPress: PropTypes.func, onKeyPress : PropTypes.func,
size: PropTypes.number, size : PropTypes.number,
active: PropTypes.bool, active : PropTypes.bool,
pressed: PropTypes.bool, pressed : PropTypes.bool,
expanded: PropTypes.bool, expanded : PropTypes.bool,
style: PropTypes.object, style : PropTypes.object,
activeStyle: PropTypes.object, activeStyle: PropTypes.object,
disabled: PropTypes.bool, disabled : PropTypes.bool,
inverted: PropTypes.bool, inverted : PropTypes.bool,
animate: PropTypes.bool, animate : PropTypes.bool,
overlay: PropTypes.bool, overlay : PropTypes.bool,
tabIndex: PropTypes.string, tabIndex : PropTypes.string,
}; };
static defaultProps = { static defaultProps = {
size: 18, size : 18,
active: false, active : false,
disabled: false, disabled: false,
animate: false, animate : false,
overlay: false, overlay : false,
tabIndex: '0', tabIndex: '0',
}; };
state = { state = {
activate: false, activate : false,
deactivate: false, deactivate: false,
} };
componentWillReceiveProps (nextProps) { componentWillReceiveProps(nextProps) {
if (!nextProps.animate) return; if (!nextProps.animate) return;
if (this.props.active && !nextProps.active) { if (this.props.active && !nextProps.active) {
@ -50,38 +50,38 @@ export default class IconButton extends React.PureComponent {
} }
} }
handleClick = (e) => { handleClick = (e) => {
e.preventDefault(); e.preventDefault();
if (!this.props.disabled) { if (!this.props.disabled) {
this.props.onClick(e); this.props.onClick(e);
} }
} };
handleKeyPress = (e) => { handleKeyPress = (e) => {
if (this.props.onKeyPress && !this.props.disabled) { if (this.props.onKeyPress && !this.props.disabled) {
this.props.onKeyPress(e); this.props.onKeyPress(e);
} }
} };
handleMouseDown = (e) => { handleMouseDown = (e) => {
if (!this.props.disabled && this.props.onMouseDown) { if (!this.props.disabled && this.props.onMouseDown) {
this.props.onMouseDown(e); this.props.onMouseDown(e);
} }
} };
handleKeyDown = (e) => { handleKeyDown = (e) => {
if (!this.props.disabled && this.props.onKeyDown) { if (!this.props.disabled && this.props.onKeyDown) {
this.props.onKeyDown(e); this.props.onKeyDown(e);
} }
} };
render () { render() {
const style = { const style = {
fontSize: `${this.props.size}px`, // fontSize: `${this.props.size}px`,
width: `${this.props.size * 1.28571429}px`, // width: `${this.props.size * 1.28571429}px`,
height: `${this.props.size * 1.28571429}px`, // height: `${this.props.size * 1.28571429}px`,
lineHeight: `${this.props.size}px`, // lineHeight: `${this.props.size}px`,
...this.props.style, ...this.props.style,
...(this.props.active ? this.props.activeStyle : {}), ...(this.props.active ? this.props.activeStyle : {}),
}; };
@ -128,8 +128,12 @@ export default class IconButton extends React.PureComponent {
tabIndex={tabIndex} tabIndex={tabIndex}
disabled={disabled} disabled={disabled}
> >
<Icon id={icon} fixedWidth aria-hidden='true' /> <Icon
</button> id={icon}
fixedWidth
aria-hidden='true'
/>
</button >
); );
} }

View File

@ -98,7 +98,7 @@ class ScrollableList extends PureComponent {
} else { } else {
return this.node; return this.node;
} }
} };
setScrollTop = newScrollTop => { setScrollTop = newScrollTop => {
if (this.getScrollTop() !== newScrollTop) { if (this.getScrollTop() !== newScrollTop) {
@ -144,7 +144,7 @@ class ScrollableList extends PureComponent {
this.mouseMovedRecently = false; this.mouseMovedRecently = false;
this.scrollToTopOnMouseIdle = false; this.scrollToTopOnMouseIdle = false;
} };
componentDidMount () { componentDidMount () {
this.attachScrollListener(); this.attachScrollListener();
@ -162,25 +162,25 @@ class ScrollableList extends PureComponent {
} else { } else {
return null; return null;
} }
} };
getScrollTop = () => { getScrollTop = () => {
return this._getScrollingElement().scrollTop; return this._getScrollingElement().scrollTop;
} };
getScrollHeight = () => { getScrollHeight = () => {
return this._getScrollingElement().scrollHeight; return this._getScrollingElement().scrollHeight;
} };
getClientHeight = () => { getClientHeight = () => {
return this._getScrollingElement().clientHeight; return this._getScrollingElement().clientHeight;
} };
updateScrollBottom = (snapshot) => { updateScrollBottom = (snapshot) => {
const newScrollTop = this.getScrollHeight() - snapshot; const newScrollTop = this.getScrollHeight() - snapshot;
this.setScrollTop(newScrollTop); this.setScrollTop(newScrollTop);
} };
getSnapshotBeforeUpdate (prevProps) { getSnapshotBeforeUpdate (prevProps) {
const someItemInserted = React.Children.count(prevProps.children) > 0 && const someItemInserted = React.Children.count(prevProps.children) > 0 &&
@ -207,7 +207,7 @@ class ScrollableList extends PureComponent {
if (width && this.state.cachedMediaWidth !== width) { if (width && this.state.cachedMediaWidth !== width) {
this.setState({ cachedMediaWidth: width }); this.setState({ cachedMediaWidth: width });
} }
} };
componentWillUnmount () { componentWillUnmount () {
this.clearMouseIdleTimer(); this.clearMouseIdleTimer();
@ -219,7 +219,7 @@ class ScrollableList extends PureComponent {
onFullScreenChange = () => { onFullScreenChange = () => {
this.setState({ fullscreen: isFullscreen() }); this.setState({ fullscreen: isFullscreen() });
} };
attachIntersectionObserver () { attachIntersectionObserver () {
let nodeOptions = { let nodeOptions = {
@ -270,12 +270,12 @@ class ScrollableList extends PureComponent {
setRef = (c) => { setRef = (c) => {
this.node = c; this.node = c;
} };
handleLoadMore = e => { handleLoadMore = e => {
e.preventDefault(); e.preventDefault();
this.props.onLoadMore(); this.props.onLoadMore();
} };
handleLoadPending = e => { handleLoadPending = e => {
e.preventDefault(); e.preventDefault();
@ -287,7 +287,7 @@ class ScrollableList extends PureComponent {
this.clearMouseIdleTimer(); this.clearMouseIdleTimer();
this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY); this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
this.mouseMovedRecently = true; this.mouseMovedRecently = true;
} };
render () { render () {
const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props; const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;

View File

@ -18,12 +18,12 @@ export default class StatusContent extends React.PureComponent {
}; };
static propTypes = { static propTypes = {
status: ImmutablePropTypes.map.isRequired, status : ImmutablePropTypes.map.isRequired,
expanded: PropTypes.bool, expanded : PropTypes.bool,
showThread: PropTypes.bool, showThread : PropTypes.bool,
onExpandedToggle: PropTypes.func, onExpandedToggle : PropTypes.func,
onClick: PropTypes.func, onClick : PropTypes.func,
collapsable: PropTypes.bool, collapsable : PropTypes.bool,
onCollapsedToggle: PropTypes.func, onCollapsedToggle: PropTypes.func,
}; };
@ -112,7 +112,7 @@ export default class StatusContent extends React.PureComponent {
e.preventDefault(); e.preventDefault();
this.context.router.history.push(`/accounts/${mention.get('id')}`); this.context.router.history.push(`/accounts/${mention.get('id')}`);
} }
} };
onHashtagClick = (hashtag, e) => { onHashtagClick = (hashtag, e) => {
hashtag = hashtag.replace(/^#/, ''); hashtag = hashtag.replace(/^#/, '');
@ -121,19 +121,19 @@ export default class StatusContent extends React.PureComponent {
e.preventDefault(); e.preventDefault();
this.context.router.history.push(`/timelines/tag/${hashtag}`); this.context.router.history.push(`/timelines/tag/${hashtag}`);
} }
} };
handleEmojiMouseEnter = ({ target }) => { handleEmojiMouseEnter = ({ target }) => {
target.src = target.getAttribute('data-original'); target.src = target.getAttribute('data-original');
} };
handleEmojiMouseLeave = ({ target }) => { handleEmojiMouseLeave = ({ target }) => {
target.src = target.getAttribute('data-static'); target.src = target.getAttribute('data-static');
} };
handleMouseDown = (e) => { handleMouseDown = (e) => {
this.startXY = [e.clientX, e.clientY]; this.startXY = [e.clientX, e.clientY];
} };
handleMouseUp = (e) => { handleMouseUp = (e) => {
if (!this.startXY) { if (!this.startXY) {
@ -156,7 +156,7 @@ export default class StatusContent extends React.PureComponent {
} }
this.startXY = null; this.startXY = null;
} };
handleSpoilerClick = (e) => { handleSpoilerClick = (e) => {
e.preventDefault(); e.preventDefault();
@ -167,11 +167,11 @@ export default class StatusContent extends React.PureComponent {
} else { } else {
this.setState({ hidden: !this.state.hidden }); this.setState({ hidden: !this.state.hidden });
} }
} };
setRef = (c) => { setRef = (c) => {
this.node = c; this.node = c;
} };
render () { render () {
const { status } = this.props; const { status } = this.props;
@ -188,9 +188,9 @@ export default class StatusContent extends React.PureComponent {
const spoilerContent = { __html: status.get('spoilerHtml') }; const spoilerContent = { __html: status.get('spoilerHtml') };
const directionStyle = { direction: 'ltr' }; const directionStyle = { direction: 'ltr' };
const classNames = classnames('status__content', { const classNames = classnames('status__content', {
'status__content--with-action': this.props.onClick && this.context.router, 'status__content--with-action' : this.props.onClick && this.context.router,
'status__content--with-spoiler': status.get('spoiler_text').length > 0, 'status__content--with-spoiler': status.get('spoiler_text').length > 0,
'status__content--collapsed': renderReadMore, 'status__content--collapsed' : renderReadMore,
}); });
if (isRtl(status.get('search_index'))) { if (isRtl(status.get('search_index'))) {
@ -198,15 +198,28 @@ export default class StatusContent extends React.PureComponent {
} }
const showThreadButton = ( const showThreadButton = (
<button className='status__content__read-more-button' onClick={this.props.onClick}> <button
<FormattedMessage id='status.show_thread' defaultMessage='Show thread' /> className='status__content__read-more-button'
</button> onClick={this.props.onClick}
>
<FormattedMessage
id='status.show_thread'
defaultMessage='Show thread'
/> <i className='fa fa-comment' />
</button >
); );
const readMoreButton = ( const readMoreButton = (
<button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'> <button
<FormattedMessage id='status.read_more' defaultMessage='Read more' /><Icon id='angle-right' fixedWidth /> className='status__content__read-more-button'
</button> onClick={this.props.onClick}
key='read-more'
>
<FormattedMessage
id='status.read_more'
defaultMessage='Read more'
/> <i className='fa fa-comment' />
</button >
); );
if (status.get('spoiler_text').length > 0) { if (status.get('spoiler_text').length > 0) {
@ -229,27 +242,48 @@ export default class StatusContent extends React.PureComponent {
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}> <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
<span dangerouslySetInnerHTML={spoilerContent} /> <span dangerouslySetInnerHTML={spoilerContent} />
{' '} {' '}
<button tabIndex='0' className={`status__content__spoiler-link ${hidden ? 'status__content__spoiler-link--show-more' : 'status__content__spoiler-link--show-less'}`} onClick={this.handleSpoilerClick}>{toggleText}</button> <button
</p> tabIndex='0'
className={`status__content__spoiler-link ${hidden ? 'status__content__spoiler-link--show-more' : 'status__content__spoiler-link--show-less'}`}
onClick={this.handleSpoilerClick}
>{toggleText}</button >
</p >
{mentionsPlaceholder} {mentionsPlaceholder}
<div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} /> <div
tabIndex={!hidden ? 0 : null}
className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`}
style={directionStyle}
dangerouslySetInnerHTML={content}
/>
{!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />} {!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
{renderViewThread && showThreadButton} {renderViewThread && showThreadButton}
</div> </div >
); );
} else if (this.props.onClick) { } else if (this.props.onClick) {
const output = [ const output = [
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content'> <div
<div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} /> className={classNames}
ref={this.setRef}
tabIndex='0'
style={directionStyle}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
key='status-content'
>
<div
className='status__content__text status__content__text--visible'
style={directionStyle}
dangerouslySetInnerHTML={content}
/>
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
{renderViewThread && showThreadButton} {renderViewThread && showThreadButton}
</div>, </div >,
]; ];
if (renderReadMore) { if (renderReadMore) {
@ -259,13 +293,22 @@ export default class StatusContent extends React.PureComponent {
return output; return output;
} else { } else {
return ( return (
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle}> <div
<div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} /> className={classNames}
ref={this.setRef}
tabIndex='0'
style={directionStyle}
>
<div
className='status__content__text status__content__text--visible'
style={directionStyle}
dangerouslySetInnerHTML={content}
/>
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
{renderViewThread && showThreadButton} {renderViewThread && showThreadButton}
</div> </div >
); );
} }
} }

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { blockDomain, unblockDomain } from '../actions/domain_blocks'; import { blockDomain, unblockDomain } from '../actions/domain_blocks';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import Domain from '../components/domain'; import Domain from '../components/domain';
import { openModal } from '../actions/modal'; import { openModal } from '../actions/modal';

View File

@ -0,0 +1,43 @@
import initialState from '../initial_state';
import React, { Fragment } from 'react';
import configureStore from '../store/configureStore';
import { Provider } from 'react-redux';
import PropTypes from 'prop-types';
import { hydrateStore } from '../actions/store';
import { addLocaleData, IntlProvider } from 'react-intl';
import { getLocale } from '../locales';
const { localeData, messages } = getLocale();
addLocaleData(localeData);
const store = configureStore();
if (initialState) {
store.dispatch(hydrateStore(initialState));
}
export default class GalleryContainer extends React.PureComponent {
static propTypes = {
locale: PropTypes.string.isRequired,
};
render() {
const { locale } = this.props;
return (
<IntlProvider
locale={locale}
messages={messages}
>
<Provider store={store}>
<Fragment >
<Gallery ></Gallery >
</Fragment >
</Provider >
</IntlProvider >
);
}
}

View File

@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import Button from 'mastodon/components/button'; import Button from 'mastodon/components/button';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { autoPlayGif, me, isStaff } from 'mastodon/initial_state'; import { autoPlayGif, isStaff, me } from 'mastodon/initial_state';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import Avatar from 'mastodon/components/avatar'; import Avatar from 'mastodon/components/avatar';
@ -15,38 +15,38 @@ import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import AccountNoteContainer from '../containers/account_note_container'; import AccountNoteContainer from '../containers/account_note_container';
const messages = defineMessages({ const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, unfollow : { id: 'account.unfollow', defaultMessage: 'Unfollow' },
follow: { id: 'account.follow', defaultMessage: 'Follow' }, follow : { id: 'account.follow', defaultMessage: 'Follow' },
cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Cancel follow request' }, cancel_follow_request : { id: 'account.cancel_follow_request', defaultMessage: 'Cancel follow request' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' }, linkVerifiedOn : { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' }, account_locked : { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' }, mention : { id: 'account.mention', defaultMessage: 'Mention @{name}' },
direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' }, direct : { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, unmute : { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
block: { id: 'account.block', defaultMessage: 'Block @{name}' }, block : { id: 'account.block', defaultMessage: 'Block @{name}' },
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, mute : { id: 'account.mute', defaultMessage: 'Mute @{name}' },
report: { id: 'account.report', defaultMessage: 'Report @{name}' }, report : { id: 'account.report', defaultMessage: 'Report @{name}' },
share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' }, share : { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
media: { id: 'account.media', defaultMessage: 'Media' }, media : { id: 'account.media', defaultMessage: 'Media' },
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' }, blockDomain : { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' }, unblockDomain : { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' }, hideReblogs : { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' }, showReblogs : { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' }, pins : { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, preferences : { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, follow_requests : { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, favourites : { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, lists : { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, blocks : { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' }, domain_blocks : { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, mutes : { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' }, endorse : { id: 'account.endorse', defaultMessage: 'Feature on profile' },
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' }, unendorse : { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' }, add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_account : { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
}); });
const dateFormatOptions = { const dateFormatOptions = {
@ -72,7 +72,7 @@ class Header extends ImmutablePureComponent {
openEditProfile = () => { openEditProfile = () => {
window.open('/settings/profile', '_blank'); window.open('/settings/profile', '_blank');
} };
isStatusesPageActive = (match, location) => { isStatusesPageActive = (match, location) => {
if (!match) { if (!match) {
@ -80,7 +80,7 @@ class Header extends ImmutablePureComponent {
} }
return !location.pathname.match(/\/(followers|following)\/?$/); return !location.pathname.match(/\/(followers|following)\/?$/);
} };
_updateEmojis () { _updateEmojis () {
const node = this.node; const node = this.node;
@ -113,15 +113,15 @@ class Header extends ImmutablePureComponent {
handleEmojiMouseEnter = ({ target }) => { handleEmojiMouseEnter = ({ target }) => {
target.src = target.getAttribute('data-original'); target.src = target.getAttribute('data-original');
} };
handleEmojiMouseLeave = ({ target }) => { handleEmojiMouseLeave = ({ target }) => {
target.src = target.getAttribute('data-static'); target.src = target.getAttribute('data-static');
} };
setRef = (c) => { setRef = (c) => {
this.node = c; this.node = c;
} };
render () { render () {
const { account, intl, domain, identity_proofs } = this.props; const { account, intl, domain, identity_proofs } = this.props;
@ -144,7 +144,13 @@ class Header extends ImmutablePureComponent {
if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) { if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
info.push(<span key='muted' className='relationship-tag'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>); info.push(<span key='muted' className='relationship-tag'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>);
} else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) { } else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
info.push(<span key='domain_blocked' className='relationship-tag'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain blocked' /></span>); info.push(<span
key='domain_blocked'
className='relationship-tag'
><FormattedMessage
id='account.domain_blocked'
defaultMessage='Domain blocked'
/></span >);
} }
if (me !== account.get('id')) { if (me !== account.get('id')) {

View File

@ -5,18 +5,18 @@ import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({ const messages = defineMessages({
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, edit_profile : { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' }, pins : { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, preferences : { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, favourites : { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' }, lists : { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, blocks : { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, domain_blocks : { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, mutes : { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' }, filters : { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, logout : { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' }, bookmarks : { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
}); });
export default @injectIntl export default @injectIntl
@ -30,7 +30,7 @@ class ActionBar extends React.PureComponent {
handleLogout = () => { handleLogout = () => {
this.props.onLogout(); this.props.onLogout();
} };
render () { render () {
const { intl } = this.props; const { intl } = this.props;

View File

@ -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 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({ 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' }, spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
publish: { id: 'compose_form.publish', defaultMessage: 'Toot' }, publish : { id: 'compose_form.publish', defaultMessage: 'Toot' },
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' }, publishLoud : { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
}); });
export default @injectIntl export default @injectIntl
@ -38,44 +38,46 @@ class ComposeForm extends ImmutablePureComponent {
}; };
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired, intl : PropTypes.object.isRequired,
text: PropTypes.string.isRequired, text : PropTypes.string.isRequired,
suggestions: ImmutablePropTypes.list, suggestions : ImmutablePropTypes.list,
spoiler: PropTypes.bool, spoiler : PropTypes.bool,
privacy: PropTypes.string, privacy : PropTypes.string,
spoilerText: PropTypes.string, spoilerText : PropTypes.string,
focusDate: PropTypes.instanceOf(Date), focusDate : PropTypes.instanceOf(Date),
caretPosition: PropTypes.number, caretPosition : PropTypes.number,
preselectDate: PropTypes.instanceOf(Date), maxTootCharsLimit : PropTypes.number,
isSubmitting: PropTypes.bool, preselectDate : PropTypes.instanceOf(Date),
isChangingUpload: PropTypes.bool, isSubmitting : PropTypes.bool,
isUploading: PropTypes.bool, isChangingUpload : PropTypes.bool,
onChange: PropTypes.func.isRequired, isUploading : PropTypes.bool,
onSubmit: PropTypes.func.isRequired, onChange : PropTypes.func.isRequired,
onClearSuggestions: PropTypes.func.isRequired, onSubmit : PropTypes.func.isRequired,
onFetchSuggestions: PropTypes.func.isRequired, onClearSuggestions : PropTypes.func.isRequired,
onFetchSuggestions : PropTypes.func.isRequired,
onSuggestionSelected: PropTypes.func.isRequired, onSuggestionSelected: PropTypes.func.isRequired,
onChangeSpoilerText: PropTypes.func.isRequired, onChangeSpoilerText : PropTypes.func.isRequired,
onPaste: PropTypes.func.isRequired, onPaste : PropTypes.func.isRequired,
onPickEmoji: PropTypes.func.isRequired, onPickEmoji : PropTypes.func.isRequired,
showSearch: PropTypes.bool, showSearch : PropTypes.bool,
anyMedia: PropTypes.bool, anyMedia : PropTypes.bool,
singleColumn: PropTypes.bool, singleColumn : PropTypes.bool,
}; };
static defaultProps = { static defaultProps = {
showSearch: false, showSearch : false,
maxTootCharsLimit: 7777,
}; };
handleChange = (e) => { handleChange = (e) => {
this.props.onChange(e.target.value); this.props.onChange(e.target.value);
} };
handleKeyDown = (e) => { handleKeyDown = (e) => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
this.handleSubmit(); this.handleSubmit();
} }
} };
handleSubmit = () => { handleSubmit = () => {
if (this.props.text !== this.autosuggestTextarea.textarea.value) { if (this.props.text !== this.autosuggestTextarea.textarea.value) {
@ -88,32 +90,32 @@ class ComposeForm extends ImmutablePureComponent {
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props; const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join(''); 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; return;
} }
this.props.onSubmit(this.context.router ? this.context.router.history : null); this.props.onSubmit(this.context.router ? this.context.router.history : null);
} };
onSuggestionsClearRequested = () => { onSuggestionsClearRequested = () => {
this.props.onClearSuggestions(); this.props.onClearSuggestions();
} };
onSuggestionsFetchRequested = (token) => { onSuggestionsFetchRequested = (token) => {
this.props.onFetchSuggestions(token); this.props.onFetchSuggestions(token);
} };
onSuggestionSelected = (tokenStart, token, value) => { onSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['text']); this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
} };
onSpoilerSuggestionSelected = (tokenStart, token, value) => { onSpoilerSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']); this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
} };
handleChangeSpoilerText = (e) => { handleChangeSpoilerText = (e) => {
this.props.onChangeSpoilerText(e.target.value); this.props.onChangeSpoilerText(e.target.value);
} };
handleFocus = () => { handleFocus = () => {
if (this.composeForm && !this.props.singleColumn) { if (this.composeForm && !this.props.singleColumn) {
@ -122,9 +124,9 @@ class ComposeForm extends ImmutablePureComponent {
this.composeForm.scrollIntoView(); this.composeForm.scrollIntoView();
} }
} }
} };
componentDidUpdate (prevProps) { componentDidUpdate(prevProps) {
// This statement does several things: // This statement does several things:
// - If we're beginning a reply, and, // - If we're beginning a reply, and,
// - Replying to zero or one users, places the cursor at the end of the textbox. // - Replying to zero or one users, places the cursor at the end of the textbox.
@ -134,19 +136,19 @@ class ComposeForm extends ImmutablePureComponent {
let selectionEnd, selectionStart; let selectionEnd, selectionStart;
if (this.props.preselectDate !== prevProps.preselectDate) { if (this.props.preselectDate !== prevProps.preselectDate) {
selectionEnd = this.props.text.length; selectionEnd = this.props.text.length;
selectionStart = this.props.text.search(/\s/) + 1; selectionStart = this.props.text.search(/\s/) + 1;
} else if (typeof this.props.caretPosition === 'number') { } else if (typeof this.props.caretPosition === 'number') {
selectionStart = this.props.caretPosition; selectionStart = this.props.caretPosition;
selectionEnd = this.props.caretPosition; selectionEnd = this.props.caretPosition;
} else { } else {
selectionEnd = this.props.text.length; selectionEnd = this.props.text.length;
selectionStart = selectionEnd; selectionStart = selectionEnd;
} }
this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd); this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
this.autosuggestTextarea.textarea.focus(); this.autosuggestTextarea.textarea.focus();
} else if(prevProps.isSubmitting && !this.props.isSubmitting) { } else if (prevProps.isSubmitting && !this.props.isSubmitting) {
this.autosuggestTextarea.textarea.focus(); this.autosuggestTextarea.textarea.focus();
} else if (this.props.spoiler !== prevProps.spoiler) { } else if (this.props.spoiler !== prevProps.spoiler) {
if (this.props.spoiler) { if (this.props.spoiler) {
@ -159,33 +161,34 @@ class ComposeForm extends ImmutablePureComponent {
setAutosuggestTextarea = (c) => { setAutosuggestTextarea = (c) => {
this.autosuggestTextarea = c; this.autosuggestTextarea = c;
} };
setSpoilerText = (c) => { setSpoilerText = (c) => {
this.spoilerText = c; this.spoilerText = c;
} };
setRef = c => { setRef = c => {
this.composeForm = c; this.composeForm = c;
}; };
handleEmojiPick = (data) => { handleEmojiPick = (data) => {
const { text } = this.props; const { text } = this.props;
const position = this.autosuggestTextarea.textarea.selectionStart; const position = this.autosuggestTextarea.textarea.selectionStart;
const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]); const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
this.props.onPickEmoji(position, data, needsSpace); this.props.onPickEmoji(position, data, needsSpace);
} };
render () { render() {
const { intl, onPaste, showSearch, anyMedia } = this.props; const { intl, onPaste, showSearch, anyMedia } = this.props;
const disabled = this.props.isSubmitting; const disabled = this.props.isSubmitting;
const text = [this.props.spoilerText, countableText(this.props.text)].join(''); 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 = ''; let publishText = '';
if (this.props.privacy === 'private' || this.props.privacy === 'direct') { 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 { } else {
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish); 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 /> <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 <AutosuggestInput
placeholder={intl.formatMessage(messages.spoiler_placeholder)} placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={this.props.spoilerText} value={this.props.spoilerText}
@ -212,7 +218,7 @@ class ComposeForm extends ImmutablePureComponent {
id='cw-spoiler-input' id='cw-spoiler-input'
className='spoiler-input__input' className='spoiler-input__input'
/> />
</div> </div >
<AutosuggestTextarea <AutosuggestTextarea
ref={this.setAutosuggestTextarea} ref={this.setAutosuggestTextarea}
@ -233,8 +239,8 @@ class ComposeForm extends ImmutablePureComponent {
<div className='compose-form__modifiers'> <div className='compose-form__modifiers'>
<UploadFormContainer /> <UploadFormContainer />
<PollFormContainer /> <PollFormContainer />
</div> </div >
</AutosuggestTextarea> </AutosuggestTextarea >
<div className='compose-form__buttons-wrapper'> <div className='compose-form__buttons-wrapper'>
<div className='compose-form__buttons'> <div className='compose-form__buttons'>
@ -242,14 +248,23 @@ class ComposeForm extends ImmutablePureComponent {
<PollButtonContainer /> <PollButtonContainer />
<PrivacyDropdownContainer /> <PrivacyDropdownContainer />
<SpoilerButtonContainer /> <SpoilerButtonContainer />
</div> </div >
<div className='character-counter__wrapper'><CharacterCounter max={500} text={text} /></div> <div className='character-counter__wrapper'><CharacterCounter
</div> max={this.props.maxTootCharsLimit}
text={text}
/></div >
</div >
<div className='compose-form__publish'> <div className='compose-form__publish'>
<div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabledButton} block /></div> <div className='compose-form__publish-button-wrapper'><Button
</div> text={publishText}
</div> onClick={this.handleSubmit}
disabled={disabledButton}
block
/></div >
</div >
</div >
); );
} }

View File

@ -11,32 +11,57 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
export default class NavigationBar extends ImmutablePureComponent { export default class NavigationBar extends ImmutablePureComponent {
static propTypes = { static propTypes = {
account: ImmutablePropTypes.map.isRequired, account : ImmutablePropTypes.map.isRequired,
onLogout: PropTypes.func.isRequired, onLogout: PropTypes.func.isRequired,
onClose: PropTypes.func, onClose : PropTypes.func,
}; };
render () { render() {
return ( return (
<div className='navigation-bar'> <div className='navigation-bar'>
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}> <Permalink
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span> href={this.props.account.get('url')}
<Avatar account={this.props.account} size={48} /> to={`/accounts/${this.props.account.get('id')}`}
</Permalink> >
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span >
<Avatar
account={this.props.account}
size={48}
/>
</Permalink >
<div className='navigation-bar__profile'> <div className='navigation-bar__profile'>
<Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}> <Permalink
<strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong> href={this.props.account.get('url')}
</Permalink> 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> <a
</div> href='/settings/profile'
className='navigation-bar__profile-edit'
>
<i className='fa fa-pencil' />
<FormattedMessage
id='navigation_bar.edit_profile'
defaultMessage='Edit profile'
/></a >
</div >
<div className='navigation-bar__actions'> <div className='navigation-bar__actions'>
<IconButton className='close' title='' icon='close' onClick={this.props.onClose} /> <IconButton
<ActionBar account={this.props.account} onLogout={this.props.onLogout} /> className='close'
</div> title=''
</div> icon='close'
onClick={this.props.onClose}
/>
<ActionBar
account={this.props.account}
onLogout={this.props.onLogout}
/>
</div >
</div >
); );
} }

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import IconButton from 'mastodon/components/icon_button'; import IconButton from 'mastodon/components/icon_button';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import AutosuggestInput from 'mastodon/components/autosuggest_input'; import AutosuggestInput from 'mastodon/components/autosuggest_input';
@ -57,19 +57,19 @@ class Option extends React.PureComponent {
if (e.key === 'Enter' || e.key === ' ') { if (e.key === 'Enter' || e.key === ' ') {
this.handleToggleMultiple(e); this.handleToggleMultiple(e);
} }
} };
onSuggestionsClearRequested = () => { onSuggestionsClearRequested = () => {
this.props.onClearSuggestions(); this.props.onClearSuggestions();
} };
onSuggestionsFetchRequested = (token) => { onSuggestionsFetchRequested = (token) => {
this.props.onFetchSuggestions(token); this.props.onFetchSuggestions(token);
} };
onSuggestionSelected = (tokenStart, token, value) => { onSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]); this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
} };
render () { render () {
const { isPollMultiple, title, index, autoFocus, intl } = this.props; const { isPollMultiple, title, index, autoFocus, intl } = this.props;

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
@ -9,7 +9,7 @@ import LoadingIndicator from '../../components/loading_indicator';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
import ColumnBackButtonSlim from '../../components/column_back_button_slim'; import ColumnBackButtonSlim from '../../components/column_back_button_slim';
import DomainContainer from '../../containers/domain_container'; import DomainContainer from '../../containers/domain_container';
import { fetchDomainBlocks, expandDomainBlocks } from '../../actions/domain_blocks'; import { expandDomainBlocks, fetchDomainBlocks } from '../../actions/domain_blocks';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
const messages = defineMessages({ const messages = defineMessages({
@ -55,7 +55,10 @@ class Blocks extends ImmutablePureComponent {
); );
} }
const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no blocked domains yet.' />; const emptyMessage = <FormattedMessage
id='empty_column.domain_blocks'
defaultMessage='There are no blocked domains yet.'
/>;
return ( return (
<Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}>

View File

@ -4,45 +4,118 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import Permalink from '../../../components/permalink'; import Permalink from '../../../components/permalink';
import Avatar from '../../../components/avatar'; import Avatar from '../../../components/avatar';
import DisplayName from '../../../components/display_name'; import DisplayName from '../../../components/display_name';
import IconButton from '../../../components/icon_button'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl'; import { shortNumberFormat } from 'mastodon/utils/numbers';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { NavLink } from 'react-router-dom';
const messages = defineMessages({ const messages = defineMessages({
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' }, authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }, reject : { id: 'follow_request.reject', defaultMessage: 'Reject' },
}); });
export default @injectIntl export default @injectIntl
class AccountAuthorize extends ImmutablePureComponent { class AccountAuthorize extends ImmutablePureComponent {
static propTypes = { static propTypes = {
account: ImmutablePropTypes.map.isRequired, account : ImmutablePropTypes.map.isRequired,
onAuthorize: PropTypes.func.isRequired, onAuthorize: PropTypes.func.isRequired,
onReject: PropTypes.func.isRequired, onReject : PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl : PropTypes.object.isRequired,
}; };
render () { render() {
const { intl, account, onAuthorize, onReject } = this.props; const { intl, account, onAuthorize, onReject } = this.props;
const content = { __html: account.get('note_emojified') }; const content = { __html: account.get('note_emojified') };
return ( return (
<div className='account-authorize__wrapper'> <div className='account-authorize__wrapper'>
<div className='account-authorize'> <div className='account-authorize'>
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'> <Permalink
<div className='account-authorize__avatar'><Avatar account={account} size={48} /></div> href={account.get('url')}
<DisplayName account={account} /> to={`/accounts/${account.get('id')}`}
</Permalink> className='detailed-status__display-name'
>
<div className='account-authorize__avatar'>
<Avatar
account={account}
size={100}
/>
<div className='account__header__content' dangerouslySetInnerHTML={content} /> </div >
</div> <DisplayName account={account} />
</Permalink >
{/*count posts*/}
<span className='account-authorize--more-data'>
<NavLink
isActive={this.isStatusesPageActive}
activeClassName='active'
to={`/accounts/${account.get('id')}`}
title={intl.formatNumber(account.get('statuses_count'))}
>
<i className='fa fa-comment' />
<strong >{shortNumberFormat(account.get('statuses_count'))}</strong > <FormattedMessage
id='account.posts'
defaultMessage='Toots'
/>
</NavLink >
<NavLink
exact
activeClassName='active'
to={`/accounts/${account.get('id')}/following`}
title={intl.formatNumber(account.get('following_count'))}
>
<strong >{shortNumberFormat(account.get('following_count'))}</strong > <FormattedMessage
id='account.follows'
defaultMessage='Follows'
/>
</NavLink >
<NavLink
exact
activeClassName='active'
to={`/accounts/${account.get('id')}/followers`}
title={intl.formatNumber(account.get('followers_count'))}
>
<strong >{shortNumberFormat(account.get('followers_count'))}</strong > <FormattedMessage
id='account.followers'
defaultMessage='Followers'
/>
</NavLink >
</span >
<div
className='account__header__content'
dangerouslySetInnerHTML={content}
/>
</div >
<div className='account--panel'> <div className='account--panel'>
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div> <div
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div> className='account--panel__button account--panel__button__allow clickable'
</div> onClick={onAuthorize}
</div> >
<i
className='fa fa-check'
title={intl.formatMessage(messages.authorize)}
/>
{intl.formatMessage(messages.authorize)}
</div >
<div
className='account--panel__button account--panel__button__reject clickable'
onClick={onReject}
>
<i
className='fa fa-times'
title={intl.formatMessage(messages.reject)}
/>
{intl.formatMessage(messages.reject)}
</div >
</div >
</div >
); );
} }

View File

@ -5,10 +5,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import IconButton from 'mastodon/components/icon_button'; import IconButton from 'mastodon/components/icon_button';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl'; import { defineMessages, FormattedDate, FormattedMessage, injectIntl } from 'react-intl';
import { autoPlayGif, reduceMotion } from 'mastodon/initial_state'; import { autoPlayGif, mascot, reduceMotion } from 'mastodon/initial_state';
import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg'; import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg';
import { mascot } from 'mastodon/initial_state';
import unicodeMapping from 'mastodon/features/emoji/emoji_unicode_mapping_light'; import unicodeMapping from 'mastodon/features/emoji/emoji_unicode_mapping_light';
import classNames from 'classnames'; import classNames from 'classnames';
import EmojiPickerDropdown from 'mastodon/features/compose/containers/emoji_picker_dropdown_container'; import EmojiPickerDropdown from 'mastodon/features/compose/containers/emoji_picker_dropdown_container';
@ -17,9 +16,9 @@ import TransitionMotion from 'react-motion/lib/TransitionMotion';
import spring from 'react-motion/lib/spring'; import spring from 'react-motion/lib/spring';
const messages = defineMessages({ const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' }, close : { id: 'lightbox.close', defaultMessage: 'Close' },
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' }, previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
next: { id: 'lightbox.next', defaultMessage: 'Next' }, next : { id: 'lightbox.next', defaultMessage: 'Next' },
}); });
class Content extends ImmutablePureComponent { class Content extends ImmutablePureComponent {
@ -34,7 +33,7 @@ class Content extends ImmutablePureComponent {
setRef = c => { setRef = c => {
this.node = c; this.node = c;
} };
componentDidMount () { componentDidMount () {
this._updateLinks(); this._updateLinks();
@ -113,7 +112,7 @@ class Content extends ImmutablePureComponent {
e.preventDefault(); e.preventDefault();
this.context.router.history.push(`/accounts/${mention.get('id')}`); this.context.router.history.push(`/accounts/${mention.get('id')}`);
} }
} };
onHashtagClick = (hashtag, e) => { onHashtagClick = (hashtag, e) => {
hashtag = hashtag.replace(/^#/, ''); hashtag = hashtag.replace(/^#/, '');
@ -122,22 +121,22 @@ class Content extends ImmutablePureComponent {
e.preventDefault(); e.preventDefault();
this.context.router.history.push(`/timelines/tag/${hashtag}`); this.context.router.history.push(`/timelines/tag/${hashtag}`);
} }
} };
onStatusClick = (status, e) => { onStatusClick = (status, e) => {
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) { if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault(); e.preventDefault();
this.context.router.history.push(`/statuses/${status.get('id')}`); this.context.router.history.push(`/statuses/${status.get('id')}`);
} }
} };
handleEmojiMouseEnter = ({ target }) => { handleEmojiMouseEnter = ({ target }) => {
target.src = target.getAttribute('data-original'); target.src = target.getAttribute('data-original');
} };
handleEmojiMouseLeave = ({ target }) => { handleEmojiMouseLeave = ({ target }) => {
target.src = target.getAttribute('data-static'); target.src = target.getAttribute('data-static');
} };
render () { render () {
const { announcement } = this.props; const { announcement } = this.props;
@ -222,11 +221,11 @@ class Reaction extends ImmutablePureComponent {
} else { } else {
addReaction(announcementId, reaction.get('name')); addReaction(announcementId, reaction.get('name'));
} }
} };
handleMouseEnter = () => this.setState({ hovered: true }) handleMouseEnter = () => this.setState({ hovered: true });
handleMouseLeave = () => this.setState({ hovered: false }) handleMouseLeave = () => this.setState({ hovered: false });
render () { render () {
const { reaction } = this.props; const { reaction } = this.props;
@ -260,7 +259,7 @@ class ReactionsBar extends ImmutablePureComponent {
handleEmojiPick = data => { handleEmojiPick = data => {
const { addReaction, announcementId } = this.props; const { addReaction, announcementId } = this.props;
addReaction(announcementId, data.native.replace(/:/g, '')); addReaction(announcementId, data.native.replace(/:/g, ''));
} };
willEnter () { willEnter () {
return { scale: reduceMotion ? 1 : 0 }; return { scale: reduceMotion ? 1 : 0 };
@ -308,26 +307,26 @@ class ReactionsBar extends ImmutablePureComponent {
class Announcement extends ImmutablePureComponent { class Announcement extends ImmutablePureComponent {
static propTypes = { static propTypes = {
announcement: ImmutablePropTypes.map.isRequired, announcement : ImmutablePropTypes.map.isRequired,
emojiMap: ImmutablePropTypes.map.isRequired, emojiMap : ImmutablePropTypes.map.isRequired,
addReaction: PropTypes.func.isRequired, addReaction : PropTypes.func.isRequired,
removeReaction: PropTypes.func.isRequired, removeReaction: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl : PropTypes.object.isRequired,
selected: PropTypes.bool, selected : PropTypes.bool,
}; };
state = { state = {
unread: !this.props.announcement.get('read'), unread: !this.props.announcement.get('read'),
}; };
componentDidUpdate () { componentDidUpdate() {
const { selected, announcement } = this.props; const { selected, announcement } = this.props;
if (!selected && this.state.unread !== !announcement.get('read')) { if (!selected && this.state.unread !== !announcement.get('read')) {
this.setState({ unread: !announcement.get('read') }); this.setState({ unread: !announcement.get('read') });
} }
} }
render () { render() {
const { announcement } = this.props; const { announcement } = this.props;
const { unread } = this.state; const { unread } = this.state;
const startsAt = announcement.get('starts_at') && new Date(announcement.get('starts_at')); const startsAt = announcement.get('starts_at') && new Date(announcement.get('starts_at'));
@ -356,7 +355,7 @@ class Announcement extends ImmutablePureComponent {
/> />
{unread && <span className='announcements__item__unread' />} {unread && <span className='announcements__item__unread' />}
</div> </div >
); );
} }
@ -366,12 +365,12 @@ export default @injectIntl
class Announcements extends ImmutablePureComponent { class Announcements extends ImmutablePureComponent {
static propTypes = { static propTypes = {
announcements: ImmutablePropTypes.list, announcements : ImmutablePropTypes.list,
emojiMap: ImmutablePropTypes.map.isRequired, emojiMap : ImmutablePropTypes.map.isRequired,
dismissAnnouncement: PropTypes.func.isRequired, dismissAnnouncement: PropTypes.func.isRequired,
addReaction: PropTypes.func.isRequired, addReaction : PropTypes.func.isRequired,
removeReaction: PropTypes.func.isRequired, removeReaction : PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl : PropTypes.object.isRequired,
}; };
state = { state = {
@ -386,15 +385,15 @@ class Announcements extends ImmutablePureComponent {
} }
} }
componentDidMount () { componentDidMount() {
this._markAnnouncementAsRead(); this._markAnnouncementAsRead();
} }
componentDidUpdate () { componentDidUpdate() {
this._markAnnouncementAsRead(); this._markAnnouncementAsRead();
} }
_markAnnouncementAsRead () { _markAnnouncementAsRead() {
const { dismissAnnouncement, announcements } = this.props; const { dismissAnnouncement, announcements } = this.props;
const { index } = this.state; const { index } = this.state;
const announcement = announcements.get(index); const announcement = announcements.get(index);
@ -403,15 +402,15 @@ class Announcements extends ImmutablePureComponent {
handleChangeIndex = index => { handleChangeIndex = index => {
this.setState({ index: index % this.props.announcements.size }); this.setState({ index: index % this.props.announcements.size });
} };
handleNextClick = () => { handleNextClick = () => {
this.setState({ index: (this.state.index + 1) % this.props.announcements.size }); this.setState({ index: (this.state.index + 1) % this.props.announcements.size });
} };
handlePrevClick = () => { handlePrevClick = () => {
this.setState({ index: (this.props.announcements.size + this.state.index - 1) % this.props.announcements.size }); this.setState({ index: (this.props.announcements.size + this.state.index - 1) % this.props.announcements.size });
} };
render () { render () {
const { announcements, intl } = this.props; const { announcements, intl } = this.props;

View File

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { addReaction, removeReaction, dismissAnnouncement } from 'mastodon/actions/announcements'; import { addReaction, dismissAnnouncement, removeReaction } from 'mastodon/actions/announcements';
import Announcements from '../components/announcements'; import Announcements from '../components/announcements';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap } from 'immutable';
@ -13,8 +13,8 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
dismissAnnouncement: id => dispatch(dismissAnnouncement(id)), dismissAnnouncement: id => dispatch(dismissAnnouncement(id)),
addReaction: (id, name) => dispatch(addReaction(id, name)), addReaction : (id, name) => dispatch(addReaction(id, name)),
removeReaction: (id, name) => dispatch(removeReaction(id, name)), removeReaction : (id, name) => dispatch(removeReaction(id, name)),
}); });
export default connect(mapStateToProps, mapDispatchToProps)(Announcements); export default connect(mapStateToProps, mapDispatchToProps)(Announcements);

View File

@ -5,8 +5,8 @@ import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column'; import Column from '../../components/column';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, moveColumn, removeColumn } from '../../actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container'; import ColumnSettingsContainer from './containers/column_settings_container';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements'; import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements';
@ -15,17 +15,17 @@ import classNames from 'classnames';
import IconWithBadge from 'mastodon/components/icon_with_badge'; import IconWithBadge from 'mastodon/components/icon_with_badge';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'column.home', defaultMessage: 'Home' }, title : { id: 'column.home', defaultMessage: 'Home' },
show_announcements: { id: 'home.show_announcements', defaultMessage: 'Show announcements' }, show_announcements: { id: 'home.show_announcements', defaultMessage: 'Show announcements' },
hide_announcements: { id: 'home.hide_announcements', defaultMessage: 'Hide announcements' }, hide_announcements: { id: 'home.hide_announcements', defaultMessage: 'Hide announcements' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0, hasUnread : state.getIn(['timelines', 'home', 'unread']) > 0,
isPartial: state.getIn(['timelines', 'home', 'isPartial']), isPartial : state.getIn(['timelines', 'home', 'isPartial']),
hasAnnouncements: !state.getIn(['announcements', 'items']).isEmpty(), hasAnnouncements : !state.getIn(['announcements', 'items']).isEmpty(),
unreadAnnouncements: state.getIn(['announcements', 'items']).count(item => !item.get('read')), unreadAnnouncements: state.getIn(['announcements', 'items']).count(item => !item.get('read')),
showAnnouncements: state.getIn(['announcements', 'show']), showAnnouncements : state.getIn(['announcements', 'show']),
}); });
export default @connect(mapStateToProps) export default @connect(mapStateToProps)
@ -53,24 +53,24 @@ class HomeTimeline extends React.PureComponent {
} else { } else {
dispatch(addColumn('HOME', {})); dispatch(addColumn('HOME', {}));
} }
} };
handleMove = (dir) => { handleMove = (dir) => {
const { columnId, dispatch } = this.props; const { columnId, dispatch } = this.props;
dispatch(moveColumn(columnId, dir)); dispatch(moveColumn(columnId, dir));
} };
handleHeaderClick = () => { handleHeaderClick = () => {
this.column.scrollTop(); this.column.scrollTop();
} };
setRef = c => { setRef = c => {
this.column = c; this.column = c;
} };
handleLoadMore = maxId => { handleLoadMore = maxId => {
this.props.dispatch(expandHomeTimeline({ maxId })); this.props.dispatch(expandHomeTimeline({ maxId }));
} };
componentDidMount () { componentDidMount () {
this.props.dispatch(fetchAnnouncements()); this.props.dispatch(fetchAnnouncements());
@ -89,7 +89,7 @@ class HomeTimeline extends React.PureComponent {
const { dispatch } = this.props; const { dispatch } = this.props;
if (wasPartial === isPartial) { if (wasPartial === isPartial) {
return;
} else if (!wasPartial && isPartial) { } else if (!wasPartial && isPartial) {
this.polling = setInterval(() => { this.polling = setInterval(() => {
dispatch(expandHomeTimeline()); dispatch(expandHomeTimeline());
@ -109,7 +109,7 @@ class HomeTimeline extends React.PureComponent {
handleToggleAnnouncementsClick = (e) => { handleToggleAnnouncementsClick = (e) => {
e.stopPropagation(); e.stopPropagation();
this.props.dispatch(toggleShowAnnouncements()); this.props.dispatch(toggleShowAnnouncements());
} };
render () { render () {
const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;

View File

@ -1,11 +1,12 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 { Link, NavLink } 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 { logOut } from 'mastodon/utils/log_out';
import { openModal } from 'mastodon/actions/modal'; import { openModal } from 'mastodon/actions/modal';
import { isStaff } from '../../../initial_state';
const messages = defineMessages({ const messages = defineMessages({
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' }, logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
@ -13,23 +14,36 @@ const messages = defineMessages({
}); });
const mapDispatchToProps = (dispatch, { intl }) => ({ const mapDispatchToProps = (dispatch, { intl }) => ({
onLogout () { onLogout() {
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.logoutMessage), message : intl.formatMessage(messages.logoutMessage),
confirm: intl.formatMessage(messages.logoutConfirm), confirm : intl.formatMessage(messages.logoutConfirm),
onConfirm: () => logOut(), onConfirm: () => logOut(),
})); }));
}, },
}); });
// const themeIsDark = true;
// const displaythemetoggler = true;
export default @injectIntl export default @injectIntl
@connect(null, mapDispatchToProps) @connect(null, mapDispatchToProps)
class LinkFooter extends React.PureComponent { class LinkFooter extends React.PureComponent {
static propTypes = { static propTypes = {
withHotkeys: PropTypes.bool, enableChristmasSnow : PropTypes.bool,
onLogout: PropTypes.func.isRequired, minimumWeekToShowSnow: PropTypes.number,
intl: PropTypes.object.isRequired, snowActive : PropTypes.bool,
withHotkeys : PropTypes.bool,
snow : PropTypes.func,
themeIsDark : PropTypes.bool,
theme : PropTypes.string,
onLogout : PropTypes.func.isRequired,
intl : PropTypes.object.isRequired,
};
static defaultProps = {
enableChristmasSnow : true,
themeIsDark : true,
minimumWeekToShowSnow: 48,
}; };
handleLogoutClick = e => { handleLogoutClick = e => {
@ -39,33 +53,243 @@ class LinkFooter extends React.PureComponent {
this.props.onLogout(); this.props.onLogout();
return false; 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
const shouldWeDisplaySnow = (weekNumber > props.minimumWeekToShowSnow) && props.enableChristmasSnow;
this.state = {
enableChristmasSnow: shouldWeDisplaySnow,
theme : props.theme,
};
// make snow effect
if (shouldWeDisplaySnow) {
import('../../../utils/snowstorm-min')
.then((snowstorm) => {
Window.snowstorm = snowstorm.default;
this.state.snow = Window.snowstorm;
// snowstorm.start();
this.state.snowActive = true;
})
.catch((err) => console.error(err));
}
} }
render () { toggleSnow = () => {
if (this.state.snow) {
if (this.state.snowActive) {
this.state.snow.stop();
this.state.enableChristmasSnow = false;
} else {
this.state.snow.start();
this.state.enableChristmasSnow = true;
}
}
};
changeTheme(newTheme) {
console.log('change theme en ', newTheme);
}
render() {
const HashTagNavlinks = ['Mastoart', 'OpenStreetMaps', 'Ironèmes', 'vélo'];
const { withHotkeys } = this.props; const { withHotkeys } = this.props;
var snowClasses = this.props.enableChristmasSnow ? 'snow-button active' : 'snow-button ';
const navToTags = HashTagNavlinks.map(element => {
return (
<li
className='tag-element'
key={element}
>
<NavLink
exact
activeClassName='active'
to={'/timelines/tag/' + element}
title='Mastoart'
>
#{element}
</NavLink >
</li >
);
});
return ( 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='links-started__footer desktop-only'>
<div className='extras'>
{/*<button className='mod-theme btn btn-block btn-small btn-primary pull-left'>*/}
{/* {this.themeIsDark ? (*/}
{/* <span*/}
{/* onClick={this.setState('theme', 'light')}*/}
{/* title='set light'*/}
{/* >*/}
{/* <i className='fa fa-pencil-o' /> to light*/}
{/* </span >*/}
{/* ) : (*/}
{/* <span*/}
{/* onClick={*/}
{/* this.changeTheme*/}
{/* }*/}
{/* title='set dark'*/}
{/* >*/}
{/* <i className='fa fa-pencil' /> to dark*/}
{/* </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 >
</div >
)}
{isStaff && (
<span className='staff-actions'>
<a
className='btn-warning'
href='/admin/tags?pending_review=1'
>
<i className='fa fa-fire' />
Trending hashtags
</a >
<a
className='btn-warning'
href='/admin/accounts'
>
<i className='fa fa-users' />
Comptes
</a >
</span >
)}
<br />
<div className='external-utilities'>
<div >
<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 >
</div >
<div className='suggested-tags'>
<ul >
{navToTags}
</ul >
</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 <FormattedMessage
id='getting_started.open_source_notice' id='getting_started.open_source_notice'
defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.' defaultMessage='Mastodon is open source software. You can contribute or report issues on the forge at {forge}.'
values={{ github: <span><a href={source_url} rel='noopener noreferrer' target='_blank'>{repository}</a> (v{version})</span> }} values={{
forge: <span ><a
href={source_url}
rel='noopener noreferrer'
target='_blank'
>{repository}</a > (v{version})</span >,
}}
/> />
</p> </p >
</div> </div >
); );
} }

View File

@ -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 >
);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,14 @@
import React from 'react';
export default class ConversationStream extends React.PureComponent {
render() {
return (
<div class='conversation-stream'>
ConversationStream todo
</div >
);
}
}

View File

@ -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 >),
);
};
}

View File

@ -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 >
);
}
};

View File

@ -10,27 +10,154 @@ import TrendsContainer from 'mastodon/features/getting_started/containers/trends
const NavigationPanel = () => ( const NavigationPanel = () => (
<div className='navigation-panel'> <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> <div className='small-texts timelines'>
<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/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' />
<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 /> <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> <div className='spacer' />
<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
<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> className='column-link column-link--transparent'
<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> to='/favourites'
{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>} ><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 /> <ListPanel />
<hr /> <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
<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> 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 && <div className='flex-spacer' />}
{showTrends && <TrendsContainer />} {showTrends && <TrendsContainer />}
</div>
</div >
); );
export default withRouter(NavigationPanel); export default withRouter(NavigationPanel);

View File

@ -8,19 +8,81 @@ import Icon from 'mastodon/components/icon';
import NotificationsCounterIcon from './notifications_counter_icon'; import NotificationsCounterIcon from './notifications_counter_icon';
export const links = [ 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
<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>, className='tabs-bar__link'
<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>, to='/timelines/home'
<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>, data-preview-title-id='column.home'
<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>, data-preview-icon='home'
<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>, ><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); return links.findIndex(link => link.props.to === path);
} }
export function getLink (index) { export function getLink(index) {
return links[index].props.to; return links[index].props.to;
} }
@ -29,13 +91,13 @@ export default @injectIntl
class TabsBar extends React.PureComponent { class TabsBar extends React.PureComponent {
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired, intl : PropTypes.object.isRequired,
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
} };
setRef = ref => { setRef = ref => {
this.node = ref; this.node = ref;
} };
handleClick = (e) => { handleClick = (e) => {
// Only apply optimization for touch devices, which we assume are slower // 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 nextTab = tabs.find(tab => tab.contains(e.target));
const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)]; const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
if (currentTab !== nextTab) { if (currentTab !== nextTab) {
if (currentTab) { if (currentTab) {
currentTab.classList.remove('active'); currentTab.classList.remove('active');
@ -67,19 +128,28 @@ class TabsBar extends React.PureComponent {
}); });
} }
} };
render () { render() {
const { intl: { formatMessage } } = this.props; const { intl: { formatMessage } } = this.props;
return ( return (
<div className='tabs-bar__wrapper'> <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 id='tabs-bar__portal' />
</div>
</div >
); );
} }

View File

@ -15,9 +15,9 @@ class ReducedMotion extends React.Component {
static propTypes = { static propTypes = {
defaultStyle: PropTypes.object, defaultStyle: PropTypes.object,
style: PropTypes.object, style : PropTypes.object,
children: PropTypes.func, children : PropTypes.func,
} };
render() { render() {
@ -33,9 +33,12 @@ class ReducedMotion extends React.Component {
}); });
return ( return (
<Motion style={style} defaultStyle={defaultStyle}> <Motion
style={style}
defaultStyle={defaultStyle}
>
{children} {children}
</Motion> </Motion >
); );
} }

View File

@ -9,7 +9,7 @@
"account.blocked": "Bloqué·e", "account.blocked": "Bloqué·e",
"account.browse_more_on_origin_server": "Parcourir davantage sur le profil original", "account.browse_more_on_origin_server": "Parcourir davantage sur le profil original",
"account.cancel_follow_request": "Annuler la demande de suivi", "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 bloqué", "account.domain_blocked": "Domaine bloqué",
"account.edit_profile": "Modifier le profil", "account.edit_profile": "Modifier le profil",
"account.endorse": "Recommander sur le profil", "account.endorse": "Recommander sur le profil",
@ -59,6 +59,7 @@
"bundle_modal_error.close": "Fermer", "bundle_modal_error.close": "Fermer",
"bundle_modal_error.message": "Une erreur sest produite lors du chargement de ce composant.", "bundle_modal_error.message": "Une erreur sest produite lors du chargement de ce composant.",
"bundle_modal_error.retry": "Réessayer", "bundle_modal_error.retry": "Réessayer",
"column.bookmarks": "Marque pages",
"column.blocks": "Comptes bloqués", "column.blocks": "Comptes bloqués",
"column.bookmarks": "Marque-pages", "column.bookmarks": "Marque-pages",
"column.community": "Fil public local", "column.community": "Fil public local",
@ -154,7 +155,7 @@
"empty_column.blocks": "Vous navez bloqué aucun·e utilisateur·rice pour le moment.", "empty_column.blocks": "Vous navez bloqué aucun·e utilisateur·rice pour le moment.",
"empty_column.bookmarked_statuses": "Vous n'avez pas de pouets enregistrés comme marque-pages pour le moment. Lorsque vous en ajouterez un, il apparaîtra ici.", "empty_column.bookmarked_statuses": "Vous n'avez pas de pouets enregistrés comme marque-pages pour le moment. Lorsque vous en ajouterez un, il apparaîtra ici.",
"empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir!", "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir!",
"empty_column.direct": "Vous navez pas encore de messages directs. Lorsque vous en enverrez ou recevrez un, il saffichera ici.", "empty_column.direct": "Vous navez pas encore de messages privés. Lorsque vous en enverrez ou recevrez un, il saffichera ici.",
"empty_column.domain_blocks": "Il ny a aucun domaine caché pour le moment.", "empty_column.domain_blocks": "Il ny a aucun domaine caché pour le moment.",
"empty_column.favourited_statuses": "Vous navez aucun pouet favoris pour le moment. Lorsque vous en mettrez un en favori, il apparaîtra ici.", "empty_column.favourited_statuses": "Vous navez aucun pouet favoris pour le moment. Lorsque vous en mettrez un en favori, il apparaîtra ici.",
"empty_column.favourites": "Personne na encore mis ce pouet en favori. Lorsque quelquun le fera, il apparaîtra ici.", "empty_column.favourites": "Personne na encore mis ce pouet en favori. Lorsque quelquun le fera, il apparaîtra ici.",
@ -179,7 +180,7 @@
"getting_started.documentation": "Documentation", "getting_started.documentation": "Documentation",
"getting_started.heading": "Pour commencer", "getting_started.heading": "Pour commencer",
"getting_started.invite": "Inviter des gens", "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.security": "Sécurité",
"getting_started.terms": "Conditions dutilisation", "getting_started.terms": "Conditions dutilisation",
"hashtag.column_header.tag_mode.all": "et {additional}", "hashtag.column_header.tag_mode.all": "et {additional}",
@ -222,7 +223,7 @@
"keyboard_shortcuts.column": "pour focaliser un statut dans lune des colonnes", "keyboard_shortcuts.column": "pour focaliser un statut dans lune des colonnes",
"keyboard_shortcuts.compose": "cibler la zone de rédaction", "keyboard_shortcuts.compose": "cibler la zone de rédaction",
"keyboard_shortcuts.description": "Description", "keyboard_shortcuts.description": "Description",
"keyboard_shortcuts.direct": "ouvrir la colonne des messages directs", "keyboard_shortcuts.direct": "ouvrir la colonne des messages privés",
"keyboard_shortcuts.down": "descendre dans la liste", "keyboard_shortcuts.down": "descendre dans la liste",
"keyboard_shortcuts.enter": "pour ouvrir le statut", "keyboard_shortcuts.enter": "pour ouvrir le statut",
"keyboard_shortcuts.favourite": "ajouter aux favoris", "keyboard_shortcuts.favourite": "ajouter aux favoris",
@ -237,7 +238,7 @@
"keyboard_shortcuts.muted": "ouvrir la liste des comptes masqués", "keyboard_shortcuts.muted": "ouvrir la liste des comptes masqués",
"keyboard_shortcuts.my_profile": "ouvrir votre profil", "keyboard_shortcuts.my_profile": "ouvrir votre profil",
"keyboard_shortcuts.notifications": "ouvrir la colonne de notifications", "keyboard_shortcuts.notifications": "ouvrir la colonne de notifications",
"keyboard_shortcuts.open_media": "ouvrir le média", "keyboard_shortcuts.open_media": "ouvrir les médias",
"keyboard_shortcuts.pinned": "ouvrir la liste des pouets épinglés", "keyboard_shortcuts.pinned": "ouvrir la liste des pouets épinglés",
"keyboard_shortcuts.profile": "ouvrir le profil de lauteur·rice", "keyboard_shortcuts.profile": "ouvrir le profil de lauteur·rice",
"keyboard_shortcuts.reply": "répondre", "keyboard_shortcuts.reply": "répondre",
@ -274,7 +275,7 @@
"navigation_bar.bookmarks": "Marque-pages", "navigation_bar.bookmarks": "Marque-pages",
"navigation_bar.community_timeline": "Fil public local", "navigation_bar.community_timeline": "Fil public local",
"navigation_bar.compose": "Rédiger un nouveau pouet", "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.discover": "Découvrir",
"navigation_bar.domain_blocks": "Domaines bloqués", "navigation_bar.domain_blocks": "Domaines bloqués",
"navigation_bar.edit_profile": "Modifier le profil", "navigation_bar.edit_profile": "Modifier le profil",
@ -375,7 +376,7 @@
"status.copy": "Copier le lien vers le pouet", "status.copy": "Copier le lien vers le pouet",
"status.delete": "Supprimer", "status.delete": "Supprimer",
"status.detailed_status": "Vue détaillée de la conversation", "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.embed": "Intégrer",
"status.favourite": "Ajouter aux favoris", "status.favourite": "Ajouter aux favoris",
"status.filtered": "Filtré", "status.filtered": "Filtré",
@ -410,9 +411,9 @@
"status.unpin": "Retirer du profil", "status.unpin": "Retirer du profil",
"suggestions.dismiss": "Rejeter la suggestion", "suggestions.dismiss": "Rejeter la suggestion",
"suggestions.header": "Vous pourriez être intéressé·e par…", "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.home": "Accueil",
"tabs_bar.local_timeline": "Fil public local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Chercher", "tabs_bar.search": "Chercher",
"time_remaining.days": "{number, plural, one {# day} other {# days}} restants", "time_remaining.days": "{number, plural, one {# day} other {# days}} restants",

View File

@ -1,18 +1,18 @@
import { import {
ANNOUNCEMENTS_FETCH_REQUEST,
ANNOUNCEMENTS_FETCH_SUCCESS,
ANNOUNCEMENTS_FETCH_FAIL,
ANNOUNCEMENTS_UPDATE,
ANNOUNCEMENTS_REACTION_UPDATE,
ANNOUNCEMENTS_REACTION_ADD_REQUEST,
ANNOUNCEMENTS_REACTION_ADD_FAIL,
ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
ANNOUNCEMENTS_TOGGLE_SHOW,
ANNOUNCEMENTS_DELETE, ANNOUNCEMENTS_DELETE,
ANNOUNCEMENTS_DISMISS_SUCCESS, ANNOUNCEMENTS_DISMISS_SUCCESS,
ANNOUNCEMENTS_FETCH_FAIL,
ANNOUNCEMENTS_FETCH_REQUEST,
ANNOUNCEMENTS_FETCH_SUCCESS,
ANNOUNCEMENTS_REACTION_ADD_FAIL,
ANNOUNCEMENTS_REACTION_ADD_REQUEST,
ANNOUNCEMENTS_REACTION_REMOVE_FAIL,
ANNOUNCEMENTS_REACTION_REMOVE_REQUEST,
ANNOUNCEMENTS_REACTION_UPDATE,
ANNOUNCEMENTS_TOGGLE_SHOW,
ANNOUNCEMENTS_UPDATE,
} from '../actions/announcements'; } from '../actions/announcements';
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; import { fromJS, List as ImmutableList, Map as ImmutableMap } from 'immutable';
const initialState = ImmutableMap({ const initialState = ImmutableMap({
items: ImmutableList(), items: ImmutableList(),

View File

@ -1,11 +1,23 @@
import { import {
COMPOSE_MOUNT,
COMPOSE_UNMOUNT,
COMPOSE_CHANGE, COMPOSE_CHANGE,
COMPOSE_COMPOSING_CHANGE,
COMPOSE_DIRECT,
COMPOSE_EMOJI_INSERT,
COMPOSE_MENTION,
COMPOSE_MOUNT,
COMPOSE_POLL_ADD,
COMPOSE_POLL_OPTION_ADD,
COMPOSE_POLL_OPTION_CHANGE,
COMPOSE_POLL_OPTION_REMOVE,
COMPOSE_POLL_REMOVE,
COMPOSE_POLL_SETTINGS_CHANGE,
COMPOSE_REPLY, COMPOSE_REPLY,
COMPOSE_REPLY_CANCEL, COMPOSE_REPLY_CANCEL,
COMPOSE_DIRECT, COMPOSE_RESET,
COMPOSE_MENTION, COMPOSE_SENSITIVITY_CHANGE,
COMPOSE_SPOILER_TEXT_CHANGE,
COMPOSE_SPOILERNESS_CHANGE,
COMPOSE_SUBMIT_FAIL,
COMPOSE_SUBMIT_REQUEST, COMPOSE_SUBMIT_REQUEST,
COMPOSE_SUBMIT_SUCCESS, COMPOSE_SUBMIT_SUCCESS,
COMPOSE_SUBMIT_FAIL, COMPOSE_SUBMIT_FAIL,
@ -22,28 +34,24 @@ import {
COMPOSE_SUGGESTIONS_READY, COMPOSE_SUGGESTIONS_READY,
COMPOSE_SUGGESTION_SELECT, COMPOSE_SUGGESTION_SELECT,
COMPOSE_SUGGESTION_TAGS_UPDATE, COMPOSE_SUGGESTION_TAGS_UPDATE,
COMPOSE_SUGGESTIONS_CLEAR,
COMPOSE_SUGGESTIONS_READY,
COMPOSE_TAG_HISTORY_UPDATE, COMPOSE_TAG_HISTORY_UPDATE,
COMPOSE_SENSITIVITY_CHANGE, COMPOSE_UNMOUNT,
COMPOSE_SPOILERNESS_CHANGE, COMPOSE_UPLOAD_CHANGE_FAIL,
COMPOSE_SPOILER_TEXT_CHANGE,
COMPOSE_VISIBILITY_CHANGE,
COMPOSE_COMPOSING_CHANGE,
COMPOSE_EMOJI_INSERT,
COMPOSE_UPLOAD_CHANGE_REQUEST, COMPOSE_UPLOAD_CHANGE_REQUEST,
COMPOSE_UPLOAD_CHANGE_SUCCESS, COMPOSE_UPLOAD_CHANGE_SUCCESS,
COMPOSE_UPLOAD_CHANGE_FAIL, COMPOSE_UPLOAD_FAIL,
COMPOSE_RESET, COMPOSE_UPLOAD_PROGRESS,
COMPOSE_POLL_ADD, COMPOSE_UPLOAD_REQUEST,
COMPOSE_POLL_REMOVE, COMPOSE_UPLOAD_SUCCESS,
COMPOSE_POLL_OPTION_ADD, COMPOSE_UPLOAD_UNDO,
COMPOSE_POLL_OPTION_CHANGE, COMPOSE_VISIBILITY_CHANGE,
COMPOSE_POLL_OPTION_REMOVE,
COMPOSE_POLL_SETTINGS_CHANGE,
} from '../actions/compose'; } from '../actions/compose';
import { TIMELINE_DELETE } from '../actions/timelines'; import { TIMELINE_DELETE } from '../actions/timelines';
import { STORE_HYDRATE } from '../actions/store'; import { STORE_HYDRATE } from '../actions/store';
import { REDRAFT } from '../actions/statuses'; import { REDRAFT } from '../actions/statuses';
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; import { fromJS, List as ImmutableList, Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
import uuid from '../uuid'; import uuid from '../uuid';
import { me } from '../initial_state'; import { me } from '../initial_state';
import { unescapeHTML } from '../utils/html'; import { unescapeHTML } from '../utils/html';
@ -92,7 +100,7 @@ function statusToTextMentions(state, status) {
} }
return set.union(status.get('mentions').filterNot(mention => mention.get('id') === me).map(mention => `@${mention.get('acct')} `)).join(''); return set.union(status.get('mentions').filterNot(mention => mention.get('id') === me).map(mention => `@${mention.get('acct')} `)).join('');
}; }
function clearAll(state) { function clearAll(state) {
return state.withMutations(map => { return state.withMutations(map => {
@ -108,7 +116,7 @@ function clearAll(state) {
map.set('poll', null); map.set('poll', null);
map.set('idempotencyKey', uuid()); map.set('idempotencyKey', uuid());
}); });
}; }
function appendMedia(state, media, file) { function appendMedia(state, media, file) {
const prevSize = state.get('media_attachments').size; const prevSize = state.get('media_attachments').size;
@ -127,7 +135,7 @@ function appendMedia(state, media, file) {
map.set('sensitive', true); map.set('sensitive', true);
} }
}); });
}; }
function removeMedia(state, mediaId) { function removeMedia(state, mediaId) {
const prevSize = state.get('media_attachments').size; const prevSize = state.get('media_attachments').size;
@ -140,7 +148,7 @@ function removeMedia(state, mediaId) {
map.set('sensitive', false); map.set('sensitive', false);
} }
}); });
}; }
const insertSuggestion = (state, position, token, completion, path) => { const insertSuggestion = (state, position, token, completion, path) => {
return state.withMutations(map => { return state.withMutations(map => {

View File

@ -1,22 +1,22 @@
import { import {
REBLOG_REQUEST,
REBLOG_FAIL,
FAVOURITE_REQUEST,
FAVOURITE_FAIL,
UNFAVOURITE_SUCCESS,
BOOKMARK_REQUEST,
BOOKMARK_FAIL, BOOKMARK_FAIL,
BOOKMARK_REQUEST,
FAVOURITE_FAIL,
FAVOURITE_REQUEST,
REBLOG_FAIL,
REBLOG_REQUEST,
UNFAVOURITE_SUCCESS,
} from '../actions/interactions'; } from '../actions/interactions';
import { import {
STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS,
STATUS_REVEAL,
STATUS_HIDE,
STATUS_COLLAPSE, STATUS_COLLAPSE,
STATUS_HIDE,
STATUS_MUTE_SUCCESS,
STATUS_REVEAL,
STATUS_UNMUTE_SUCCESS,
} from '../actions/statuses'; } from '../actions/statuses';
import { TIMELINE_DELETE } from '../actions/timelines'; import { TIMELINE_DELETE } from '../actions/timelines';
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; 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)); const importStatus = (state, status) => state.set(status.id, fromJS(status));
@ -34,7 +34,7 @@ const deleteStatus = (state, id, references) => {
const initialState = ImmutableMap(); const initialState = ImmutableMap();
export default function statuses(state = initialState, action) { export default function statuses(state = initialState, action) {
switch(action.type) { switch (action.type) {
case STATUS_IMPORT: case STATUS_IMPORT:
return importStatus(state, action.status); return importStatus(state, action.status);
case STATUSES_IMPORT: case STATUSES_IMPORT:

View File

@ -0,0 +1,121 @@
/** @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='&bull;';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);

View File

@ -35,7 +35,7 @@ delegate(document, '.filter-subset--with-select select', 'change', ({ target })
}); });
const onDomainBlockSeverityChange = (target) => { const onDomainBlockSeverityChange = (target) => {
const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media'); const rejectMediaDiv = document.querySelector('.input.with_label.domain_block_reject_media');
const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports'); const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports');
if (rejectMediaDiv) { if (rejectMediaDiv) {

View File

@ -0,0 +1,32 @@
//@import 'mastodon/variables';
@import 'bliss/variables';
@import 'bliss/mixins';
@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';

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,16 @@
$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;
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,724 @@
$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;
}
.administrate-stuff {
margin-right: 1ch;
color: $classic-highlight-color;
}

View File

@ -0,0 +1,209 @@
@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%;
}
}
.media-gallery {
margin-left: 0;
}
.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;
}
}
}
.clickable {
cursor: pointer;
&:hover {
color: $ui-highlight-color;
}
}
.pull-right {
float: right;
}
.pull-left {
float: left;
}

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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%);
}
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -0,0 +1,191 @@
.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%);
}
}
}
}
}
*|*:link, *|*:visited {
color: $ui-primary-color !important;
&:hover {
color: $ui-highlight-color !important;
background: $dark-text-color !important;
}
}
.getting-started__footer,
.links-started__footer {
display: block;
//position: fixed;
//bottom: 0;
//left: 1em;
//width: 268px;
z-index: 10;
font-size: 0.85em;
.fa {
margin-right: 1ch;
}
ul {
list-style-type: none;
}
li {
list-style-type: none;
padding: 0.15rem 0;
display: inline-block;
width: auto;
&.tag-element, a {
color: $ui-primary-color !important;
padding: 0.25em;
&:hover {
color: $ui-highlight-color !important;
background: $dark-text-color !important;
}
}
}
i {
margin: 0.5ch;
}
a {
margin-left: 2ch;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,135 @@
$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;
padding: 0.5em 1em;
}
.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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,31 @@
@import '../mastodon/variables';
// Commonly used web colors
$black: #000; // Black
$white: #fff; // White
$success-green: #6bbd77 !default; // Padua
$error-red: #d4839b !default; // Cerise
$warning-red: #528dc8 !default; // Sunset Orange
$gold-star: #98c6ff !default; // Dark Goldenrod
/************************
BEGIN bliss specific variables
************************/
// 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
// 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;
/************************
END bliss specific variables
************************/

View File

@ -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;
}
}

View File

View File

View File

View File

View File

@ -1,3 +1,3 @@
@import 'mastodon-light/variables'; @import 'mastodon-light/variables';
@import 'application'; @import 'bliss';
@import 'mastodon-light/diff'; @import 'mastodon-light/diff';

View File

@ -32,7 +32,7 @@ html {
.error-column, .error-column,
.regeneration-indicator { .regeneration-indicator {
background: $white; background: $white;
border: 1px solid lighten($ui-base-color, 8%); border: 1px solid $light-text-color !important;
border-top: 0; border-top: 0;
} }
@ -774,3 +774,123 @@ html {
.audio-player .video-player__time-total { .audio-player .video-player__time-total {
color: $primary-text-color; color: $primary-text-color;
} }
body,
.tabs-bar__wrapper {
background: $ui-base-lighter-color !important;
}
a {
color: $ui-highlight-color;
&.column-link {
color: $darker-text-color;
}
}
.search__input,
.compose-form .compose-form__buttons-wrapper {
background: $classic-secondary-color;
}
.search__input, {
&:focus {
color: $white;
}
}
.compose-form__publish-button-wrapper {
width: 100%;
.button {
display: block;
float: none;
}
}
input,
.simple_form input[type="text"], .simple_form input[type="number"], .simple_form input[type="email"], .simple_form input[type="password"], .simple_form textarea {
color: $white;
}
.links-started__footer {
a, li {
display: inline-block;
width: auto;
float: left;
margin-right: 2ch;
}
ul li {
list-style-type: none;
}
p {
float: left;
}
}
.admin-wrapper .sidebar ul a {
color: $white !important;
}
.flash-message {
background: $classic-highlight-color;
}
.table th, .table td {
background: $classic-secondary-color;
color: $classic-base-color;
}
.table > tbody > tr:nth-child(2n+1) > td, .table > tbody > tr:nth-child(2n+1) > th {
background: darken($classic-primary-color, 0.2);
}
.table {
border: 1px solid $classic-base-color;
}
.notification__filter-bar button, .notification__filter-bar a, .account__section-headline button, .account__section-headline a {
background: $classic-secondary-color;
color: $dark-text-color;
}
.notification__filter-bar button.active, .notification__filter-bar a.active, .account__section-headline button.active, .account__section-headline a.active {
color: $ui-highlight-color;
}
.status.status-direct:not(.read) {
background: $ui-base-lighter-color;
}
.reply-indicator__content .status__content__spoiler-link, .status__content .status__content__spoiler-link {
color: $white;
&:hover {
color: $ui-highlight-color;
background: $ui-primary-color;
}
}
.dashboard__counters div a {
color: $white !important;
}
.status__wrapper {
&:hover {
background: $ui-base-lighter-color;
}
}
body.theme-mastodon-light,
.tabs-bar__wrapper {
background: $ui-base-lighter-color !important;
}
.recommended {
color: $white !important;
}

View File

@ -1,10 +1,10 @@
// Dependent colors // Dependent colors
$black: #000000; $black: #000000;
$white: #ffffff; $white: #eeeeff;
$classic-base-color: #282c37; $classic-base-color: #282c37;
$classic-primary-color: #9baec8; $classic-primary-color: #9baec8;
$classic-secondary-color: #d9e1e8; $classic-secondary-color: #d9e1f8;
$classic-highlight-color: #2b90d9; $classic-highlight-color: #2b90d9;
// Differences // Differences
@ -13,10 +13,10 @@ $success-green: lighten(#3c754d, 8%);
$base-overlay-background: $white !default; $base-overlay-background: $white !default;
$valid-value-color: $success-green !default; $valid-value-color: $success-green !default;
$ui-base-color: $classic-secondary-color !default; $ui-base-color: $base-overlay-background !default;
$ui-base-lighter-color: #b0c0cf; $ui-base-lighter-color: #dedeff;
$ui-primary-color: #9bcbed; $ui-primary-color: #9bcbed;
$ui-secondary-color: $classic-base-color !default; $ui-secondary-color: $classic-secondary-color !default;
$ui-highlight-color: #2b90d9; $ui-highlight-color: #2b90d9;
$primary-text-color: $black !default; $primary-text-color: $black !default;

View File

@ -1010,8 +1010,12 @@
} }
@keyframes fade { @keyframes fade {
0% { opacity: 0; } 0% {
100% { opacity: 1; } opacity: 0;
}
100% {
opacity: 1;
}
} }
opacity: 1; opacity: 1;
@ -1161,7 +1165,7 @@
.status__action-bar { .status__action-bar {
align-items: center; align-items: center;
display: flex; display: flex;
margin-top: 8px; margin-left: 2.4em;
&__counter { &__counter {
display: inline-flex; display: inline-flex;
@ -2258,7 +2262,7 @@ a.account__display-name {
.scrollable { .scrollable {
overflow: visible; overflow: visible;
@supports(display: grid) { @supports (display: grid) {
contain: content; contain: content;
} }
} }
@ -2600,7 +2604,7 @@ a.account__display-name {
overflow-y: auto; overflow-y: auto;
} }
@supports(display: grid) { // hack to fix Chrome <57 @supports (display: grid) { // hack to fix Chrome <57
contain: strict; contain: strict;
} }
@ -2617,7 +2621,7 @@ a.account__display-name {
} }
.scrollable.fullscreen { .scrollable.fullscreen {
@supports(display: grid) { // hack to fix Chrome <57 @supports (display: grid) { // hack to fix Chrome <57
contain: none; contain: none;
} }
} }
@ -3600,9 +3604,15 @@ a.status-card.compact:hover {
} }
@keyframes loader-label { @keyframes loader-label {
0% { opacity: 0.25; } 0% {
30% { opacity: 1; } opacity: 0.25;
100% { opacity: 0.25; } }
30% {
opacity: 1;
}
100% {
opacity: 0.25;
}
} }
.video-error-cover { .video-error-cover {
@ -3891,7 +3901,7 @@ a.status-card.compact:hover {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@supports(display: grid) { // hack to fix Chrome <57 @supports (display: grid) { // hack to fix Chrome <57
contain: strict; contain: strict;
} }
@ -5301,6 +5311,7 @@ a.status-card.compact:hover {
overflow: hidden; overflow: hidden;
position: absolute; position: absolute;
} }
/* End Media Gallery */ /* End Media Gallery */
.detailed, .detailed,
@ -6033,9 +6044,15 @@ noscript {
} }
@keyframes flicker { @keyframes flicker {
0% { opacity: 1; } 0% {
30% { opacity: 0.75; } opacity: 1;
100% { opacity: 1; } }
30% {
opacity: 0.75;
}
100% {
opacity: 1;
}
} }
@media screen and (max-width: 630px) and (max-height: 400px) { @media screen and (max-width: 630px) and (max-height: 400px) {
@ -6068,13 +6085,13 @@ noscript {
& > .icon-button.close { & > .icon-button.close {
will-change: opacity transform; will-change: opacity transform;
transition: opacity $duration * 0.5 $delay, transition: opacity $duration * 0.5 $delay,
transform $duration $delay; transform $duration $delay;
} }
& > .compose__action-bar .icon-button { & > .compose__action-bar .icon-button {
will-change: opacity transform; will-change: opacity transform;
transition: opacity $duration * 0.5 $delay + $duration * 0.5, transition: opacity $duration * 0.5 $delay + $duration * 0.5,
transform $duration $delay; transform $duration $delay;
} }
} }
} }
@ -6760,7 +6777,7 @@ noscript {
.conversation { .conversation {
display: flex; display: flex;
border-bottom: 1px solid lighten($ui-base-color, 8%); border-bottom: 1px solid lighten($ui-base-color, 8%);
padding: 5px; padding: 1em;
padding-bottom: 0; padding-bottom: 0;
&:focus { &:focus {

View File

@ -21,7 +21,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
svg { > svg {
fill: $primary-text-color; fill: $primary-text-color;
height: 42px; height: 42px;
margin-right: 10px; margin-right: 10px;

View File

@ -84,6 +84,7 @@ code {
position: absolute; position: absolute;
margin: 0 4px; margin: 0 4px;
margin-top: -2px; margin-top: -2px;
color: $white;
} }
} }
} }

View File

@ -1,18 +1,18 @@
// Commonly used web colors // Commonly used web colors
$black: #000000; // Black $black: #000000; // Black
$white: #ffffff; // White $white: #ffffff; // White
$success-green: #79bd9a !default; // Padua $success-green: #79bd9a !default; // Padua
$error-red: #df405a !default; // Cerise $error-red: #df405a !default; // Cerise
$warning-red: #ff5050 !default; // Sunset Orange $warning-red: #ff5050 !default; // Sunset Orange
$gold-star: #ca8f04 !default; // Dark Goldenrod $gold-star: #ca8f04 !default; // Dark Goldenrod
$red-bookmark: $warning-red; $red-bookmark: $warning-red;
// Values from the classic Mastodon UI // Values from the classic Mastodon UI
$classic-base-color: #282c37; // Midnight Express $classic-base-color: #282c37; // Midnight Express
$classic-primary-color: #9baec8; // Echo Blue $classic-primary-color: #9baec8; // Echo Blue
$classic-secondary-color: #d9e1e8; // Pattens Blue $classic-secondary-color: #d9e1e8; // Pattens Blue
$classic-highlight-color: #2b90d9; // Summer Sky $classic-highlight-color: #2b90d9; // Summer Sky
// Variables for defaults in UI // Variables for defaults in UI
$base-shadow-color: $black !default; $base-shadow-color: $black !default;
@ -23,10 +23,10 @@ $valid-value-color: $success-green !default;
$error-value-color: $error-red !default; $error-value-color: $error-red !default;
// Tell UI to use selected colors // Tell UI to use selected colors
$ui-base-color: $classic-base-color !default; // Darkest $ui-base-color: $classic-base-color !default; // Darkest
$ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest $ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
$ui-primary-color: $classic-primary-color !default; // Lighter $ui-primary-color: $classic-primary-color !default; // Lighter
$ui-secondary-color: $classic-secondary-color !default; // Lightest $ui-secondary-color: $classic-secondary-color !default; // Lightest
$ui-highlight-color: $classic-highlight-color !default; $ui-highlight-color: $classic-highlight-color !default;
// Variables for texts // Variables for texts
@ -41,6 +41,20 @@ $inverted-text-color: $ui-base-color !default;
$lighter-text-color: $ui-base-lighter-color !default; $lighter-text-color: $ui-base-lighter-color !default;
$light-text-color: $ui-primary-color !default; $light-text-color: $ui-primary-color !default;
// 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 // Language codes that uses CJK fonts
$cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW; $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;

16
app/models/user_group.rb Normal file
View File

@ -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

View File

@ -0,0 +1,3 @@
class UserGroupSerializer < ActiveModel::Serializer
attributes :id, :name, :createAt, :visibility, :members
end

View File

@ -22,3 +22,11 @@
- else - else
= table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}") = 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) = 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")

View File

@ -49,7 +49,11 @@
%th= t('admin.accounts.role') %th= t('admin.accounts.role')
%th= t('admin.accounts.most_recent_ip') %th= t('admin.accounts.most_recent_ip')
%th= t('admin.accounts.most_recent_activity') %th= t('admin.accounts.most_recent_activity')
%th %th links
%th statuses
%th following
%th followers
%th nuke
%tbody %tbody
= render @accounts = render @accounts

View File

@ -11,7 +11,8 @@
%link{ rel: 'dns-prefetch', href: storage_host }/ %link{ rel: 'dns-prefetch', href: storage_host }/
= stylesheet_pack_tag 'common', media: 'all' = stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all' -#= stylesheet_pack_tag 'bliss', media: 'all'
-#= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
= render_initial_state = render_initial_state

View File

@ -6,7 +6,7 @@
%title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ') %title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ')
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/ %meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
= stylesheet_pack_tag 'common', media: 'all' = stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all' -#= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'error', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag 'error', integrity: true, crossorigin: 'anonymous'
%body.error %body.error

View File

@ -3,7 +3,8 @@
- content_for :header_tags do - content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
.well
%strong= @accounts.length
.filters .filters
.filter-subset .filter-subset
%strong= t 'relationships.relationship' %strong= t 'relationships.relationship'

View File

@ -9,7 +9,7 @@
.fields-group.fields-row__column.fields-row__column-6 .fields-group.fields-row__column.fields-row__column-6
= f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale, hint: false = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }, selected: I18n.locale, hint: false
.fields-group.fields-row__column.fields-row__column-6 .fields-group.fields-row__column.fields-row__column-6
= f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false, hint: false = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false, hint: true
- unless I18n.locale == :en - unless I18n.locale == :en
.flash-message.translation-prompt .flash-message.translation-prompt
@ -62,3 +62,4 @@
.actions .actions
= f.button :button, t('generic.save_changes'), type: :submit = f.button :button, t('generic.save_changes'), type: :submit

View File

@ -24,7 +24,7 @@
%hr.spacer/ %hr.spacer/
.fields-group .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 .fields-group
= f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot') = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')

Some files were not shown because too many files have changed in this diff Show More