mirror of
https://framagit.org/tykayn/mastodon.git
synced 2023-08-25 08:33:12 +02:00
merge messaging
This commit is contained in:
commit
519c9f417e
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@
|
||||
/db/*.sqlite3-journal
|
||||
|
||||
# Ignore all logfiles and tempfiles.
|
||||
.eslintcache
|
||||
/log/*
|
||||
!/log/.keep
|
||||
/tmp
|
||||
|
@ -3,6 +3,7 @@ Authors
|
||||
|
||||
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
||||
and provided thanks to the work of the following contributors:
|
||||
* [Tykayn](https://framagit.org/tykayn) for the Bliss version
|
||||
|
||||
* [Gargron](https://github.com/Gargron)
|
||||
* [ThibG](https://github.com/ThibG)
|
||||
|
5
Gemfile
5
Gemfile
@ -6,7 +6,8 @@ ruby '>= 2.4.0', '< 2.7.0'
|
||||
gem 'pkg-config', '~> 1.4'
|
||||
|
||||
gem 'puma', '~> 4.3'
|
||||
gem 'rails', '~> 5.2.3'
|
||||
gem 'rails', '~> 5.2.4'
|
||||
gem 'sprockets', '~> 3.7'
|
||||
gem 'thor', '~> 0.20'
|
||||
|
||||
gem 'hamlit-rails', '~> 0.2'
|
||||
@ -122,7 +123,7 @@ group :test do
|
||||
gem 'rspec-sidekiq', '~> 3.0'
|
||||
gem 'simplecov', '~> 0.17', require: false
|
||||
gem 'webmock', '~> 3.7'
|
||||
gem 'parallel_tests', '~> 2.29'
|
||||
gem 'parallel_tests', '~> 2.30'
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
97
Gemfile.lock
97
Gemfile.lock
@ -44,25 +44,25 @@ GIT
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actioncable (5.2.4)
|
||||
actionpack (= 5.2.4)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activejob (= 5.2.3)
|
||||
actionmailer (5.2.4)
|
||||
actionpack (= 5.2.4)
|
||||
actionview (= 5.2.4)
|
||||
activejob (= 5.2.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
actionpack (5.2.4)
|
||||
actionview (= 5.2.4)
|
||||
activesupport (= 5.2.4)
|
||||
rack (~> 2.0)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
actionview (5.2.4)
|
||||
activesupport (= 5.2.4)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
@ -73,20 +73,20 @@ GEM
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
active_record_query_trace (1.7)
|
||||
activejob (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
activejob (5.2.4)
|
||||
activesupport (= 5.2.4)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
activerecord (5.2.3)
|
||||
activemodel (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
activemodel (5.2.4)
|
||||
activesupport (= 5.2.4)
|
||||
activerecord (5.2.4)
|
||||
activemodel (= 5.2.4)
|
||||
activesupport (= 5.2.4)
|
||||
arel (>= 9.0)
|
||||
activestorage (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
activerecord (= 5.2.3)
|
||||
activestorage (5.2.4)
|
||||
actionpack (= 5.2.4)
|
||||
activerecord (= 5.2.4)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (5.2.3)
|
||||
activesupport (5.2.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
@ -218,7 +218,7 @@ GEM
|
||||
docile (1.3.2)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (5.2.2)
|
||||
doorkeeper (5.2.3)
|
||||
railties (>= 5)
|
||||
dotenv (2.7.5)
|
||||
dotenv-rails (2.7.5)
|
||||
@ -238,9 +238,9 @@ GEM
|
||||
erubi (1.9.0)
|
||||
et-orbi (1.1.6)
|
||||
tzinfo
|
||||
excon (0.62.0)
|
||||
excon (0.71.0)
|
||||
fabrication (2.21.0)
|
||||
faker (2.8.0)
|
||||
faker (2.8.1)
|
||||
i18n (>= 1.6, < 1.8)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
@ -324,7 +324,7 @@ GEM
|
||||
jmespath (1.4.0)
|
||||
json (2.2.0)
|
||||
json-canonicalization (0.1.0)
|
||||
json-ld-preloaded (3.0.4)
|
||||
json-ld-preloaded (3.0.6)
|
||||
json-ld (~> 3.0)
|
||||
multi_json (~> 1.12)
|
||||
rdf (~> 3.0)
|
||||
@ -380,7 +380,7 @@ GEM
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.13.0)
|
||||
msgpack (1.3.1)
|
||||
multi_json (1.13.1)
|
||||
multi_json (1.14.1)
|
||||
multipart-post (2.1.1)
|
||||
necromancer (0.5.1)
|
||||
net-ldap (0.16.2)
|
||||
@ -424,7 +424,7 @@ GEM
|
||||
av (~> 0.9.0)
|
||||
paperclip (>= 2.5.2)
|
||||
parallel (1.19.1)
|
||||
parallel_tests (2.29.2)
|
||||
parallel_tests (2.30.0)
|
||||
parallel
|
||||
parser (2.6.5.0)
|
||||
ast (~> 2.4.0)
|
||||
@ -469,18 +469,18 @@ GEM
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.3)
|
||||
actioncable (= 5.2.3)
|
||||
actionmailer (= 5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
actionview (= 5.2.3)
|
||||
activejob (= 5.2.3)
|
||||
activemodel (= 5.2.3)
|
||||
activerecord (= 5.2.3)
|
||||
activestorage (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
rails (5.2.4)
|
||||
actioncable (= 5.2.4)
|
||||
actionmailer (= 5.2.4)
|
||||
actionpack (= 5.2.4)
|
||||
actionview (= 5.2.4)
|
||||
activejob (= 5.2.4)
|
||||
activemodel (= 5.2.4)
|
||||
activerecord (= 5.2.4)
|
||||
activestorage (= 5.2.4)
|
||||
activesupport (= 5.2.4)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.3)
|
||||
railties (= 5.2.4)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
@ -496,15 +496,15 @@ GEM
|
||||
railties (>= 5.0, < 6)
|
||||
rails-settings-cached (0.6.6)
|
||||
rails (>= 4.2.0)
|
||||
railties (5.2.3)
|
||||
actionpack (= 5.2.3)
|
||||
activesupport (= 5.2.3)
|
||||
railties (5.2.4)
|
||||
actionpack (= 5.2.4)
|
||||
activesupport (= 5.2.4)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
rake (13.0.1)
|
||||
rdf (3.0.12)
|
||||
rdf (3.0.13)
|
||||
hamster (~> 3.0)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
rdf-normalize (0.3.3)
|
||||
@ -615,7 +615,7 @@ GEM
|
||||
sshkit (1.20.0)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
stackprof (0.2.13)
|
||||
stackprof (0.2.14)
|
||||
statsd-ruby (1.4.0)
|
||||
stoplight (2.2.0)
|
||||
streamio-ffmpeg (3.0.2)
|
||||
@ -667,9 +667,9 @@ GEM
|
||||
webpush (0.3.8)
|
||||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
websocket-driver (0.7.0)
|
||||
websocket-driver (0.7.1)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.3)
|
||||
websocket-extensions (0.1.4)
|
||||
wisper (2.0.1)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
@ -753,7 +753,7 @@ DEPENDENCIES
|
||||
paperclip (~> 6.0)
|
||||
paperclip-av-transcoder (~> 0.6)
|
||||
parallel (~> 1.19)
|
||||
parallel_tests (~> 2.29)
|
||||
parallel_tests (~> 2.30)
|
||||
parslet
|
||||
pg (~> 1.1)
|
||||
pghero (~> 2.4)
|
||||
@ -767,7 +767,7 @@ DEPENDENCIES
|
||||
pundit (~> 2.1)
|
||||
rack-attack (~> 6.2)
|
||||
rack-cors (~> 1.1)
|
||||
rails (~> 5.2.3)
|
||||
rails (~> 5.2.4)
|
||||
rails-controller-testing (~> 1.0)
|
||||
rails-i18n (~> 5.1)
|
||||
rails-settings-cached (~> 0.6)
|
||||
@ -789,6 +789,7 @@ DEPENDENCIES
|
||||
simple-navigation (~> 4.1)
|
||||
simple_form (~> 5.0)
|
||||
simplecov (~> 0.17)
|
||||
sprockets (~> 3.7)
|
||||
sprockets-rails (~> 3.2)
|
||||
stackprof
|
||||
stoplight (~> 2.2.0)
|
||||
|
20
README.md
20
README.md
@ -1,5 +1,23 @@
|
||||
![Mastodon](https://i.imgur.com/NhZc40l.png)
|
||||
========
|
||||
# CipherBliss version of Mastodon
|
||||
|
||||
|
||||
features:
|
||||
|
||||
* liks to free tools
|
||||
* snow fall during the end of the yar.
|
||||
|
||||
## Todo
|
||||
* proper responsive columning
|
||||
* **instant Messaging** in the web front end.
|
||||
|
||||
uses the api to get accounts
|
||||
```bash
|
||||
GET /api/v1/accounts/[id of my account]/following
|
||||
```
|
||||
|
||||
========
|
||||
|
||||
[![GitHub release](https://img.shields.io/github/release/tootsuite/mastodon.svg)][releases]
|
||||
[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci]
|
||||
@ -72,7 +90,7 @@ Mastodon acts as an OAuth2 provider so 3rd party apps can use the REST and Strea
|
||||
- **Ruby** 2.4+
|
||||
- **Node.js** 8+
|
||||
|
||||
The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/administration/installation/) is available in the documentation.
|
||||
The repository includes deployment configurations for **Docker and docker-compose**, but also a few specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. The [**stand-alone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
|
||||
|
||||
A **Vagrant** configuration is included for development purposes.
|
||||
|
||||
|
@ -4,75 +4,75 @@ import { importAccount, importFetchedAccount, importFetchedAccounts } from './im
|
||||
|
||||
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
|
||||
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
|
||||
export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL';
|
||||
export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL';
|
||||
|
||||
export const ACCOUNT_FOLLOW_REQUEST = 'ACCOUNT_FOLLOW_REQUEST';
|
||||
export const ACCOUNT_FOLLOW_SUCCESS = 'ACCOUNT_FOLLOW_SUCCESS';
|
||||
export const ACCOUNT_FOLLOW_FAIL = 'ACCOUNT_FOLLOW_FAIL';
|
||||
export const ACCOUNT_FOLLOW_FAIL = 'ACCOUNT_FOLLOW_FAIL';
|
||||
|
||||
export const ACCOUNT_UNFOLLOW_REQUEST = 'ACCOUNT_UNFOLLOW_REQUEST';
|
||||
export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS';
|
||||
export const ACCOUNT_UNFOLLOW_FAIL = 'ACCOUNT_UNFOLLOW_FAIL';
|
||||
export const ACCOUNT_UNFOLLOW_FAIL = 'ACCOUNT_UNFOLLOW_FAIL';
|
||||
|
||||
export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST';
|
||||
export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS';
|
||||
export const ACCOUNT_BLOCK_FAIL = 'ACCOUNT_BLOCK_FAIL';
|
||||
export const ACCOUNT_BLOCK_FAIL = 'ACCOUNT_BLOCK_FAIL';
|
||||
|
||||
export const ACCOUNT_UNBLOCK_REQUEST = 'ACCOUNT_UNBLOCK_REQUEST';
|
||||
export const ACCOUNT_UNBLOCK_SUCCESS = 'ACCOUNT_UNBLOCK_SUCCESS';
|
||||
export const ACCOUNT_UNBLOCK_FAIL = 'ACCOUNT_UNBLOCK_FAIL';
|
||||
export const ACCOUNT_UNBLOCK_FAIL = 'ACCOUNT_UNBLOCK_FAIL';
|
||||
|
||||
export const ACCOUNT_MUTE_REQUEST = 'ACCOUNT_MUTE_REQUEST';
|
||||
export const ACCOUNT_MUTE_SUCCESS = 'ACCOUNT_MUTE_SUCCESS';
|
||||
export const ACCOUNT_MUTE_FAIL = 'ACCOUNT_MUTE_FAIL';
|
||||
export const ACCOUNT_MUTE_FAIL = 'ACCOUNT_MUTE_FAIL';
|
||||
|
||||
export const ACCOUNT_UNMUTE_REQUEST = 'ACCOUNT_UNMUTE_REQUEST';
|
||||
export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS';
|
||||
export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL';
|
||||
export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL';
|
||||
|
||||
export const ACCOUNT_PIN_REQUEST = 'ACCOUNT_PIN_REQUEST';
|
||||
export const ACCOUNT_PIN_SUCCESS = 'ACCOUNT_PIN_SUCCESS';
|
||||
export const ACCOUNT_PIN_FAIL = 'ACCOUNT_PIN_FAIL';
|
||||
export const ACCOUNT_PIN_FAIL = 'ACCOUNT_PIN_FAIL';
|
||||
|
||||
export const ACCOUNT_UNPIN_REQUEST = 'ACCOUNT_UNPIN_REQUEST';
|
||||
export const ACCOUNT_UNPIN_SUCCESS = 'ACCOUNT_UNPIN_SUCCESS';
|
||||
export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL';
|
||||
export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL';
|
||||
|
||||
export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST';
|
||||
export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS';
|
||||
export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL';
|
||||
export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL';
|
||||
|
||||
export const FOLLOWERS_EXPAND_REQUEST = 'FOLLOWERS_EXPAND_REQUEST';
|
||||
export const FOLLOWERS_EXPAND_SUCCESS = 'FOLLOWERS_EXPAND_SUCCESS';
|
||||
export const FOLLOWERS_EXPAND_FAIL = 'FOLLOWERS_EXPAND_FAIL';
|
||||
export const FOLLOWERS_EXPAND_FAIL = 'FOLLOWERS_EXPAND_FAIL';
|
||||
|
||||
export const FOLLOWING_FETCH_REQUEST = 'FOLLOWING_FETCH_REQUEST';
|
||||
export const FOLLOWING_FETCH_SUCCESS = 'FOLLOWING_FETCH_SUCCESS';
|
||||
export const FOLLOWING_FETCH_FAIL = 'FOLLOWING_FETCH_FAIL';
|
||||
export const FOLLOWING_FETCH_FAIL = 'FOLLOWING_FETCH_FAIL';
|
||||
|
||||
export const FOLLOWING_EXPAND_REQUEST = 'FOLLOWING_EXPAND_REQUEST';
|
||||
export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS';
|
||||
export const FOLLOWING_EXPAND_FAIL = 'FOLLOWING_EXPAND_FAIL';
|
||||
export const FOLLOWING_EXPAND_FAIL = 'FOLLOWING_EXPAND_FAIL';
|
||||
|
||||
export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST';
|
||||
export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS';
|
||||
export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL';
|
||||
export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL';
|
||||
|
||||
export const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST';
|
||||
export const FOLLOW_REQUESTS_FETCH_SUCCESS = 'FOLLOW_REQUESTS_FETCH_SUCCESS';
|
||||
export const FOLLOW_REQUESTS_FETCH_FAIL = 'FOLLOW_REQUESTS_FETCH_FAIL';
|
||||
export const FOLLOW_REQUESTS_FETCH_FAIL = 'FOLLOW_REQUESTS_FETCH_FAIL';
|
||||
|
||||
export const FOLLOW_REQUESTS_EXPAND_REQUEST = 'FOLLOW_REQUESTS_EXPAND_REQUEST';
|
||||
export const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS';
|
||||
export const FOLLOW_REQUESTS_EXPAND_FAIL = 'FOLLOW_REQUESTS_EXPAND_FAIL';
|
||||
export const FOLLOW_REQUESTS_EXPAND_FAIL = 'FOLLOW_REQUESTS_EXPAND_FAIL';
|
||||
|
||||
export const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST';
|
||||
export const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS';
|
||||
export const FOLLOW_REQUEST_AUTHORIZE_FAIL = 'FOLLOW_REQUEST_AUTHORIZE_FAIL';
|
||||
export const FOLLOW_REQUEST_AUTHORIZE_FAIL = 'FOLLOW_REQUEST_AUTHORIZE_FAIL';
|
||||
|
||||
export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST';
|
||||
export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS';
|
||||
export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL';
|
||||
export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL';
|
||||
|
||||
function getFromDB(dispatch, getState, index, id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -106,7 +106,7 @@ export function fetchAccount(id) {
|
||||
dispatch,
|
||||
getState,
|
||||
db.transaction('accounts', 'read').objectStore('accounts').index('id'),
|
||||
id
|
||||
id,
|
||||
).then(() => db.close(), error => {
|
||||
db.close();
|
||||
throw error;
|
||||
@ -118,29 +118,29 @@ export function fetchAccount(id) {
|
||||
dispatch(fetchAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchAccountSuccess() {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_SUCCESS,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchAccountFail(id, error) {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_FAIL,
|
||||
type : ACCOUNT_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccount(id, reblogs = true) {
|
||||
return (dispatch, getState) => {
|
||||
@ -155,7 +155,7 @@ export function followAccount(id, reblogs = true) {
|
||||
dispatch(followAccountFail(error, locked));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -167,59 +167,59 @@ export function unfollowAccount(id) {
|
||||
dispatch(unfollowAccountFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccountRequest(id, locked) {
|
||||
return {
|
||||
type: ACCOUNT_FOLLOW_REQUEST,
|
||||
type : ACCOUNT_FOLLOW_REQUEST,
|
||||
id,
|
||||
locked,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccountSuccess(relationship, alreadyFollowing) {
|
||||
return {
|
||||
type: ACCOUNT_FOLLOW_SUCCESS,
|
||||
type : ACCOUNT_FOLLOW_SUCCESS,
|
||||
relationship,
|
||||
alreadyFollowing,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccountFail(error, locked) {
|
||||
return {
|
||||
type: ACCOUNT_FOLLOW_FAIL,
|
||||
type : ACCOUNT_FOLLOW_FAIL,
|
||||
error,
|
||||
locked,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_UNFOLLOW_REQUEST,
|
||||
type : ACCOUNT_UNFOLLOW_REQUEST,
|
||||
id,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccountSuccess(relationship, statuses) {
|
||||
return {
|
||||
type: ACCOUNT_UNFOLLOW_SUCCESS,
|
||||
type : ACCOUNT_UNFOLLOW_SUCCESS,
|
||||
relationship,
|
||||
statuses,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_UNFOLLOW_FAIL,
|
||||
type : ACCOUNT_UNFOLLOW_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function blockAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -232,7 +232,7 @@ export function blockAccount(id) {
|
||||
dispatch(blockAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unblockAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -244,14 +244,14 @@ export function unblockAccount(id) {
|
||||
dispatch(unblockAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function blockAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_BLOCK_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function blockAccountSuccess(relationship, statuses) {
|
||||
return {
|
||||
@ -259,36 +259,35 @@ export function blockAccountSuccess(relationship, statuses) {
|
||||
relationship,
|
||||
statuses,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function blockAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_BLOCK_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unblockAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_UNBLOCK_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unblockAccountSuccess(relationship) {
|
||||
return {
|
||||
type: ACCOUNT_UNBLOCK_SUCCESS,
|
||||
relationship,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unblockAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_UNBLOCK_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export function muteAccount(id, notifications) {
|
||||
return (dispatch, getState) => {
|
||||
@ -301,7 +300,7 @@ export function muteAccount(id, notifications) {
|
||||
dispatch(muteAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -313,14 +312,14 @@ export function unmuteAccount(id) {
|
||||
dispatch(unmuteAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_MUTE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteAccountSuccess(relationship, statuses) {
|
||||
return {
|
||||
@ -328,36 +327,35 @@ export function muteAccountSuccess(relationship, statuses) {
|
||||
relationship,
|
||||
statuses,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_MUTE_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_UNMUTE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteAccountSuccess(relationship) {
|
||||
return {
|
||||
type: ACCOUNT_UNMUTE_SUCCESS,
|
||||
relationship,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_UNMUTE_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export function fetchFollowers(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -373,14 +371,14 @@ export function fetchFollowers(id) {
|
||||
dispatch(fetchFollowersFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowersRequest(id) {
|
||||
return {
|
||||
type: FOLLOWERS_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowersSuccess(id, accounts, next) {
|
||||
return {
|
||||
@ -389,7 +387,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowersFail(id, error) {
|
||||
return {
|
||||
@ -397,7 +395,7 @@ export function fetchFollowersFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowers(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -419,14 +417,14 @@ export function expandFollowers(id) {
|
||||
dispatch(expandFollowersFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowersRequest(id) {
|
||||
return {
|
||||
type: FOLLOWERS_EXPAND_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowersSuccess(id, accounts, next) {
|
||||
return {
|
||||
@ -435,7 +433,7 @@ export function expandFollowersSuccess(id, accounts, next) {
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowersFail(id, error) {
|
||||
return {
|
||||
@ -443,7 +441,7 @@ export function expandFollowersFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowing(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -459,14 +457,14 @@ export function fetchFollowing(id) {
|
||||
dispatch(fetchFollowingFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowingRequest(id) {
|
||||
return {
|
||||
type: FOLLOWING_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowingSuccess(id, accounts, next) {
|
||||
return {
|
||||
@ -475,7 +473,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowingFail(id, error) {
|
||||
return {
|
||||
@ -483,7 +481,7 @@ export function fetchFollowingFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowing(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -505,14 +503,14 @@ export function expandFollowing(id) {
|
||||
dispatch(expandFollowingFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowingRequest(id) {
|
||||
return {
|
||||
type: FOLLOWING_EXPAND_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowingSuccess(id, accounts, next) {
|
||||
return {
|
||||
@ -521,7 +519,7 @@ export function expandFollowingSuccess(id, accounts, next) {
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowingFail(id, error) {
|
||||
return {
|
||||
@ -529,7 +527,7 @@ export function expandFollowingFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationships(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
@ -548,31 +546,31 @@ export function fetchRelationships(accountIds) {
|
||||
dispatch(fetchRelationshipsFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationshipsRequest(ids) {
|
||||
return {
|
||||
type: RELATIONSHIPS_FETCH_REQUEST,
|
||||
type : RELATIONSHIPS_FETCH_REQUEST,
|
||||
ids,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationshipsSuccess(relationships) {
|
||||
return {
|
||||
type: RELATIONSHIPS_FETCH_SUCCESS,
|
||||
type : RELATIONSHIPS_FETCH_SUCCESS,
|
||||
relationships,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationshipsFail(error) {
|
||||
return {
|
||||
type: RELATIONSHIPS_FETCH_FAIL,
|
||||
type : RELATIONSHIPS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowRequests() {
|
||||
return (dispatch, getState) => {
|
||||
@ -584,13 +582,13 @@ export function fetchFollowRequests() {
|
||||
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => dispatch(fetchFollowRequestsFail(error)));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowRequestsRequest() {
|
||||
return {
|
||||
type: FOLLOW_REQUESTS_FETCH_REQUEST,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowRequestsSuccess(accounts, next) {
|
||||
return {
|
||||
@ -598,14 +596,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowRequestsFail(error) {
|
||||
return {
|
||||
type: FOLLOW_REQUESTS_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowRequests() {
|
||||
return (dispatch, getState) => {
|
||||
@ -623,13 +621,13 @@ export function expandFollowRequests() {
|
||||
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => dispatch(expandFollowRequestsFail(error)));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowRequestsRequest() {
|
||||
return {
|
||||
type: FOLLOW_REQUESTS_EXPAND_REQUEST,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowRequestsSuccess(accounts, next) {
|
||||
return {
|
||||
@ -637,14 +635,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowRequestsFail(error) {
|
||||
return {
|
||||
type: FOLLOW_REQUESTS_EXPAND_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function authorizeFollowRequest(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -655,21 +653,21 @@ export function authorizeFollowRequest(id) {
|
||||
.then(() => dispatch(authorizeFollowRequestSuccess(id)))
|
||||
.catch(error => dispatch(authorizeFollowRequestFail(id, error)));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function authorizeFollowRequestRequest(id) {
|
||||
return {
|
||||
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function authorizeFollowRequestSuccess(id) {
|
||||
return {
|
||||
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function authorizeFollowRequestFail(id, error) {
|
||||
return {
|
||||
@ -677,8 +675,7 @@ export function authorizeFollowRequestFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export function rejectFollowRequest(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -689,21 +686,21 @@ export function rejectFollowRequest(id) {
|
||||
.then(() => dispatch(rejectFollowRequestSuccess(id)))
|
||||
.catch(error => dispatch(rejectFollowRequestFail(id, error)));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function rejectFollowRequestRequest(id) {
|
||||
return {
|
||||
type: FOLLOW_REQUEST_REJECT_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function rejectFollowRequestSuccess(id) {
|
||||
return {
|
||||
type: FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function rejectFollowRequestFail(id, error) {
|
||||
return {
|
||||
@ -711,7 +708,7 @@ export function rejectFollowRequestFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -723,7 +720,7 @@ export function pinAccount(id) {
|
||||
dispatch(pinAccountFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -735,46 +732,46 @@ export function unpinAccount(id) {
|
||||
dispatch(unpinAccountFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_PIN_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinAccountSuccess(relationship) {
|
||||
return {
|
||||
type: ACCOUNT_PIN_SUCCESS,
|
||||
relationship,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_PIN_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_UNPIN_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinAccountSuccess(relationship) {
|
||||
return {
|
||||
type: ACCOUNT_UNPIN_SUCCESS,
|
||||
relationship,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_UNPIN_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -7,26 +7,25 @@ import { useEmoji } from './emojis';
|
||||
import resizeImage from '../utils/resize_image';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { updateTimeline } from './timelines';
|
||||
import { showAlertForError } from './alerts';
|
||||
import { showAlert } from './alerts';
|
||||
import { showAlert, showAlertForError } from './alerts';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags;
|
||||
|
||||
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
|
||||
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
|
||||
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
|
||||
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
|
||||
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
|
||||
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
|
||||
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
|
||||
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
|
||||
export const COMPOSE_RESET = 'COMPOSE_RESET';
|
||||
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
|
||||
export const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS';
|
||||
export const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL';
|
||||
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
|
||||
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
|
||||
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
|
||||
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
|
||||
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
|
||||
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
|
||||
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
|
||||
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
|
||||
export const COMPOSE_RESET = 'COMPOSE_RESET';
|
||||
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
|
||||
export const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS';
|
||||
export const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL';
|
||||
export const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS';
|
||||
export const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO';
|
||||
export const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO';
|
||||
|
||||
export const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
|
||||
export const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
|
||||
@ -35,32 +34,32 @@ export const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
|
||||
|
||||
export const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
|
||||
|
||||
export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
|
||||
export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
|
||||
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
|
||||
|
||||
export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
|
||||
export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
|
||||
export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
|
||||
export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
|
||||
export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
|
||||
export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
|
||||
export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
|
||||
|
||||
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
|
||||
|
||||
export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
|
||||
export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
|
||||
export const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL';
|
||||
export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
|
||||
export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
|
||||
export const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL';
|
||||
|
||||
export const COMPOSE_POLL_ADD = 'COMPOSE_POLL_ADD';
|
||||
export const COMPOSE_POLL_REMOVE = 'COMPOSE_POLL_REMOVE';
|
||||
export const COMPOSE_POLL_OPTION_ADD = 'COMPOSE_POLL_OPTION_ADD';
|
||||
export const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE';
|
||||
export const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE';
|
||||
export const COMPOSE_POLL_ADD = 'COMPOSE_POLL_ADD';
|
||||
export const COMPOSE_POLL_REMOVE = 'COMPOSE_POLL_REMOVE';
|
||||
export const COMPOSE_POLL_OPTION_ADD = 'COMPOSE_POLL_OPTION_ADD';
|
||||
export const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE';
|
||||
export const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE';
|
||||
export const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE';
|
||||
|
||||
const messages = defineMessages({
|
||||
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
||||
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
||||
uploadErrorPoll : { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
||||
});
|
||||
|
||||
const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1);
|
||||
@ -76,57 +75,57 @@ export function changeCompose(text) {
|
||||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function replyCompose(status, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: COMPOSE_REPLY,
|
||||
type : COMPOSE_REPLY,
|
||||
status: status,
|
||||
});
|
||||
|
||||
ensureComposeIsVisible(getState, routerHistory);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function cancelReplyCompose() {
|
||||
return {
|
||||
type: COMPOSE_REPLY_CANCEL,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function resetCompose() {
|
||||
return {
|
||||
type: COMPOSE_RESET,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function mentionCompose(account, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: COMPOSE_MENTION,
|
||||
type : COMPOSE_MENTION,
|
||||
account: account,
|
||||
});
|
||||
|
||||
ensureComposeIsVisible(getState, routerHistory);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function directCompose(account, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: COMPOSE_DIRECT,
|
||||
type : COMPOSE_DIRECT,
|
||||
account: account,
|
||||
});
|
||||
|
||||
ensureComposeIsVisible(getState, routerHistory);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function submitCompose(routerHistory) {
|
||||
return function (dispatch, getState) {
|
||||
const status = getState().getIn(['compose', 'text'], '');
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
|
||||
if ((!status || !status.length) && media.size === 0) {
|
||||
return;
|
||||
@ -137,11 +136,11 @@ export function submitCompose(routerHistory) {
|
||||
api(getState).post('/api/v1/statuses', {
|
||||
status,
|
||||
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
||||
media_ids: media.map(item => item.get('id')),
|
||||
sensitive: getState().getIn(['compose', 'sensitive']),
|
||||
spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
||||
visibility: getState().getIn(['compose', 'privacy']),
|
||||
poll: getState().getIn(['compose', 'poll'], null),
|
||||
media_ids : media.map(item => item.get('id')),
|
||||
sensitive : getState().getIn(['compose', 'sensitive']),
|
||||
spoiler_text : getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
||||
visibility : getState().getIn(['compose', 'privacy']),
|
||||
poll : getState().getIn(['compose', 'poll'], null),
|
||||
}, {
|
||||
headers: {
|
||||
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
||||
@ -179,33 +178,33 @@ export function submitCompose(routerHistory) {
|
||||
dispatch(submitComposeFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function submitComposeRequest() {
|
||||
return {
|
||||
type: COMPOSE_SUBMIT_REQUEST,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function submitComposeSuccess(status) {
|
||||
return {
|
||||
type: COMPOSE_SUBMIT_SUCCESS,
|
||||
type : COMPOSE_SUBMIT_SUCCESS,
|
||||
status: status,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function submitComposeFail(error) {
|
||||
return {
|
||||
type: COMPOSE_SUBMIT_FAIL,
|
||||
type : COMPOSE_SUBMIT_FAIL,
|
||||
error: error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function uploadCompose(files) {
|
||||
return function (dispatch, getState) {
|
||||
const uploadLimit = 4;
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const pending = getState().getIn(['compose', 'pending_media_attachments']);
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const pending = getState().getIn(['compose', 'pending_media_attachments']);
|
||||
const progress = new Array(files.length).fill(0);
|
||||
let total = Array.from(files).reduce((a, v) => a + v.size, 0);
|
||||
|
||||
@ -231,15 +230,16 @@ export function uploadCompose(files) {
|
||||
total += file.size - f.size;
|
||||
|
||||
return api(getState).post('/api/v1/media', data, {
|
||||
onUploadProgress: function({ loaded }){
|
||||
onUploadProgress: function ({ loaded }) {
|
||||
progress[i] = loaded;
|
||||
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
||||
},
|
||||
}).then(({ data }) => dispatch(uploadComposeSuccess(data, f)));
|
||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeUploadCompose(id, params) {
|
||||
return (dispatch, getState) => {
|
||||
@ -251,68 +251,69 @@ export function changeUploadCompose(id, params) {
|
||||
dispatch(changeUploadComposeFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeUploadComposeRequest() {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_CHANGE_REQUEST,
|
||||
type : COMPOSE_UPLOAD_CHANGE_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeUploadComposeSuccess(media) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||
media: media,
|
||||
type : COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||
media : media,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeUploadComposeFail(error) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_CHANGE_FAIL,
|
||||
error: error,
|
||||
type : COMPOSE_UPLOAD_CHANGE_FAIL,
|
||||
error : error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function uploadComposeRequest() {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_REQUEST,
|
||||
type : COMPOSE_UPLOAD_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function uploadComposeProgress(loaded, total) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_PROGRESS,
|
||||
type : COMPOSE_UPLOAD_PROGRESS,
|
||||
loaded: loaded,
|
||||
total: total,
|
||||
total : total,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function uploadComposeSuccess(media, file) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_SUCCESS,
|
||||
media: media,
|
||||
file: file,
|
||||
type : COMPOSE_UPLOAD_SUCCESS,
|
||||
media : media,
|
||||
file : file,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function uploadComposeFail(error) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_FAIL,
|
||||
error: error,
|
||||
type : COMPOSE_UPLOAD_FAIL,
|
||||
error : error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function undoUploadCompose(media_id) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_UNDO,
|
||||
type : COMPOSE_UPLOAD_UNDO,
|
||||
media_id: media_id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function clearComposeSuggestions() {
|
||||
if (cancelFetchComposeSuggestionsAccounts) {
|
||||
@ -321,7 +322,7 @@ export function clearComposeSuggestions() {
|
||||
return {
|
||||
type: COMPOSE_SUGGESTIONS_CLEAR,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
||||
if (cancelFetchComposeSuggestionsAccounts) {
|
||||
@ -334,9 +335,9 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) =>
|
||||
}),
|
||||
|
||||
params: {
|
||||
q: token.slice(1),
|
||||
q : token.slice(1),
|
||||
resolve: false,
|
||||
limit: 4,
|
||||
limit : 4,
|
||||
},
|
||||
}).then(response => {
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
@ -366,10 +367,10 @@ const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => {
|
||||
}),
|
||||
|
||||
params: {
|
||||
type: 'hashtags',
|
||||
q: token.slice(1),
|
||||
resolve: false,
|
||||
limit: 4,
|
||||
type : 'hashtags',
|
||||
q : token.slice(1),
|
||||
resolve : false,
|
||||
limit : 4,
|
||||
exclude_unreviewed: true,
|
||||
},
|
||||
}).then(({ data }) => {
|
||||
@ -395,7 +396,7 @@ export function fetchComposeSuggestions(token) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function readyComposeSuggestionsEmojis(token, emojis) {
|
||||
return {
|
||||
@ -403,7 +404,7 @@ export function readyComposeSuggestionsEmojis(token, emojis) {
|
||||
token,
|
||||
emojis,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function readyComposeSuggestionsAccounts(token, accounts) {
|
||||
return {
|
||||
@ -411,7 +412,7 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
|
||||
token,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const readyComposeSuggestionsTags = (token, tags) => ({
|
||||
type: COMPOSE_SUGGESTIONS_READY,
|
||||
@ -424,27 +425,27 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
|
||||
let completion, startPosition;
|
||||
|
||||
if (suggestion.type === 'emoji') {
|
||||
completion = suggestion.native || suggestion.colons;
|
||||
completion = suggestion.native || suggestion.colons;
|
||||
startPosition = position - 1;
|
||||
|
||||
dispatch(useEmoji(suggestion));
|
||||
} else if (suggestion.type === 'hashtag') {
|
||||
completion = `#${suggestion.name}`;
|
||||
completion = `#${suggestion.name}`;
|
||||
startPosition = position - 1;
|
||||
} else if (suggestion.type === 'account') {
|
||||
completion = getState().getIn(['accounts', suggestion.id, 'acct']);
|
||||
completion = getState().getIn(['accounts', suggestion.id, 'acct']);
|
||||
startPosition = position;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: COMPOSE_SUGGESTION_SELECT,
|
||||
type : COMPOSE_SUGGESTION_SELECT,
|
||||
position: startPosition,
|
||||
token,
|
||||
completion,
|
||||
path,
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function updateSuggestionTags(token) {
|
||||
return {
|
||||
@ -492,39 +493,39 @@ export function mountCompose() {
|
||||
return {
|
||||
type: COMPOSE_MOUNT,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmountCompose() {
|
||||
return {
|
||||
type: COMPOSE_UNMOUNT,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeComposeSensitivity() {
|
||||
return {
|
||||
type: COMPOSE_SENSITIVITY_CHANGE,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeComposeSpoilerness() {
|
||||
return {
|
||||
type: COMPOSE_SPOILERNESS_CHANGE,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeComposeSpoilerText(text) {
|
||||
return {
|
||||
type: COMPOSE_SPOILER_TEXT_CHANGE,
|
||||
text,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeComposeVisibility(value) {
|
||||
return {
|
||||
type: COMPOSE_VISIBILITY_CHANGE,
|
||||
value,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function insertEmojiCompose(position, emoji, needsSpace) {
|
||||
return {
|
||||
@ -533,33 +534,33 @@ export function insertEmojiCompose(position, emoji, needsSpace) {
|
||||
emoji,
|
||||
needsSpace,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeComposing(value) {
|
||||
return {
|
||||
type: COMPOSE_COMPOSING_CHANGE,
|
||||
value,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function addPoll() {
|
||||
return {
|
||||
type: COMPOSE_POLL_ADD,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function removePoll() {
|
||||
return {
|
||||
type: COMPOSE_POLL_REMOVE,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function addPollOption(title) {
|
||||
return {
|
||||
type: COMPOSE_POLL_OPTION_ADD,
|
||||
title,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changePollOption(index, title) {
|
||||
return {
|
||||
@ -567,14 +568,14 @@ export function changePollOption(index, title) {
|
||||
index,
|
||||
title,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function removePollOption(index) {
|
||||
return {
|
||||
type: COMPOSE_POLL_OPTION_REMOVE,
|
||||
index,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changePollSettings(expiresIn, isMultiple) {
|
||||
return {
|
||||
@ -582,4 +583,4 @@ export function changePollSettings(expiresIn, isMultiple) {
|
||||
expiresIn,
|
||||
isMultiple,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,23 +1,19 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import {
|
||||
importFetchedAccounts,
|
||||
importFetchedStatuses,
|
||||
importFetchedStatus,
|
||||
} from './importer';
|
||||
import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
|
||||
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
|
||||
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
|
||||
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
|
||||
|
||||
export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST';
|
||||
export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
|
||||
export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL';
|
||||
export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE';
|
||||
export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL';
|
||||
export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE';
|
||||
|
||||
export const CONVERSATIONS_READ = 'CONVERSATIONS_READ';
|
||||
|
||||
export const CONVERSATIONS_DELETE_REQUEST = 'CONVERSATIONS_DELETE_REQUEST';
|
||||
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 = () => ({
|
||||
type: CONVERSATIONS_MOUNT,
|
||||
@ -30,7 +26,7 @@ export const unmountConversations = () => ({
|
||||
export const markConversationRead = conversationId => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: CONVERSATIONS_READ,
|
||||
id: conversationId,
|
||||
id : conversationId,
|
||||
});
|
||||
|
||||
api(getState).post(`/api/v1/conversations/${conversationId}/read`);
|
||||
|
@ -3,31 +3,31 @@ import openDB from '../storage/db';
|
||||
import { evictStatus } from '../storage/modifier';
|
||||
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer';
|
||||
import { importAccount, importFetchedStatus, importFetchedStatuses, importStatus } from './importer';
|
||||
import { ensureComposeIsVisible } from './compose';
|
||||
|
||||
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
|
||||
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_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_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_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_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_HIDE = 'STATUS_HIDE';
|
||||
export const STATUS_HIDE = 'STATUS_HIDE';
|
||||
|
||||
export const REDRAFT = 'REDRAFT';
|
||||
|
||||
@ -37,7 +37,7 @@ export function fetchStatusRequest(id, skipLoading) {
|
||||
id,
|
||||
skipLoading,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function getFromDB(dispatch, getState, accountIndex, index, id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -113,24 +113,24 @@ export function fetchStatus(id) {
|
||||
dispatch(fetchStatusFail(id, error, skipLoading));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchStatusSuccess(skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_SUCCESS,
|
||||
skipLoading,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchStatusFail(id, error, skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_FAIL,
|
||||
type : STATUS_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipLoading,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function redraft(status, raw_text) {
|
||||
return {
|
||||
@ -138,7 +138,7 @@ export function redraft(status, raw_text) {
|
||||
status,
|
||||
raw_text,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||
return (dispatch, getState) => {
|
||||
@ -163,29 +163,29 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||
dispatch(deleteStatusFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatusRequest(id) {
|
||||
return {
|
||||
type: STATUS_DELETE_REQUEST,
|
||||
id: id,
|
||||
id : id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatusSuccess(id) {
|
||||
return {
|
||||
type: STATUS_DELETE_SUCCESS,
|
||||
id: id,
|
||||
id : id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteStatusFail(id, error) {
|
||||
return {
|
||||
type: STATUS_DELETE_FAIL,
|
||||
id: id,
|
||||
type : STATUS_DELETE_FAIL,
|
||||
id : id,
|
||||
error: error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContext(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -203,33 +203,33 @@ export function fetchContext(id) {
|
||||
dispatch(fetchContextFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContextRequest(id) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContextSuccess(id, ancestors, descendants) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_SUCCESS,
|
||||
type : CONTEXT_FETCH_SUCCESS,
|
||||
id,
|
||||
ancestors,
|
||||
descendants,
|
||||
statuses: ancestors.concat(descendants),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContextFail(id, error) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_FAIL,
|
||||
type : CONTEXT_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -241,21 +241,21 @@ export function muteStatus(id) {
|
||||
dispatch(muteStatusFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatusRequest(id) {
|
||||
return {
|
||||
type: STATUS_MUTE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatusSuccess(id) {
|
||||
return {
|
||||
type: STATUS_MUTE_SUCCESS,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatusFail(id, error) {
|
||||
return {
|
||||
@ -263,7 +263,7 @@ export function muteStatusFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
@ -275,21 +275,21 @@ export function unmuteStatus(id) {
|
||||
dispatch(unmuteStatusFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatusRequest(id) {
|
||||
return {
|
||||
type: STATUS_UNMUTE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatusSuccess(id) {
|
||||
return {
|
||||
type: STATUS_UNMUTE_SUCCESS,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatusFail(id, error) {
|
||||
return {
|
||||
@ -297,7 +297,7 @@ export function unmuteStatusFail(id, error) {
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function hideStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
@ -308,7 +308,7 @@ export function hideStatus(ids) {
|
||||
type: STATUS_HIDE,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function revealStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
@ -319,4 +319,4 @@ export function revealStatus(ids) {
|
||||
type: STATUS_REVEAL,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -37,27 +37,27 @@ class Account extends ImmutablePureComponent {
|
||||
|
||||
handleFollow = () => {
|
||||
this.props.onFollow(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleBlock = () => {
|
||||
this.props.onBlock(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleMute = () => {
|
||||
this.props.onMute(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleMuteNotifications = () => {
|
||||
this.props.onMuteNotifications(this.props.account, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleUnmuteNotifications = () => {
|
||||
this.props.onMuteNotifications(this.props.account, false);
|
||||
}
|
||||
};
|
||||
|
||||
handleAction = () => {
|
||||
this.props.onActionClick(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, intl, hidden, onActionClick, actionIcon, actionTitle } = this.props;
|
||||
@ -111,7 +111,10 @@ class Account extends ImmutablePureComponent {
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<Permalink key={account.get('id')} className='account__display-name' title={account.get('acct')} href={account.get('url')} to={`/accounts/${account.get('id')}`}>
|
||||
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
|
||||
<div className='account__avatar-wrapper'><Avatar
|
||||
account={account}
|
||||
size={55}
|
||||
/></div >
|
||||
<DisplayName account={account} />
|
||||
</Permalink>
|
||||
|
||||
|
@ -7,16 +7,16 @@ export default class Avatar extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
style: PropTypes.object,
|
||||
inline: PropTypes.bool,
|
||||
size : PropTypes.number.isRequired,
|
||||
style : PropTypes.object,
|
||||
inline : PropTypes.bool,
|
||||
animate: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
animate: autoPlayGif,
|
||||
size: 20,
|
||||
inline: false,
|
||||
size : 20,
|
||||
inline : false,
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -26,14 +26,14 @@ export default class Avatar extends React.PureComponent {
|
||||
handleMouseEnter = () => {
|
||||
if (this.props.animate) return;
|
||||
this.setState({ hovering: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
if (this.props.animate) return;
|
||||
this.setState({ hovering: false });
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { account, size, animate, inline } = this.props;
|
||||
const { hovering } = this.state;
|
||||
|
||||
@ -48,8 +48,8 @@ export default class Avatar extends React.PureComponent {
|
||||
|
||||
const style = {
|
||||
...this.props.style,
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
width : `${size}px`,
|
||||
height : `${size}px`,
|
||||
backgroundSize: `${size}px ${size}px`,
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { version, source_url } from 'mastodon/initial_state';
|
||||
import { source_url, version } from 'mastodon/initial_state';
|
||||
|
||||
export default class ErrorBoundary extends React.PureComponent {
|
||||
|
||||
@ -10,17 +10,17 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||
};
|
||||
|
||||
state = {
|
||||
hasError: false,
|
||||
stackTrace: undefined,
|
||||
hasError : false,
|
||||
stackTrace : undefined,
|
||||
componentStack: undefined,
|
||||
};
|
||||
|
||||
componentDidCatch (error, info) {
|
||||
componentDidCatch(error, info) {
|
||||
this.setState({
|
||||
hasError: true,
|
||||
stackTrace: error.stack,
|
||||
hasError : true,
|
||||
stackTrace : error.stack,
|
||||
componentStack: info && info.componentStack,
|
||||
copied: false,
|
||||
copied : false,
|
||||
});
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||
const { stackTrace } = this.state;
|
||||
const textarea = document.createElement('textarea');
|
||||
|
||||
textarea.textContent = stackTrace;
|
||||
textarea.textContent = stackTrace;
|
||||
textarea.style.position = 'fixed';
|
||||
|
||||
document.body.appendChild(textarea);
|
||||
@ -44,7 +44,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||
|
||||
this.setState({ copied: true });
|
||||
setTimeout(() => this.setState({ copied: false }), 700);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasError, copied } = this.state;
|
||||
@ -54,13 +54,32 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='error-boundary'>
|
||||
<div>
|
||||
<p className='error-boundary__error'><FormattedMessage id='error.unexpected_crash.explanation' defaultMessage='Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.' /></p>
|
||||
<p><FormattedMessage id='error.unexpected_crash.next_steps' defaultMessage='Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.' /></p>
|
||||
<p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied && 'copied'}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='error-boundary hero text-center padded content'>
|
||||
<div >
|
||||
<p className='error-boundary__error title content-heading'><FormattedMessage
|
||||
id='error.unexpected_crash.explanation'
|
||||
defaultMessage='Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.'
|
||||
/></p >
|
||||
<p className='content'><FormattedMessage
|
||||
id='error.unexpected_crash.next_steps'
|
||||
defaultMessage='Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.'
|
||||
/></p >
|
||||
<p className='error-boundary__footer'>Mastodon v{version} · <a
|
||||
href={source_url}
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='errors.unexpected_crash.report_issue'
|
||||
defaultMessage='Report issue'
|
||||
/></a > · <button
|
||||
onClick={this.handleCopyStackTrace}
|
||||
className={copied && 'copied'}
|
||||
><FormattedMessage
|
||||
id='errors.unexpected_crash.copy_stacktrace'
|
||||
defaultMessage='Copy stacktrace to clipboard'
|
||||
/></button ></p >
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6,41 +6,41 @@ import Icon from 'mastodon/components/icon';
|
||||
export default class IconButton extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
title: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
className : PropTypes.string,
|
||||
title : PropTypes.string.isRequired,
|
||||
icon : PropTypes.string.isRequired,
|
||||
onClick : PropTypes.func,
|
||||
onMouseDown: PropTypes.func,
|
||||
onKeyDown: PropTypes.func,
|
||||
onKeyPress: PropTypes.func,
|
||||
size: PropTypes.number,
|
||||
active: PropTypes.bool,
|
||||
pressed: PropTypes.bool,
|
||||
expanded: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
onKeyDown : PropTypes.func,
|
||||
onKeyPress : PropTypes.func,
|
||||
size : PropTypes.number,
|
||||
active : PropTypes.bool,
|
||||
pressed : PropTypes.bool,
|
||||
expanded : PropTypes.bool,
|
||||
style : PropTypes.object,
|
||||
activeStyle: PropTypes.object,
|
||||
disabled: PropTypes.bool,
|
||||
inverted: PropTypes.bool,
|
||||
animate: PropTypes.bool,
|
||||
overlay: PropTypes.bool,
|
||||
tabIndex: PropTypes.string,
|
||||
disabled : PropTypes.bool,
|
||||
inverted : PropTypes.bool,
|
||||
animate : PropTypes.bool,
|
||||
overlay : PropTypes.bool,
|
||||
tabIndex : PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
size: 18,
|
||||
active: false,
|
||||
size : 18,
|
||||
active : false,
|
||||
disabled: false,
|
||||
animate: false,
|
||||
overlay: false,
|
||||
animate : false,
|
||||
overlay : false,
|
||||
tabIndex: '0',
|
||||
};
|
||||
|
||||
state = {
|
||||
activate: false,
|
||||
activate : false,
|
||||
deactivate: false,
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!nextProps.animate) return;
|
||||
|
||||
if (this.props.active && !nextProps.active) {
|
||||
@ -50,38 +50,38 @@ export default class IconButton extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = (e) => {
|
||||
handleClick = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!this.props.disabled) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (this.props.onKeyPress && !this.props.disabled) {
|
||||
this.props.onKeyPress(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown = (e) => {
|
||||
if (!this.props.disabled && this.props.onMouseDown) {
|
||||
this.props.onMouseDown(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (!this.props.disabled && this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const style = {
|
||||
fontSize: `${this.props.size}px`,
|
||||
width: `${this.props.size * 1.28571429}px`,
|
||||
height: `${this.props.size * 1.28571429}px`,
|
||||
lineHeight: `${this.props.size}px`,
|
||||
// fontSize: `${this.props.size}px`,
|
||||
// width: `${this.props.size * 1.28571429}px`,
|
||||
// height: `${this.props.size * 1.28571429}px`,
|
||||
// lineHeight: `${this.props.size}px`,
|
||||
...this.props.style,
|
||||
...(this.props.active ? this.props.activeStyle : {}),
|
||||
};
|
||||
@ -128,8 +128,12 @@ export default class IconButton extends React.PureComponent {
|
||||
tabIndex={tabIndex}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />
|
||||
</button>
|
||||
<Icon
|
||||
id={icon}
|
||||
fixedWidth
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</button >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ import { Audio, MediaGallery, Video } from '../features/ui/util/async-components
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import { displayMedia } from '../initial_state';
|
||||
import { displayMedia, isStaff } from '../initial_state';
|
||||
// We use the component (and not the container) since we do not want
|
||||
// to use the progress bar to show download progress
|
||||
import Bundle from '../features/ui/components/bundle';
|
||||
@ -82,11 +82,15 @@ class Status extends ImmutablePureComponent {
|
||||
onMoveUp : PropTypes.func,
|
||||
onMoveDown : PropTypes.func,
|
||||
showThread : PropTypes.bool,
|
||||
threadsCompile : PropTypes.bool,
|
||||
getScrollPosition : PropTypes.func,
|
||||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth : PropTypes.func,
|
||||
cachedMediaWidth : PropTypes.number,
|
||||
};
|
||||
static defaultProps = {
|
||||
threadsCompile: true,
|
||||
};
|
||||
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
// evaluate to false. See react-immutable-pure-component for usage.
|
||||
@ -199,24 +203,24 @@ class Status extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
renderLoadingMediaGallery() {
|
||||
return <div
|
||||
return (<div
|
||||
className='media-gallery'
|
||||
style={{ height: '110px' }}
|
||||
/>;
|
||||
/>);
|
||||
}
|
||||
|
||||
renderLoadingVideoPlayer() {
|
||||
return <div
|
||||
return (<div
|
||||
className='video-player'
|
||||
style={{ height: '110px' }}
|
||||
/>;
|
||||
/>);
|
||||
}
|
||||
|
||||
renderLoadingAudioPlayer() {
|
||||
return <div
|
||||
return (<div
|
||||
className='audio-player'
|
||||
style={{ height: '110px' }}
|
||||
/>;
|
||||
/>);
|
||||
}
|
||||
|
||||
handleOpenVideo = (media, startTime) => {
|
||||
@ -497,20 +501,20 @@ class Status extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
if (otherAccounts && otherAccounts.size > 0) {
|
||||
statusAvatar = <AvatarComposite
|
||||
statusAvatar = (<AvatarComposite
|
||||
accounts={otherAccounts}
|
||||
size={48}
|
||||
/>;
|
||||
size={55}
|
||||
/>);
|
||||
} else if (account === undefined || account === null) {
|
||||
statusAvatar = <Avatar
|
||||
statusAvatar = (<Avatar
|
||||
account={status.get('account')}
|
||||
size={48}
|
||||
/>;
|
||||
size={55}
|
||||
/>);
|
||||
} else {
|
||||
statusAvatar = <AvatarOverlay
|
||||
statusAvatar = (<AvatarOverlay
|
||||
account={status.get('account')}
|
||||
friend={account}
|
||||
/>;
|
||||
/>);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -542,6 +546,12 @@ class Status extends ImmutablePureComponent {
|
||||
role='presentation'
|
||||
/>
|
||||
<div className='status__info'>
|
||||
|
||||
{isStaff && (<div className='administrate-stuff pull-left'>
|
||||
|
||||
<i className='fa fa-gears' />
|
||||
</div >
|
||||
)}
|
||||
<a
|
||||
href={status.get('url')}
|
||||
className='status__relative-time'
|
||||
@ -595,10 +605,19 @@ class Status extends ImmutablePureComponent {
|
||||
/>
|
||||
</button >
|
||||
)}
|
||||
{/*<div className='well'>*/}
|
||||
|
||||
{/* {status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) &&*/}
|
||||
{/* <p > oui je cause tout seul, c'est un thread</p >*/}
|
||||
{/* }*/}
|
||||
{/* {this.props.threadsCompile &&*/}
|
||||
{/* <p > les threads sont en mode compilés</p >*/}
|
||||
{/* }*/}
|
||||
{/*</div >*/}
|
||||
<StatusActionBar
|
||||
status={status}
|
||||
account={account} {...other} />
|
||||
account={account} {...other}
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
</HotKeys >
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'intersection-observer';
|
||||
import 'requestidlecallback';
|
||||
import objectFitImages from 'object-fit-images';
|
||||
import objectFitImages from 'object-fit-images';
|
||||
|
||||
objectFitImages();
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
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 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 Icon from 'mastodon/components/icon';
|
||||
import Avatar from 'mastodon/components/avatar';
|
||||
@ -70,7 +70,7 @@ class Header extends ImmutablePureComponent {
|
||||
|
||||
openEditProfile = () => {
|
||||
window.open('/settings/profile', '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
isStatusesPageActive = (match, location) => {
|
||||
if (!match) {
|
||||
@ -78,7 +78,7 @@ class Header extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
return !location.pathname.match(/\/(followers|following)\/?$/);
|
||||
}
|
||||
};
|
||||
|
||||
_updateEmojis () {
|
||||
const node = this.node;
|
||||
@ -111,15 +111,15 @@ class Header extends ImmutablePureComponent {
|
||||
|
||||
handleEmojiMouseEnter = ({ target }) => {
|
||||
target.src = target.getAttribute('data-original');
|
||||
}
|
||||
};
|
||||
|
||||
handleEmojiMouseLeave = ({ target }) => {
|
||||
target.src = target.getAttribute('data-static');
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, intl, domain, identity_proofs } = this.props;
|
||||
@ -263,7 +263,10 @@ class Header extends ImmutablePureComponent {
|
||||
<div className='account__header__bar'>
|
||||
<div className='account__header__tabs'>
|
||||
<a className='avatar' href={account.get('url')} rel='noopener noreferrer' target='_blank'>
|
||||
<Avatar account={account} size={90} />
|
||||
<Avatar
|
||||
account={account}
|
||||
size={120}
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div className='spacer' />
|
||||
|
@ -21,12 +21,13 @@ const mapStateToProps = (state, { params: { accountId }, withReplies = false })
|
||||
const path = withReplies ? `${accountId}:with_replies` : accountId;
|
||||
|
||||
return {
|
||||
isAccount: !!state.getIn(['accounts', accountId]),
|
||||
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
||||
isAccount : !!state.getIn(['accounts', accountId]),
|
||||
account : state.getIn(['accounts', accountId]),
|
||||
statusIds : state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
||||
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
||||
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
||||
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
||||
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||
isLoading : state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
||||
hasMore : state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
||||
blockedBy : state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||
};
|
||||
};
|
||||
|
||||
@ -34,20 +35,20 @@ export default @connect(mapStateToProps)
|
||||
class AccountTimeline extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
params : PropTypes.object.isRequired,
|
||||
dispatch : PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
statusIds: ImmutablePropTypes.list,
|
||||
featuredStatusIds: ImmutablePropTypes.list,
|
||||
isLoading: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
withReplies: PropTypes.bool,
|
||||
blockedBy: PropTypes.bool,
|
||||
isAccount: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
statusIds : ImmutablePropTypes.list,
|
||||
featuredStatusIds : ImmutablePropTypes.list,
|
||||
isLoading : PropTypes.bool,
|
||||
hasMore : PropTypes.bool,
|
||||
withReplies : PropTypes.bool,
|
||||
blockedBy : PropTypes.bool,
|
||||
isAccount : PropTypes.bool,
|
||||
multiColumn : PropTypes.bool,
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
componentWillMount() {
|
||||
const { params: { accountId }, withReplies } = this.props;
|
||||
|
||||
this.props.dispatch(fetchAccount(accountId));
|
||||
@ -60,7 +61,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
|
||||
this.props.dispatch(fetchAccount(nextProps.params.accountId));
|
||||
this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
|
||||
@ -74,33 +75,42 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies }));
|
||||
}
|
||||
this.props.dispatch(expandAccountTimeline(this.props.params.accountId, {
|
||||
maxId,
|
||||
withReplies: this.props.withReplies,
|
||||
}));
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount, multiColumn } = this.props;
|
||||
|
||||
if (!isAccount) {
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<ColumnBackButton multiColumn={multiColumn} />
|
||||
<MissingIndicator />
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
if (!statusIds && isLoading) {
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;
|
||||
const emptyMessage = blockedBy ? <FormattedMessage
|
||||
id='empty_column.account_unavailable'
|
||||
defaultMessage='Profile unavailable'
|
||||
/> : <FormattedMessage
|
||||
id='empty_column.account_timeline'
|
||||
defaultMessage='No toots here!'
|
||||
/>;
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<ColumnBackButton multiColumn={multiColumn} />
|
||||
|
||||
<StatusList
|
||||
@ -116,7 +126,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
/>
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
spoilerText : PropTypes.string,
|
||||
focusDate : PropTypes.instanceOf(Date),
|
||||
caretPosition : PropTypes.number,
|
||||
maxTootCharsLimit : PropTypes.number,
|
||||
preselectDate : PropTypes.instanceOf(Date),
|
||||
isSubmitting : PropTypes.bool,
|
||||
isChangingUpload : PropTypes.bool,
|
||||
@ -64,7 +65,8 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
showSearch: false,
|
||||
showSearch : false,
|
||||
maxTootCharsLimit: 7777,
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
@ -88,7 +90,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
||||
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
|
||||
if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > 7777 || (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;
|
||||
}
|
||||
|
||||
@ -181,23 +183,26 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
const { intl, onPaste, showSearch, anyMedia } = this.props;
|
||||
const disabled = this.props.isSubmitting;
|
||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > 7777 || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
|
||||
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > this.props.maxTootCharsLimit || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
|
||||
let publishText = '';
|
||||
|
||||
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
||||
publishText =
|
||||
<span className='compose-form__publish-private'><Icon id='lock'/> {intl.formatMessage(messages.publish)}</span>;
|
||||
<span className='compose-form__publish-private'><Icon id='lock' /> {intl.formatMessage(messages.publish)}</span >;
|
||||
} else {
|
||||
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='compose-form'>
|
||||
<WarningContainer/>
|
||||
<WarningContainer />
|
||||
|
||||
<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
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
@ -213,7 +218,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
/>
|
||||
</div>
|
||||
</div >
|
||||
|
||||
<AutosuggestTextarea
|
||||
ref={this.setAutosuggestTextarea}
|
||||
@ -230,31 +235,36 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
onPaste={onPaste}
|
||||
autoFocus={!showSearch && !isMobile(window.innerWidth)}
|
||||
>
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick}/>
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
<div className='compose-form__modifiers'>
|
||||
<UploadFormContainer/>
|
||||
<PollFormContainer/>
|
||||
</div>
|
||||
</AutosuggestTextarea>
|
||||
<UploadFormContainer />
|
||||
<PollFormContainer />
|
||||
</div >
|
||||
</AutosuggestTextarea >
|
||||
|
||||
<div className='compose-form__buttons-wrapper'>
|
||||
<div className='compose-form__buttons'>
|
||||
<UploadButtonContainer/>
|
||||
<PollButtonContainer/>
|
||||
<PrivacyDropdownContainer/>
|
||||
<SpoilerButtonContainer/>
|
||||
</div>
|
||||
<div className='character-counter__wrapper'><CharacterCounter max={7777} text={text}/></div>
|
||||
</div>
|
||||
<UploadButtonContainer />
|
||||
<PollButtonContainer />
|
||||
<PrivacyDropdownContainer />
|
||||
<SpoilerButtonContainer />
|
||||
</div >
|
||||
<div className='character-counter__wrapper'><CharacterCounter
|
||||
max={this.props.maxTootCharsLimit}
|
||||
text={text}
|
||||
/></div >
|
||||
</div >
|
||||
|
||||
<div className='compose-form__publish'>
|
||||
<div className='compose-form__publish-button-wrapper'><Button
|
||||
text={publishText} onClick={this.handleSubmit}
|
||||
disabled={disabledButton} block
|
||||
/></div>
|
||||
</div>
|
||||
text={publishText}
|
||||
onClick={this.handleSubmit}
|
||||
disabled={disabledButton}
|
||||
block
|
||||
/></div >
|
||||
</div >
|
||||
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ export default class NavigationBar extends ImmutablePureComponent {
|
||||
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span >
|
||||
<Avatar
|
||||
account={this.props.account}
|
||||
size={48}
|
||||
size={55}
|
||||
/>
|
||||
</Permalink >
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import IconButton from 'mastodon/components/icon_button';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
||||
@ -56,19 +56,19 @@ class Option extends React.PureComponent {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
this.handleToggleMultiple(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isPollMultiple, title, index, intl } = this.props;
|
||||
@ -82,8 +82,8 @@ class Option extends React.PureComponent {
|
||||
onKeyPress={this.handleCheckboxKeypress}
|
||||
role='button'
|
||||
tabIndex='0'
|
||||
title={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
|
||||
aria-label={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
|
||||
title={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
|
||||
aria-label={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
|
||||
/>
|
||||
|
||||
<AutosuggestInput
|
||||
|
@ -2,62 +2,62 @@ import { connect } from 'react-redux';
|
||||
import ComposeForm from '../components/compose_form';
|
||||
import {
|
||||
changeCompose,
|
||||
submitCompose,
|
||||
changeComposeSpoilerText,
|
||||
clearComposeSuggestions,
|
||||
fetchComposeSuggestions,
|
||||
selectComposeSuggestion,
|
||||
changeComposeSpoilerText,
|
||||
insertEmojiCompose,
|
||||
selectComposeSuggestion,
|
||||
submitCompose,
|
||||
uploadCompose,
|
||||
} from '../../../actions/compose';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
text: state.getIn(['compose', 'text']),
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
spoiler: state.getIn(['compose', 'spoiler']),
|
||||
spoilerText: state.getIn(['compose', 'spoiler_text']),
|
||||
privacy: state.getIn(['compose', 'privacy']),
|
||||
focusDate: state.getIn(['compose', 'focusDate']),
|
||||
caretPosition: state.getIn(['compose', 'caretPosition']),
|
||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||
text : state.getIn(['compose', 'text']),
|
||||
suggestions : state.getIn(['compose', 'suggestions']),
|
||||
spoiler : state.getIn(['compose', 'spoiler']),
|
||||
spoilerText : state.getIn(['compose', 'spoiler_text']),
|
||||
privacy : state.getIn(['compose', 'privacy']),
|
||||
focusDate : state.getIn(['compose', 'focusDate']),
|
||||
caretPosition : state.getIn(['compose', 'caretPosition']),
|
||||
preselectDate : state.getIn(['compose', 'preselectDate']),
|
||||
isSubmitting : state.getIn(['compose', 'is_submitting']),
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
isUploading : state.getIn(['compose', 'is_uploading']),
|
||||
showSearch : state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
anyMedia : state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
onChange (text) {
|
||||
onChange(text) {
|
||||
dispatch(changeCompose(text));
|
||||
},
|
||||
|
||||
onSubmit (router) {
|
||||
onSubmit(router) {
|
||||
dispatch(submitCompose(router));
|
||||
},
|
||||
|
||||
onClearSuggestions () {
|
||||
onClearSuggestions() {
|
||||
dispatch(clearComposeSuggestions());
|
||||
},
|
||||
|
||||
onFetchSuggestions (token) {
|
||||
onFetchSuggestions(token) {
|
||||
dispatch(fetchComposeSuggestions(token));
|
||||
},
|
||||
|
||||
onSuggestionSelected (position, token, suggestion, path) {
|
||||
onSuggestionSelected(position, token, suggestion, path) {
|
||||
dispatch(selectComposeSuggestion(position, token, suggestion, path));
|
||||
},
|
||||
|
||||
onChangeSpoilerText (checked) {
|
||||
onChangeSpoilerText(checked) {
|
||||
dispatch(changeComposeSpoilerText(checked));
|
||||
},
|
||||
|
||||
onPaste (files) {
|
||||
onPaste(files) {
|
||||
dispatch(uploadCompose(files));
|
||||
},
|
||||
|
||||
onPickEmoji (position, data, needsSpace) {
|
||||
onPickEmoji(position, data, needsSpace) {
|
||||
dispatch(insertEmojiCompose(position, data, needsSpace));
|
||||
},
|
||||
|
||||
|
@ -150,7 +150,7 @@ class Conversation extends ImmutablePureComponent {
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDelete });
|
||||
|
||||
const names = accounts.map(a => <Permalink
|
||||
const names = accounts.map(a => (<Permalink
|
||||
to={`/accounts/${a.get('id')}`}
|
||||
href={a.get('url')}
|
||||
key={a.get('id')}
|
||||
@ -160,7 +160,7 @@ class Conversation extends ImmutablePureComponent {
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
||||
/></bdi >
|
||||
</Permalink >).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
</Permalink >)).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
|
||||
const handlers = {
|
||||
reply : this.handleReply,
|
||||
@ -179,7 +179,7 @@ class Conversation extends ImmutablePureComponent {
|
||||
<div className='conversation__avatar'>
|
||||
<AvatarComposite
|
||||
accounts={accounts}
|
||||
size={48}
|
||||
size={55}
|
||||
/>
|
||||
</div >
|
||||
|
||||
@ -221,23 +221,24 @@ class Conversation extends ImmutablePureComponent {
|
||||
)}
|
||||
|
||||
<div className='status__action-bar'>
|
||||
<IconButton
|
||||
className='status__action-bar-button'
|
||||
title={intl.formatMessage(messages.reply)}
|
||||
icon='reply'
|
||||
onClick={this.handleReply}
|
||||
/>
|
||||
|
||||
<div className='status__action-bar-dropdown'>
|
||||
<DropdownMenuContainer
|
||||
status={lastStatus}
|
||||
items={menu}
|
||||
icon='ellipsis-h'
|
||||
size={18}
|
||||
direction='right'
|
||||
style={{ width: '15em' }}
|
||||
size={18}
|
||||
title={intl.formatMessage(messages.more)}
|
||||
/>
|
||||
</div >
|
||||
<IconButton
|
||||
className='status__action-bar-button conversation_reply'
|
||||
title={intl.formatMessage(messages.reply)}
|
||||
icon='reply'
|
||||
size={40}
|
||||
onClick={this.handleReply}
|
||||
/>
|
||||
</div >
|
||||
</div >
|
||||
</div >
|
||||
|
@ -9,10 +9,10 @@ import DisplayName from 'mastodon/components/display_name';
|
||||
import Permalink from 'mastodon/components/permalink';
|
||||
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
|
||||
import IconButton from 'mastodon/components/icon_button';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state';
|
||||
import { shortNumberFormat } from 'mastodon/utils/numbers';
|
||||
import { followAccount, unfollowAccount, blockAccount, unblockAccount, unmuteAccount } from 'mastodon/actions/accounts';
|
||||
import { blockAccount, followAccount, unblockAccount, unfollowAccount, unmuteAccount } from 'mastodon/actions/accounts';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { initMuteModal } from 'mastodon/actions/mutes';
|
||||
|
||||
@ -113,27 +113,27 @@ class AccountCard extends ImmutablePureComponent {
|
||||
|
||||
handleEmojiMouseEnter = ({ target }) => {
|
||||
target.src = target.getAttribute('data-original');
|
||||
}
|
||||
};
|
||||
|
||||
handleEmojiMouseLeave = ({ target }) => {
|
||||
target.src = target.getAttribute('data-static');
|
||||
}
|
||||
};
|
||||
|
||||
handleFollow = () => {
|
||||
this.props.onFollow(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleBlock = () => {
|
||||
this.props.onBlock(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleMute = () => {
|
||||
this.props.onMute(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, intl } = this.props;
|
||||
@ -165,7 +165,10 @@ class AccountCard extends ImmutablePureComponent {
|
||||
|
||||
<div className='directory__card__bar'>
|
||||
<Permalink className='directory__card__bar__name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
|
||||
<Avatar account={account} size={48} />
|
||||
<Avatar
|
||||
account={account}
|
||||
size={55}
|
||||
/>
|
||||
<DisplayName account={account} />
|
||||
</Permalink>
|
||||
|
||||
|
@ -31,7 +31,10 @@ class AccountAuthorize extends ImmutablePureComponent {
|
||||
<div className='account-authorize__wrapper'>
|
||||
<div className='account-authorize'>
|
||||
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
|
||||
<div className='account-authorize__avatar'><Avatar account={account} size={48} /></div>
|
||||
<div className='account-authorize__avatar'><Avatar
|
||||
account={account}
|
||||
size={55}
|
||||
/></div >
|
||||
<DisplayName account={account} />
|
||||
</Permalink>
|
||||
|
||||
|
@ -5,11 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { debounce } from 'lodash';
|
||||
import LoadingIndicator from '../../components/loading_indicator';
|
||||
import {
|
||||
fetchAccount,
|
||||
fetchFollowing,
|
||||
expandFollowing,
|
||||
} from '../../actions/accounts';
|
||||
import { expandFollowing, fetchAccount, fetchFollowing } from '../../actions/accounts';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import Column from '../ui/components/column';
|
||||
@ -19,34 +15,34 @@ import ScrollableList from '../../components/scrollable_list';
|
||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
isAccount: !!state.getIn(['accounts', props.params.accountId]),
|
||||
isAccount : !!state.getIn(['accounts', props.params.accountId]),
|
||||
accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
|
||||
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||
hasMore : !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
|
||||
blockedBy : state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
class Following extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
params : PropTypes.object.isRequired,
|
||||
dispatch : PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
hasMore: PropTypes.bool,
|
||||
blockedBy: PropTypes.bool,
|
||||
isAccount: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
accountIds : ImmutablePropTypes.list,
|
||||
hasMore : PropTypes.bool,
|
||||
blockedBy : PropTypes.bool,
|
||||
isAccount : PropTypes.bool,
|
||||
multiColumn : PropTypes.bool,
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
componentWillMount() {
|
||||
if (!this.props.accountIds) {
|
||||
this.props.dispatch(fetchAccount(this.props.params.accountId));
|
||||
this.props.dispatch(fetchFollowing(this.props.params.accountId));
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
|
||||
this.props.dispatch(fetchAccount(nextProps.params.accountId));
|
||||
this.props.dispatch(fetchFollowing(nextProps.params.accountId));
|
||||
@ -57,29 +53,35 @@ class Following extends ImmutablePureComponent {
|
||||
this.props.dispatch(expandFollowing(this.props.params.accountId));
|
||||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props;
|
||||
|
||||
if (!isAccount) {
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<MissingIndicator />
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />;
|
||||
const emptyMessage = blockedBy ? (<FormattedMessage
|
||||
id='empty_column.account_unavailable'
|
||||
defaultMessage='Profile unavailable'
|
||||
/>) : (<FormattedMessage
|
||||
id='account.follows.empty'
|
||||
defaultMessage="This user doesn't follow anyone yet."
|
||||
/>);
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<ColumnBackButton multiColumn={multiColumn} />
|
||||
|
||||
<ScrollableList
|
||||
@ -87,16 +89,23 @@ class Following extends ImmutablePureComponent {
|
||||
hasMore={hasMore}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
||||
prepend={<HeaderContainer
|
||||
accountId={this.props.params.accountId}
|
||||
hideTabs
|
||||
/>}
|
||||
alwaysPrepend
|
||||
emptyMessage={emptyMessage}
|
||||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{blockedBy ? [] : accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
(<AccountContainer
|
||||
key={id}
|
||||
id={id}
|
||||
withNote={false}
|
||||
/>),
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
</ScrollableList >
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
@ -13,10 +13,10 @@ import Permalink from 'mastodon/components/permalink';
|
||||
|
||||
const messages = defineMessages({
|
||||
favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' },
|
||||
follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
|
||||
ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
|
||||
poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
|
||||
reblog: { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
|
||||
follow : { id: 'notification.follow', defaultMessage: '{name} followed you' },
|
||||
ownPoll : { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
|
||||
poll : { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
|
||||
reblog : { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
|
||||
});
|
||||
|
||||
const notificationForScreenReader = (intl, message, timestamp) => {
|
||||
@ -35,31 +35,31 @@ class Notification extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
notification: ImmutablePropTypes.map.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
onMoveUp: PropTypes.func.isRequired,
|
||||
onMoveDown: PropTypes.func.isRequired,
|
||||
onMention: PropTypes.func.isRequired,
|
||||
onFavourite: PropTypes.func.isRequired,
|
||||
onReblog: PropTypes.func.isRequired,
|
||||
onToggleHidden: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
intl: PropTypes.object.isRequired,
|
||||
getScrollPosition: PropTypes.func,
|
||||
notification : ImmutablePropTypes.map.isRequired,
|
||||
hidden : PropTypes.bool,
|
||||
onMoveUp : PropTypes.func.isRequired,
|
||||
onMoveDown : PropTypes.func.isRequired,
|
||||
onMention : PropTypes.func.isRequired,
|
||||
onFavourite : PropTypes.func.isRequired,
|
||||
onReblog : PropTypes.func.isRequired,
|
||||
onToggleHidden : PropTypes.func.isRequired,
|
||||
status : ImmutablePropTypes.map,
|
||||
intl : PropTypes.object.isRequired,
|
||||
getScrollPosition : PropTypes.func,
|
||||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth: PropTypes.func,
|
||||
cachedMediaWidth: PropTypes.number,
|
||||
cacheMediaWidth : PropTypes.func,
|
||||
cachedMediaWidth : PropTypes.number,
|
||||
};
|
||||
|
||||
handleMoveUp = () => {
|
||||
const { notification, onMoveUp } = this.props;
|
||||
onMoveUp(notification.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveDown = () => {
|
||||
const { notification, onMoveDown } = this.props;
|
||||
onMoveDown(notification.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
const { notification } = this.props;
|
||||
@ -69,94 +69,131 @@ class Notification extends ImmutablePureComponent {
|
||||
} else {
|
||||
this.handleOpenProfile();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenProfile = () => {
|
||||
const { notification } = this.props;
|
||||
this.context.router.history.push(`/accounts/${notification.getIn(['account', 'id'])}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleMention = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const { notification, onMention } = this.props;
|
||||
onMention(notification.get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
const { status } = this.props;
|
||||
if (status) this.props.onFavourite(status);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyBoost = e => {
|
||||
const { status } = this.props;
|
||||
if (status) this.props.onReblog(status, e);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleHidden = () => {
|
||||
const { status } = this.props;
|
||||
if (status) this.props.onToggleHidden(status);
|
||||
}
|
||||
};
|
||||
|
||||
getHandlers () {
|
||||
getHandlers() {
|
||||
return {
|
||||
reply: this.handleMention,
|
||||
favourite: this.handleHotkeyFavourite,
|
||||
boost: this.handleHotkeyBoost,
|
||||
mention: this.handleMention,
|
||||
open: this.handleOpen,
|
||||
openProfile: this.handleOpenProfile,
|
||||
moveUp: this.handleMoveUp,
|
||||
moveDown: this.handleMoveDown,
|
||||
reply : this.handleMention,
|
||||
favourite : this.handleHotkeyFavourite,
|
||||
boost : this.handleHotkeyBoost,
|
||||
mention : this.handleMention,
|
||||
open : this.handleOpen,
|
||||
openProfile : this.handleOpenProfile,
|
||||
moveUp : this.handleMoveUp,
|
||||
moveDown : this.handleMoveDown,
|
||||
toggleHidden: this.handleHotkeyToggleHidden,
|
||||
};
|
||||
}
|
||||
|
||||
renderFollow (notification, account, link) {
|
||||
renderFollow(notification, account, link) {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-follow focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='user-plus' fixedWidth />
|
||||
</div>
|
||||
<div
|
||||
className='notification notification-follow focusable'
|
||||
tabIndex='0'
|
||||
aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}
|
||||
>
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
<FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<AccountContainer id={account.get('id')} hidden={this.props.hidden} />
|
||||
</div>
|
||||
</HotKeys>
|
||||
<span title={notification.get('created_at')}>
|
||||
<span className='media'>
|
||||
<span className='media-left'>
|
||||
<AccountContainer
|
||||
id={account.get('id')}
|
||||
hidden={this.props.hidden}
|
||||
/>
|
||||
</span >
|
||||
<span className='media-center'>
|
||||
<FormattedMessage
|
||||
id='notification.follow'
|
||||
defaultMessage='{name} followed you'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
</span >
|
||||
<span className='media-right'>
|
||||
<Icon
|
||||
id='user-plus'
|
||||
fixedWidth
|
||||
/>
|
||||
</span >
|
||||
</span >
|
||||
</span >
|
||||
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
renderFollowRequest (notification, account, link) {
|
||||
renderFollowRequest(notification, account, link) {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-follow-request focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow_request', defaultMessage: '{name} has requested to follow you' }, { name: account.get('acct') }), notification.get('created_at'))}>
|
||||
<div
|
||||
className='notification notification-follow-request focusable'
|
||||
tabIndex='0'
|
||||
aria-label={notificationForScreenReader(intl, intl.formatMessage({
|
||||
id : 'notification.follow_request',
|
||||
defaultMessage: '{name} has requested to follow you',
|
||||
}, { name: account.get('acct') }), notification.get('created_at'))}
|
||||
>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='user' fixedWidth />
|
||||
</div>
|
||||
<Icon
|
||||
id='user'
|
||||
fixedWidth
|
||||
/>
|
||||
</div >
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
<FormattedMessage id='notification.follow_request' defaultMessage='{name} has requested to follow you' values={{ name: link }} />
|
||||
</span>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='notification.follow_request'
|
||||
defaultMessage='{name} has requested to follow you'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
</span >
|
||||
</div >
|
||||
|
||||
<FollowRequestContainer id={account.get('id')} withNote={false} hidden={this.props.hidden} />
|
||||
</div>
|
||||
</HotKeys>
|
||||
<FollowRequestContainer
|
||||
id={account.get('id')}
|
||||
withNote={false}
|
||||
hidden={this.props.hidden}
|
||||
/>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
renderMention (notification) {
|
||||
renderMention(notification) {
|
||||
return (
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
@ -173,21 +210,33 @@ class Notification extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
renderFavourite (notification, link) {
|
||||
renderFavourite(notification, link) {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-favourite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.favourite, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div
|
||||
className='notification notification-favourite focusable'
|
||||
tabIndex='0'
|
||||
aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.favourite, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}
|
||||
>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='star' className='star-icon' fixedWidth />
|
||||
</div>
|
||||
<Icon
|
||||
id='star'
|
||||
className='star-icon'
|
||||
fixedWidth
|
||||
/>
|
||||
</div >
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
<FormattedMessage id='notification.favourite' defaultMessage='{name} favourited your status' values={{ name: link }} />
|
||||
</span>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='notification.favourite'
|
||||
defaultMessage='{name} favourited your status'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
</span >
|
||||
</div >
|
||||
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
@ -200,26 +249,37 @@ class Notification extends ImmutablePureComponent {
|
||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
renderReblog (notification, link) {
|
||||
renderReblog(notification, link) {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-reblog focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div
|
||||
className='notification notification-reblog focusable'
|
||||
tabIndex='0'
|
||||
aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}
|
||||
>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='retweet' fixedWidth />
|
||||
</div>
|
||||
<Icon
|
||||
id='retweet'
|
||||
fixedWidth
|
||||
/>
|
||||
</div >
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
<FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} />
|
||||
</span>
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='notification.reblog'
|
||||
defaultMessage='{name} boosted your status'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
</span >
|
||||
</div >
|
||||
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
@ -232,32 +292,45 @@ class Notification extends ImmutablePureComponent {
|
||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
renderPoll (notification, account) {
|
||||
renderPoll(notification, account) {
|
||||
const { intl } = this.props;
|
||||
const ownPoll = me === account.get('id');
|
||||
const message = ownPoll ? intl.formatMessage(messages.ownPoll) : intl.formatMessage(messages.poll);
|
||||
const ownPoll = me === account.get('id');
|
||||
const message = ownPoll ? intl.formatMessage(messages.ownPoll) : intl.formatMessage(messages.poll);
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-poll focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, message, notification.get('created_at'))}>
|
||||
<div
|
||||
className='notification notification-poll focusable'
|
||||
tabIndex='0'
|
||||
aria-label={notificationForScreenReader(intl, message, notification.get('created_at'))}
|
||||
>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='tasks' fixedWidth />
|
||||
</div>
|
||||
<Icon
|
||||
id='tasks'
|
||||
fixedWidth
|
||||
/>
|
||||
</div >
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
{ownPoll ? (
|
||||
<FormattedMessage id='notification.own_poll' defaultMessage='Your poll has ended' />
|
||||
<FormattedMessage
|
||||
id='notification.own_poll'
|
||||
defaultMessage='Your poll has ended'
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage id='notification.poll' defaultMessage='A poll you have voted in has ended' />
|
||||
<FormattedMessage
|
||||
id='notification.poll'
|
||||
defaultMessage='A poll you have voted in has ended'
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</span >
|
||||
</div >
|
||||
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
@ -270,18 +343,24 @@ class Notification extends ImmutablePureComponent {
|
||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { notification } = this.props;
|
||||
const account = notification.get('account');
|
||||
const displayNameHtml = { __html: account.get('display_name_html') };
|
||||
const link = <bdi><Permalink className='notification__display-name' href={account.get('url')} title={account.get('acct')} to={`/accounts/${account.get('id')}`} dangerouslySetInnerHTML={displayNameHtml} /></bdi>;
|
||||
const account = notification.get('account');
|
||||
const displayNameHtml = { __html: account.get('display_name_html') };
|
||||
const link = (<bdi ><Permalink
|
||||
className='notification__display-name'
|
||||
href={account.get('url')}
|
||||
title={account.get('acct')}
|
||||
to={`/accounts/${account.get('id')}`}
|
||||
dangerouslySetInnerHTML={displayNameHtml}
|
||||
/></bdi >);
|
||||
|
||||
switch(notification.get('type')) {
|
||||
switch (notification.get('type')) {
|
||||
case 'follow':
|
||||
return this.renderFollow(notification, account, link);
|
||||
case 'follow_request':
|
||||
|
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import LoadingIndicator from '../../components/loading_indicator';
|
||||
import { fetchReblogs } from '../../actions/interactions';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import Column from '../ui/components/column';
|
||||
import ScrollableList from '../../components/scrollable_list';
|
||||
@ -25,15 +25,15 @@ export default @connect(mapStateToProps)
|
||||
class Reblogs extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
params : PropTypes.object.isRequired,
|
||||
dispatch : PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
multiColumn: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
accountIds : ImmutablePropTypes.list,
|
||||
multiColumn : PropTypes.bool,
|
||||
intl : PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
componentWillMount() {
|
||||
if (!this.props.accountIds) {
|
||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||
}
|
||||
@ -47,20 +47,23 @@ class Reblogs extends ImmutablePureComponent {
|
||||
|
||||
handleRefresh = () => {
|
||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
<Column>
|
||||
<Column >
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = <FormattedMessage id='status.reblogs.empty' defaultMessage='No one has boosted this toot yet. When someone does, they will show up here.' />;
|
||||
const emptyMessage = (<FormattedMessage
|
||||
id='status.reblogs.empty'
|
||||
defaultMessage='No one has boosted this toot yet. When someone does, they will show up here.'
|
||||
/>);
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn}>
|
||||
@ -68,7 +71,12 @@ class Reblogs extends ImmutablePureComponent {
|
||||
showBackButton
|
||||
multiColumn={multiColumn}
|
||||
extraButton={(
|
||||
<button className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button>
|
||||
<button
|
||||
className='column-header__button'
|
||||
title={intl.formatMessage(messages.refresh)}
|
||||
aria-label={intl.formatMessage(messages.refresh)}
|
||||
onClick={this.handleRefresh}
|
||||
><Icon id='refresh' /></button >
|
||||
)}
|
||||
/>
|
||||
|
||||
@ -79,10 +87,14 @@ class Reblogs extends ImmutablePureComponent {
|
||||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} withNote={false} />
|
||||
(<AccountContainer
|
||||
key={id}
|
||||
id={id}
|
||||
withNote={false}
|
||||
/>),
|
||||
)}
|
||||
</ScrollableList>
|
||||
</Column>
|
||||
</ScrollableList >
|
||||
</Column >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -45,15 +45,15 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenVideo = (media, startTime) => {
|
||||
this.props.onOpenVideo(media, startTime);
|
||||
}
|
||||
};
|
||||
|
||||
handleExpandedToggle = () => {
|
||||
this.props.onToggleHidden(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
_measureHeight (heightJustChanged) {
|
||||
if (this.props.measureHeight && this.node) {
|
||||
@ -68,7 +68,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
this._measureHeight();
|
||||
}
|
||||
};
|
||||
|
||||
componentDidUpdate (prevProps, prevState) {
|
||||
this._measureHeight(prevState.height !== this.state.height);
|
||||
@ -86,7 +86,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
||||
@ -211,8 +211,14 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
<div style={outerStyle}>
|
||||
<div ref={this.setRef} className={classNames('detailed-status', { compact })}>
|
||||
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
|
||||
<div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
|
||||
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
|
||||
<div className='detailed-status__display-avatar'><Avatar
|
||||
account={status.get('account')}
|
||||
size={55}
|
||||
/></div >
|
||||
<DisplayName
|
||||
account={status.get('account')}
|
||||
localDomain={this.props.domain}
|
||||
/>
|
||||
</a>
|
||||
|
||||
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
||||
|
@ -551,8 +551,9 @@ class Status extends ImmutablePureComponent {
|
||||
className={classNames('scrollable', { fullscreen })}
|
||||
ref={this.setRef}
|
||||
>
|
||||
{/*<h2 className='debug'>ancestors:</h2 >*/}
|
||||
{ancestors}
|
||||
|
||||
{/*<h2 className='debug'>common:</h2 >*/}
|
||||
<HotKeys handlers={handlers}>
|
||||
<div
|
||||
className={classNames('focusable', 'detailed-status__wrapper')}
|
||||
@ -591,7 +592,7 @@ class Status extends ImmutablePureComponent {
|
||||
/>
|
||||
</div >
|
||||
</HotKeys >
|
||||
|
||||
{/*<h2 className='debug'>Descendants:</h2 >*/}
|
||||
{descendants}
|
||||
</div >
|
||||
</ScrollContainer >
|
||||
|
@ -26,16 +26,29 @@ export default class ActionsModal extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<li key={`${text}-${i}`}>
|
||||
<a href={href} target='_blank' rel='noopener noreferrer' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
|
||||
{icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
|
||||
<div>
|
||||
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
|
||||
<div>{meta}</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<a
|
||||
href={href}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
onClick={this.props.onClick}
|
||||
data-index={i}
|
||||
className={classNames({ active })}
|
||||
>
|
||||
{icon && <IconButton
|
||||
title={text}
|
||||
icon={icon}
|
||||
role='presentation'
|
||||
tabIndex='-1'
|
||||
inverted
|
||||
/>}
|
||||
<div >
|
||||
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div >
|
||||
<div >{meta}</div >
|
||||
</div >
|
||||
</a >
|
||||
</li >
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const status = this.props.status && (
|
||||
@ -49,7 +62,10 @@ export default class ActionsModal extends ImmutablePureComponent {
|
||||
|
||||
<a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
|
||||
<div className='status__avatar'>
|
||||
<Avatar account={this.props.status.get('account')} size={48} />
|
||||
<Avatar
|
||||
account={this.props.status.get('account')}
|
||||
size={55}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DisplayName account={this.props.status.get('account')} />
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import Button from '../../../components/button';
|
||||
import StatusContent from '../../../components/status_content';
|
||||
import Avatar from '../../../components/avatar';
|
||||
@ -37,7 +37,7 @@ class BoostModal extends ImmutablePureComponent {
|
||||
handleReblog = () => {
|
||||
this.props.onReblog(this.props.status);
|
||||
this.props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
handleAccountClick = (e) => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
@ -45,11 +45,11 @@ class BoostModal extends ImmutablePureComponent {
|
||||
this.props.onClose();
|
||||
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.button = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { status, intl } = this.props;
|
||||
@ -66,7 +66,10 @@ class BoostModal extends ImmutablePureComponent {
|
||||
|
||||
<a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
|
||||
<div className='status__avatar'>
|
||||
<Avatar account={status.get('account')} size={48} />
|
||||
<Avatar
|
||||
account={status.get('account')}
|
||||
size={55}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DisplayName account={status.get('account')} />
|
||||
|
@ -31,6 +31,8 @@ import NavigationPanel from './navigation_panel';
|
||||
|
||||
import detectPassiveEvents from 'detect-passive-events';
|
||||
import { scrollRight } from '../../../scroll';
|
||||
import LinkFooter from './link_footer';
|
||||
import InstantMessaging from './messaging/instantMessaging';
|
||||
|
||||
const componentMap = {
|
||||
'COMPOSE' : Compose,
|
||||
@ -157,10 +159,10 @@ class ColumnsArea extends ImmutablePureComponent {
|
||||
|
||||
const view = (index === columnIndex) ?
|
||||
React.cloneElement(this.props.children) :
|
||||
<ColumnLoading
|
||||
(<ColumnLoading
|
||||
title={title}
|
||||
icon={icon}
|
||||
/>;
|
||||
/>);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -187,12 +189,12 @@ class ColumnsArea extends ImmutablePureComponent {
|
||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||
|
||||
if (singleColumn) {
|
||||
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link
|
||||
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : (<Link
|
||||
key='floating-action-button'
|
||||
to='/statuses/new'
|
||||
className='floating-action-button'
|
||||
aria-label={intl.formatMessage(messages.publish)}
|
||||
><Icon id='pencil' /></Link >;
|
||||
><Icon id='pencil' /></Link >);
|
||||
|
||||
const content = columnIndex !== -1 ? (
|
||||
<ReactSwipeableViews
|
||||
@ -217,10 +219,13 @@ class ColumnsArea extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<div className='columns-area__panels'>
|
||||
|
||||
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
|
||||
<div className='columns-area__panels__pane__inner'>
|
||||
<ComposePanel />
|
||||
|
||||
</div >
|
||||
|
||||
</div >
|
||||
|
||||
<div className='columns-area__panels__main'>
|
||||
@ -233,8 +238,12 @@ class ColumnsArea extends ImmutablePureComponent {
|
||||
<NavigationPanel />
|
||||
</div >
|
||||
</div >
|
||||
|
||||
<LinkFooter withHotkeys />
|
||||
{floatingActionButton}
|
||||
<div className='hidden_nope'>
|
||||
|
||||
<InstantMessaging />
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
@ -255,17 +264,20 @@ class ColumnsArea extends ImmutablePureComponent {
|
||||
loading={this.renderLoading(column.get('id'))}
|
||||
error={this.renderError}
|
||||
>
|
||||
{SpecificComponent => <SpecificComponent
|
||||
{SpecificComponent => (<SpecificComponent
|
||||
columnId={column.get('uuid')}
|
||||
params={params}
|
||||
multiColumn {...other} />}
|
||||
multiColumn {...other}
|
||||
/>)}
|
||||
|
||||
</BundleContainer >
|
||||
);
|
||||
})}
|
||||
|
||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
|
||||
</div >
|
||||
);
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,15 +2,13 @@ import React from 'react';
|
||||
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||
import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
|
||||
import LinkFooter from './link_footer';
|
||||
|
||||
const ComposePanel = () => (
|
||||
<div className='compose-panel'>
|
||||
<SearchContainer openInRoute />
|
||||
<NavigationContainer />
|
||||
<ComposeFormContainer singleColumn />
|
||||
<LinkFooter withHotkeys />
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
|
||||
export default ComposePanel;
|
||||
|
@ -2,10 +2,11 @@ import { connect } from 'react-redux';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
import { invitesEnabled, repository, source_url, version } from 'mastodon/initial_state';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { isStaff } from '../../../initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
|
||||
@ -22,14 +23,26 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
},
|
||||
});
|
||||
|
||||
// const displaythemetoggler = true;
|
||||
export default @injectIntl
|
||||
@connect(null, mapDispatchToProps)
|
||||
class LinkFooter extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
withHotkeys: PropTypes.bool,
|
||||
onLogout : PropTypes.func.isRequired,
|
||||
intl : PropTypes.object.isRequired,
|
||||
enableChristmasSnow : PropTypes.bool,
|
||||
minimumWeekToShowSnow: PropTypes.number,
|
||||
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 => {
|
||||
@ -41,35 +54,177 @@ class LinkFooter extends React.PureComponent {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
var snowClasses = this.props.enableChristmasSnow ? 'snow-button active' : 'snow-button ';
|
||||
const navToTags = HashTagNavlinks.map(element => {
|
||||
return (
|
||||
<li
|
||||
className='tag-element btn-small btn'
|
||||
key={element}
|
||||
>
|
||||
|
||||
<NavLink
|
||||
exact
|
||||
activeClassName='active'
|
||||
to={'/timelines/tag/' + element}
|
||||
title='Mastoart'
|
||||
>
|
||||
#{element}
|
||||
</NavLink >
|
||||
</li >
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='getting-started__footer'>
|
||||
<ul >
|
||||
<li >
|
||||
<a href='https://liberapay.com/cipherbliss'>Supportez Cipherbliss</a >
|
||||
</li >
|
||||
<li >
|
||||
|
||||
<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'>
|
||||
<NavLink
|
||||
exact
|
||||
activeClassName='active'
|
||||
to={'/tk-example/'}
|
||||
title='tk example link'
|
||||
>
|
||||
example link
|
||||
</NavLink >
|
||||
<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'>
|
||||
<a href='https://mastodon.cipherbliss.com/@tykayn'>
|
||||
<i className='fa fa-paper-plane' />
|
||||
contactez nous</a >
|
||||
</li >
|
||||
<li >
|
||||
contactez nous
|
||||
</a >
|
||||
<a href='https://liberapay.com/cipherbliss'><i className='fa fa-coffee' /> Supportez
|
||||
Cipherbliss</a >
|
||||
|
||||
<a href='/admin/tags?pending_review=1'>
|
||||
<i className='fa fa-fire' />
|
||||
Trending hashtags</a >
|
||||
<a href='https://peertube.cipherbliss.com'> <i className='fa fa-play ' /> Videos</a >
|
||||
<a href='https://framadate.org/'> <i className='fa fa-calendar' /> FramaDate</a >
|
||||
<a href='https://framapad.org/'> <i className='fa fa-file-text' /> Pad</a >
|
||||
<a href='https://framagit.org/tykayn/mastodon'> <i className='fa fa-gitlab' /> Source</a >
|
||||
<hr />
|
||||
</li >
|
||||
{invitesEnabled && <li ><a
|
||||
href='/invites'
|
||||
target='_blank'
|
||||
><FormattedMessage
|
||||
id='getting_started.invite'
|
||||
defaultMessage='Invite people'
|
||||
/> ·</a >
|
||||
</li >}
|
||||
<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'
|
||||
|
@ -1,147 +0,0 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
export default class Messaging extends ImmutablePureComponent {
|
||||
|
||||
// static propTypes = {
|
||||
// following : ImmutablePropTypes.list,
|
||||
// conversations: ImmutablePropTypes.list,
|
||||
// newMessage : ImmutablePropTypes.string,
|
||||
// };
|
||||
|
||||
// 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.newMessage = 'meh';
|
||||
// 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() {
|
||||
// const contactlist = null;
|
||||
return (
|
||||
<div>
|
||||
messagerie todo
|
||||
</div>
|
||||
);
|
||||
// const contactlist = this.props.following.foreEach(elem => (
|
||||
// <li className='user-item'>
|
||||
// <div
|
||||
// className='username'
|
||||
// onClick={this.openConversationWith(elem.username)}
|
||||
// >
|
||||
// Machin
|
||||
// </div >
|
||||
// <div className='last-active'>3 min</div >
|
||||
// </li >
|
||||
// ));
|
||||
// return (
|
||||
// <div className='messaging-container'>
|
||||
// <div className='messaging-box'>
|
||||
// <div className='title'>
|
||||
// <i
|
||||
// role='img'
|
||||
// className='fa fa-envelope column-header__icon fa-fw'
|
||||
// />
|
||||
// Messaging box
|
||||
// </div >
|
||||
// <div className='user-list column-header'>
|
||||
// <h2 className='title'>User list</h2 >
|
||||
// <ul >
|
||||
// {contactlist}
|
||||
// </ul >
|
||||
// </div >
|
||||
//
|
||||
// </div >
|
||||
// <div className='conversations_list'>
|
||||
// <ul >
|
||||
// <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()}
|
||||
// >
|
||||
// {/*value={this.newMessage.toString()}*/}
|
||||
// <textarea
|
||||
// name='messager'
|
||||
// id=''
|
||||
// cols='30'
|
||||
// rows='10'
|
||||
// className='messager-textarea'
|
||||
// placeholder='allez dis nous tout'
|
||||
//
|
||||
// />
|
||||
// <input
|
||||
// type='submit'
|
||||
// name='submit'
|
||||
// value='Send'
|
||||
// />
|
||||
// </form >
|
||||
// </div >
|
||||
// </li >
|
||||
// </ul >
|
||||
// </div >
|
||||
// </div >
|
||||
// );
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,65 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Permalink from '../../../../components/permalink';
|
||||
|
||||
export default class Contact extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: PropTypes.object,
|
||||
};
|
||||
static defaultProps = {};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
account: this.props.account,
|
||||
};
|
||||
}
|
||||
|
||||
openConversationWithAccount = (accountId) => {
|
||||
dispatchEvent({ type: 'openConversation', target: accountId });
|
||||
};
|
||||
|
||||
render() {
|
||||
const account = this.props.account;
|
||||
return (
|
||||
<div
|
||||
className='contact media'
|
||||
onClick={this.openConversationWithAccount}
|
||||
>
|
||||
<div className='name media-left'>
|
||||
|
||||
<Permalink
|
||||
key={account.id}
|
||||
className='account__display-name'
|
||||
title={account.acct}
|
||||
href={account.url}
|
||||
to={`/accounts/${account.id}`}
|
||||
>
|
||||
|
||||
<div className='avatar image is-32x32'>
|
||||
<img
|
||||
className='is-rounded'
|
||||
src={this.props.account.avatar}
|
||||
alt='avatar'
|
||||
/>
|
||||
</div >
|
||||
</Permalink >
|
||||
</div >
|
||||
<div className='media-content'>
|
||||
<div className='username'>
|
||||
{this.props.account.username}
|
||||
</div >
|
||||
</div >
|
||||
<div className='media-right'>
|
||||
<div className='last-seen'>
|
||||
<i className='fa fa-clock-o' />
|
||||
</div >
|
||||
</div >
|
||||
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import accounts, { me } from '../../../../initial_state';
|
||||
import api from '../../../../api';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { mockContactList } from './mocks/mockContactList';
|
||||
import Contact from './Contact';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default class ContactsList extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
myAccount : PropTypes.array,
|
||||
showList : PropTypes.bool,
|
||||
contactList : PropTypes.array,
|
||||
conversationList: PropTypes.array,
|
||||
following_count : PropTypes.number,
|
||||
};
|
||||
static defaultProps = {
|
||||
showList : true,
|
||||
myAccount : null,
|
||||
userID : me,
|
||||
following_count : 0,
|
||||
contactList : mockContactList,
|
||||
conversationList: mockContactList,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
following_count : accounts.accounts[me].following_count,
|
||||
showList : props.showList,
|
||||
myAccount : accounts.accounts[me],
|
||||
contactList : mockContactList,
|
||||
conversationList: mockContactList,
|
||||
};
|
||||
// this.fetchContacts(1);
|
||||
}
|
||||
|
||||
submitCompose() {
|
||||
console.log('submit message');
|
||||
}
|
||||
|
||||
toggleList = () => {
|
||||
this.setState((state) => {
|
||||
return {
|
||||
showList: !state.showList,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* find followed accounts
|
||||
* @param AccountID
|
||||
* @returns {Promise<AxiosResponse<T> | void>}
|
||||
*/
|
||||
fetchContacts = (AccountID = me) => {
|
||||
return api(this.getState()).get('/api/v1/accounts/' + AccountID + '/following').then(resp => {
|
||||
console.log('resp', resp);
|
||||
}).catch(err => console.error('err', err));
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
let renderedList = (
|
||||
<span >no contacts</span >
|
||||
);
|
||||
if (this.props.contactList) {
|
||||
renderedList = this.props.contactList.map(account => {
|
||||
return (
|
||||
<li
|
||||
className='contact-item'
|
||||
key={account.id}
|
||||
>
|
||||
<Contact account={account} />
|
||||
</li >
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const showListClass = (this.state.showList ? 'active' : 'inactive');
|
||||
const classList = 'btn btn-primary toggle-list ' + showListClass;
|
||||
return (
|
||||
<div className='messaging-container'>
|
||||
<link
|
||||
rel='stylesheet'
|
||||
type='text/css'
|
||||
media='screen'
|
||||
href='https://cdn.conversejs.org/6.0.0/dist/converse.min.css'
|
||||
/>
|
||||
{/*<script*/}
|
||||
{/* src='https://cdn.conversejs.org/6.0.0/dist/converse.min.js'*/}
|
||||
{/* charSet='utf-8'*/}
|
||||
{/*></script >*/}
|
||||
{/*<script >*/}
|
||||
{/* converse.initialize({*/}
|
||||
{/* bosh_service_url : 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes*/}
|
||||
{/* show_controlbox_by_default: true*/}
|
||||
{/*});*/}
|
||||
{/*</script >*/}
|
||||
<div
|
||||
className={classNames('messaging-box ', {
|
||||
'active' : this.state.showList,
|
||||
'inactive': !this.state.showList,
|
||||
})}
|
||||
>
|
||||
<div className='card-title '>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-comment column-header__icon fa-fw'
|
||||
/>
|
||||
{this.state.following_count} contacts
|
||||
<button
|
||||
className={classList}
|
||||
onClick={this.toggleList}
|
||||
>
|
||||
{this.state.showList && (
|
||||
<i className='fa fa-caret-up' />
|
||||
)}
|
||||
{!this.state.showList && (
|
||||
<i className='fa fa-caret-left' />
|
||||
)}
|
||||
</button >
|
||||
</div >
|
||||
<div className='user-list'>
|
||||
|
||||
{this.state.showList && (
|
||||
<div className='contact-list-container'>
|
||||
<ul className='contact-list'>
|
||||
{renderedList}
|
||||
</ul >
|
||||
</div >
|
||||
)}
|
||||
</div >
|
||||
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ConversationStream from './conversation-stream';
|
||||
|
||||
/**
|
||||
* a conversation between the current logged in user and one recipient
|
||||
*/
|
||||
export default class ConversationItem extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
messages : PropTypes.array, // our and their message sorted chronologically
|
||||
recipient : PropTypes.any, // account of the person we talk to, not current logged in account
|
||||
newMessages: PropTypes.number,
|
||||
displayed : PropTypes.bool,
|
||||
};
|
||||
static defaultProps = {
|
||||
newMessages: 0,
|
||||
displayed : true,
|
||||
};
|
||||
following = [];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
console.log('props', props);
|
||||
this.state = {
|
||||
composeMessage: '',
|
||||
displayed : this.props.displayed,
|
||||
newMessages : this.props.newMessages,
|
||||
isFocused : false,
|
||||
};
|
||||
}
|
||||
|
||||
submitCompose = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
console.log('submit');
|
||||
};
|
||||
|
||||
toggleVisibility = () => {
|
||||
this.setState({ 'displayed': !this.state.displayed });
|
||||
};
|
||||
toggleFocused = () => {
|
||||
this.setState({ 'isFocused': !this.state.isFocused });
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
e.preventDefault();
|
||||
console.log('e', e);
|
||||
};
|
||||
|
||||
render() {
|
||||
const hasNewClass = this.state.newMessages ? 'has-new-message' : 'nothing-new';
|
||||
const isVisible = this.state.displayed ? 'displayed' : 'hidden';
|
||||
const isFocused = this.state.isFocused ? 'isFocused' : 'not-focused';
|
||||
const list = (
|
||||
<li className={'conversation-item ' + hasNewClass + ' ' + isVisible + ' ' + isFocused}>
|
||||
<div className='top-title'>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-comment-o column-header__icon fa-fw'
|
||||
/>
|
||||
<span className='username'>
|
||||
{this.props.recipient.username}
|
||||
</span >
|
||||
{/*<Contact account={this.props.recipient} />*/}
|
||||
{this.props.newMessages > 0 && (
|
||||
<span className='new-message-counter'>
|
||||
({this.props.newMessages})
|
||||
</span >
|
||||
)}
|
||||
<button
|
||||
className='btn btn-small'
|
||||
onClick={this.toggleVisibility}
|
||||
>
|
||||
<i
|
||||
role='img'
|
||||
className='fa fa-caret-down column-header__icon fa-fw'
|
||||
/>
|
||||
</button >
|
||||
</div >
|
||||
<ConversationStream messages={this.props.messages} />
|
||||
<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'
|
||||
onFocusCapture={this.toggleFocused}
|
||||
onBlurCapture={this.toggleFocused}
|
||||
onChange={this.handleChange}
|
||||
|
||||
/>
|
||||
<input
|
||||
type='submit'
|
||||
name='submit'
|
||||
value='Send'
|
||||
/>
|
||||
</form >
|
||||
</div >
|
||||
</li >
|
||||
);
|
||||
return (
|
||||
<ul >
|
||||
{list}
|
||||
</ul >
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default class ConversationStream extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
messages: PropTypes.array,
|
||||
};
|
||||
|
||||
render() {
|
||||
let messagesLists = (
|
||||
<div className='no_messages'>
|
||||
no messages
|
||||
</div >
|
||||
);
|
||||
|
||||
if (this.props.messages) {
|
||||
|
||||
messagesLists = this.props.messages.map(message => {
|
||||
return (
|
||||
<li
|
||||
className={'message ' + message.who}
|
||||
key={message.id}
|
||||
>
|
||||
<p >{message.text}</p >
|
||||
<div className='arrow-down' />
|
||||
</li >
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
return (
|
||||
<div className='conversation-stream'>
|
||||
<div className='messages'>
|
||||
{messagesLists}
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import ConversationItem from './conversation-item';
|
||||
import { mockRecipient, mockRecipient2 } from './mocks/mockConversation';
|
||||
|
||||
export default class ConversationStack extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
conversations: PropTypes.array,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
conversations: [mockRecipient, mockRecipient2],
|
||||
};
|
||||
|
||||
openConversationWith(account) {
|
||||
console.log('openConversationWith name', account.username);
|
||||
}
|
||||
|
||||
render() {
|
||||
let list = this.props.conversations.map(recipient => {
|
||||
|
||||
console.log('recipient', recipient);
|
||||
recipient = recipient[0];
|
||||
return (
|
||||
<li
|
||||
className='conversation-item-wrapper'
|
||||
key={'wrapper' + recipient.id}
|
||||
>
|
||||
<ConversationItem
|
||||
recipient={recipient}
|
||||
messages={recipient.messages}
|
||||
key={'ConversationItem_' + recipient.id}
|
||||
onClick={this.openConversationWith}
|
||||
/>
|
||||
</li >
|
||||
);
|
||||
|
||||
},
|
||||
)
|
||||
;
|
||||
return (
|
||||
<ul className='stack conversations_list'>
|
||||
{list}
|
||||
</ul >
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ContactsList from './contacts-list';
|
||||
import ConversationStack from './conversationStack';
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
isAccount : !!state.getIn(['accounts', props.params.accountId]),
|
||||
accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
|
||||
hasMore : !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
|
||||
blockedBy : state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||
});
|
||||
// @connect(mapStateToProps)
|
||||
/**
|
||||
* main component for IM, gathers contact list and list of conversations
|
||||
*/
|
||||
export default class InstantMessaging extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
// params : PropTypes.object.isRequired,
|
||||
dispatch : PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
accountIds : ImmutablePropTypes.list,
|
||||
hasMore : PropTypes.bool,
|
||||
blockedBy : PropTypes.bool,
|
||||
isAccount : PropTypes.bool,
|
||||
multiColumn : PropTypes.bool,
|
||||
};
|
||||
// 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() {
|
||||
//
|
||||
// };
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div className='main-instant-messaging'>
|
||||
|
||||
<ContactsList />
|
||||
<ConversationStack />
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,69 @@
|
||||
export const mockContactList = [{
|
||||
'key' : '2',
|
||||
'id' : '2',
|
||||
'username' : 'demoguy',
|
||||
'acct' : 'demoguy',
|
||||
'display_name' : '',
|
||||
'locked' : false,
|
||||
'bot' : false,
|
||||
'discoverable' : null,
|
||||
'group' : false,
|
||||
'created_at' : '2019-12-18T11:02:26.494Z',
|
||||
'note' : '<p></p>',
|
||||
'url' : 'http://localhost:3000/@demoguy',
|
||||
'avatar' : 'http://localhost:3000/avatars/original/missing.png',
|
||||
'avatar_static' : 'http://localhost:3000/avatars/original/missing.png',
|
||||
'header' : 'http://localhost:3000/headers/original/missing.png',
|
||||
'header_static' : 'http://localhost:3000/headers/original/missing.png',
|
||||
'followers_count' : 1,
|
||||
'following_count' : 1,
|
||||
'statuses_count' : 8,
|
||||
'last_status_at' : '2019-12-23T15:20:31.575Z',
|
||||
'emojis' : [],
|
||||
'fields' : [],
|
||||
'display_name_html': 'demoguy',
|
||||
'note_emojified' : '<p></p>',
|
||||
}, {
|
||||
'key' : '1',
|
||||
'id' : '1',
|
||||
'username' : 'admin',
|
||||
'acct' : 'admin',
|
||||
'display_name' : '',
|
||||
'locked' : true,
|
||||
'bot' : false,
|
||||
'discoverable' : true,
|
||||
'group' : false,
|
||||
'created_at' : '2019-12-10T13:19:44.106Z',
|
||||
'note' : '<p></p>',
|
||||
'url' : 'http://localhost:3000/@admin',
|
||||
'avatar' : 'http://localhost:3000/system/accounts/avatars/000/000/001/original/a1497a4af5fd8616.png?1576254102',
|
||||
'avatar_static' : 'http://localhost:3000/system/accounts/avatars/000/000/001/original/a1497a4af5fd8616.png?1576254102',
|
||||
'header' : 'http://localhost:3000/system/accounts/headers/000/000/001/original/f13ebf964b09ed26.png?1576254102',
|
||||
'header_static' : 'http://localhost:3000/system/accounts/headers/000/000/001/original/f13ebf964b09ed26.png?1576254102',
|
||||
'followers_count' : 4,
|
||||
'following_count' : 3,
|
||||
'statuses_count' : 31,
|
||||
'last_status_at' : '2020-01-04T15:13:32.824Z',
|
||||
'emojis' : [],
|
||||
'fields' : [
|
||||
{
|
||||
'name' : 'sssss',
|
||||
'value' : 'muuuuu',
|
||||
'verified_at' : null,
|
||||
'name_emojified' : 'sssss',
|
||||
'value_emojified': 'muuuuu',
|
||||
'value_plain' : 'muuuuu',
|
||||
},
|
||||
],
|
||||
'display_name_html': 'admin',
|
||||
'note_emojified' : '<p></p>',
|
||||
}];
|
||||
export const mockContactListShort = [{
|
||||
'key' : '2',
|
||||
'id' : '2',
|
||||
'username': 'demoguy',
|
||||
}, {
|
||||
'key' : '1',
|
||||
'id' : '1',
|
||||
'username': 'admin',
|
||||
}];
|
@ -0,0 +1,33 @@
|
||||
export const mockMessages = [
|
||||
{ id: 0, text: 'oh hello there! 😋 ', who: 'theirs' },
|
||||
{ id: 1, text: 'General Emoji', who: 'ours' },
|
||||
{ id: 2, text: 'we just achieved comedy', who: 'theirs' },
|
||||
]
|
||||
;
|
||||
export const mockMessages2 = [
|
||||
{ id: 0, text: 'oh oh oh ', who: 'theirs' },
|
||||
{ id: 1, text: 'General Emoji', who: 'ours' },
|
||||
{ id: 2, text: 'DANGER!!', who: 'theirs' },
|
||||
{ id: 3, text: 'JUST KIDDING WILL ROBINSON.', who: 'theirs' },
|
||||
]
|
||||
;
|
||||
|
||||
export const mockRecipient = [
|
||||
{
|
||||
id : 3,
|
||||
username : 'chuck norris',
|
||||
newMessages: 5,
|
||||
messages : mockMessages,
|
||||
},
|
||||
]
|
||||
;
|
||||
|
||||
export const mockRecipient2 = [
|
||||
{
|
||||
id : 4,
|
||||
username : 'the Bo Botte',
|
||||
newMessages: 0,
|
||||
messages : mockMessages2,
|
||||
},
|
||||
]
|
||||
;
|
@ -8,24 +8,53 @@ import FollowRequestsNavLink from './follow_requests_nav_link';
|
||||
import ListPanel from './list_panel';
|
||||
import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container';
|
||||
|
||||
const showMessaging = true;
|
||||
|
||||
const NavigationPanel = () => (
|
||||
<div className='navigation-panel'>
|
||||
<div className='small-texts timelines'>
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/home'
|
||||
data-preview-title-id='column.home'
|
||||
data-preview-icon='home'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='home'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.home'
|
||||
defaultMessage='Home'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/public/local'
|
||||
data-preview-title-id='column.community'
|
||||
data-preview-icon='users'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.local_timeline'
|
||||
defaultMessage='Local'
|
||||
/></NavLink >
|
||||
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
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'
|
||||
exact
|
||||
to='/timelines/public'
|
||||
data-preview-title-id='column.public'
|
||||
data-preview-icon='globe'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='globe'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.federated_timeline'
|
||||
defaultMessage='Federated'
|
||||
/></NavLink >
|
||||
|
||||
</div >
|
||||
<div className='spacer'></div >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/notifications'
|
||||
@ -37,34 +66,6 @@ const NavigationPanel = () => (
|
||||
id='tabs_bar.notifications'
|
||||
defaultMessage='Notifications'
|
||||
/></NavLink >
|
||||
<FollowRequestsNavLink />
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/public/local'
|
||||
data-preview-title-id='column.community'
|
||||
data-preview-icon='users'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.local_timeline'
|
||||
defaultMessage='Local'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
exact
|
||||
to='/timelines/public'
|
||||
data-preview-title-id='column.public'
|
||||
data-preview-icon='globe'
|
||||
><Icon
|
||||
className='column-link__icon'
|
||||
id='globe'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.federated_timeline'
|
||||
defaultMessage='Federated'
|
||||
/></NavLink >
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/timelines/direct'
|
||||
@ -76,6 +77,10 @@ const NavigationPanel = () => (
|
||||
id='navigation_bar.direct'
|
||||
defaultMessage='Direct messages'
|
||||
/></NavLink >
|
||||
<FollowRequestsNavLink />
|
||||
|
||||
<div className='spacer'></div >
|
||||
|
||||
<NavLink
|
||||
className='column-link column-link--transparent'
|
||||
to='/favourites'
|
||||
@ -151,8 +156,6 @@ const NavigationPanel = () => (
|
||||
|
||||
{showTrends && <div className='flex-spacer' />}
|
||||
{showTrends && <TrendsContainer />}
|
||||
{/*{showMessaging && <Messaging />}*/}
|
||||
{/*<Messaging />*/}
|
||||
|
||||
</div >
|
||||
);
|
||||
|
@ -8,19 +8,81 @@ import Icon from 'mastodon/components/icon';
|
||||
import NotificationsCounterIcon from './notifications_counter_icon';
|
||||
|
||||
export const links = [
|
||||
<NavLink className='tabs-bar__link' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' data-preview-icon='bell' ><Icon id='search' fixedWidth /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><Icon id='bars' fixedWidth /></NavLink>,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
to='/timelines/home'
|
||||
data-preview-title-id='column.home'
|
||||
data-preview-icon='home'
|
||||
><Icon
|
||||
id='home'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.home'
|
||||
defaultMessage='Home'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
to='/notifications'
|
||||
data-preview-title-id='column.notifications'
|
||||
data-preview-icon='bell'
|
||||
><NotificationsCounterIcon /><FormattedMessage
|
||||
id='tabs_bar.notifications'
|
||||
defaultMessage='Notifications'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
to='/timelines/public/local'
|
||||
data-preview-title-id='column.community'
|
||||
data-preview-icon='users'
|
||||
><Icon
|
||||
id='users'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.local_timeline'
|
||||
defaultMessage='Local'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
exact
|
||||
to='/timelines/public'
|
||||
data-preview-title-id='column.public'
|
||||
data-preview-icon='globe'
|
||||
><Icon
|
||||
id='globe'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.federated_timeline'
|
||||
defaultMessage='Federated'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link optional'
|
||||
to='/search'
|
||||
data-preview-title-id='tabs_bar.search'
|
||||
data-preview-icon='bell'
|
||||
><Icon
|
||||
id='search'
|
||||
fixedWidth
|
||||
/><FormattedMessage
|
||||
id='tabs_bar.search'
|
||||
defaultMessage='Search'
|
||||
/></NavLink >,
|
||||
<NavLink
|
||||
className='tabs-bar__link'
|
||||
style={{ flexGrow: '0', flexBasis: '30px' }}
|
||||
to='/getting-started'
|
||||
data-preview-title-id='getting_started.heading'
|
||||
data-preview-icon='bars'
|
||||
><Icon
|
||||
id='bars'
|
||||
fixedWidth
|
||||
/></NavLink >,
|
||||
];
|
||||
|
||||
export function getIndex (path) {
|
||||
export function getIndex(path) {
|
||||
return links.findIndex(link => link.props.to === path);
|
||||
}
|
||||
|
||||
export function getLink (index) {
|
||||
export function getLink(index) {
|
||||
return links[index].props.to;
|
||||
}
|
||||
|
||||
@ -29,13 +91,13 @@ export default @injectIntl
|
||||
class TabsBar extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
intl : PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
}
|
||||
};
|
||||
|
||||
setRef = ref => {
|
||||
this.node = ref;
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = (e) => {
|
||||
// Only apply optimization for touch devices, which we assume are slower
|
||||
@ -50,7 +112,6 @@ class TabsBar extends React.PureComponent {
|
||||
const nextTab = tabs.find(tab => tab.contains(e.target));
|
||||
const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
|
||||
|
||||
|
||||
if (currentTab !== nextTab) {
|
||||
if (currentTab) {
|
||||
currentTab.classList.remove('active');
|
||||
@ -67,19 +128,28 @@ class TabsBar extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { intl: { formatMessage } } = this.props;
|
||||
|
||||
return (
|
||||
<div className='tabs-bar__wrapper'>
|
||||
<nav className='tabs-bar' ref={this.setRef}>
|
||||
{links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
|
||||
</nav>
|
||||
|
||||
<nav
|
||||
className='tabs-bar'
|
||||
ref={this.setRef}
|
||||
>
|
||||
{links.map(link => React.cloneElement(link, {
|
||||
key : link.props.to,
|
||||
onClick : this.handleClick,
|
||||
'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }),
|
||||
}))}
|
||||
</nav >
|
||||
|
||||
<div id='tabs-bar__portal' />
|
||||
</div>
|
||||
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -10,112 +10,118 @@ import LoadingBarContainer from './containers/loading_bar_container';
|
||||
import ModalContainer from './containers/modal_container';
|
||||
import { isMobile } from '../../is_mobile';
|
||||
import { debounce } from 'lodash';
|
||||
import { uploadCompose, resetCompose } from '../../actions/compose';
|
||||
import { resetCompose, uploadCompose } from '../../actions/compose';
|
||||
import { expandHomeTimeline } from '../../actions/timelines';
|
||||
import { expandNotifications } from '../../actions/notifications';
|
||||
import { fetchFilters } from '../../actions/filters';
|
||||
import { clearHeight } from '../../actions/height_cache';
|
||||
import { focusApp, unfocusApp } from 'mastodon/actions/app';
|
||||
import { submitMarkers } from 'mastodon/actions/markers';
|
||||
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
|
||||
import { WrappedRoute, WrappedSwitch } from './util/react_router_helpers';
|
||||
import UploadArea from './components/upload_area';
|
||||
import ColumnsAreaContainer from './containers/columns_area_container';
|
||||
import DocumentTitle from './components/document_title';
|
||||
import {
|
||||
Compose,
|
||||
Status,
|
||||
GettingStarted,
|
||||
KeyboardShortcuts,
|
||||
PublicTimeline,
|
||||
CommunityTimeline,
|
||||
AccountTimeline,
|
||||
AccountGallery,
|
||||
HomeTimeline,
|
||||
AccountTimeline,
|
||||
Blocks,
|
||||
BookmarkedStatuses,
|
||||
CommunityTimeline,
|
||||
Compose,
|
||||
Directory,
|
||||
DirectTimeline,
|
||||
DomainBlocks,
|
||||
FavouritedStatuses,
|
||||
Favourites,
|
||||
Followers,
|
||||
Following,
|
||||
Reblogs,
|
||||
Favourites,
|
||||
DirectTimeline,
|
||||
HashtagTimeline,
|
||||
Notifications,
|
||||
FollowRequests,
|
||||
GenericNotFound,
|
||||
FavouritedStatuses,
|
||||
BookmarkedStatuses,
|
||||
ListTimeline,
|
||||
Blocks,
|
||||
DomainBlocks,
|
||||
Mutes,
|
||||
PinnedStatuses,
|
||||
GettingStarted,
|
||||
HashtagTimeline,
|
||||
HomeTimeline,
|
||||
KeyboardShortcuts,
|
||||
Lists,
|
||||
ListTimeline,
|
||||
Mutes,
|
||||
Notifications,
|
||||
PinnedStatuses,
|
||||
PublicTimeline,
|
||||
Reblogs,
|
||||
Search,
|
||||
Directory,
|
||||
Status,
|
||||
} from './util/async-components';
|
||||
import { me, forceSingleColumn } from '../../initial_state';
|
||||
import { forceSingleColumn, me } from '../../initial_state';
|
||||
import { previewState as previewMediaState } from './components/media_modal';
|
||||
import { previewState as previewVideoState } from './components/video_modal';
|
||||
|
||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||
// Without this it ends up in ~8 very commonly used bundles.
|
||||
import '../../components/status';
|
||||
import InstantMessaging from './components/messaging/instantMessaging';
|
||||
|
||||
const messages = defineMessages({
|
||||
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isComposing: state.getIn(['compose', 'is_composing']),
|
||||
hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
|
||||
isComposing : state.getIn(['compose', 'is_composing']),
|
||||
hasComposingText : state.getIn(['compose', 'text']).trim().length !== 0,
|
||||
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4,
|
||||
dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
|
||||
canUploadMore : !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4,
|
||||
dropdownMenuIsOpen : state.getIn(['dropdown_menu', 'openId']) !== null,
|
||||
});
|
||||
|
||||
const keyMap = {
|
||||
help: '?',
|
||||
new: 'n',
|
||||
search: 's',
|
||||
forceNew: 'option+n',
|
||||
focusColumn: ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
||||
reply: 'r',
|
||||
favourite: 'f',
|
||||
boost: 'b',
|
||||
mention: 'm',
|
||||
open: ['enter', 'o'],
|
||||
openProfile: 'p',
|
||||
moveDown: ['down', 'j'],
|
||||
moveUp: ['up', 'k'],
|
||||
back: 'backspace',
|
||||
goToHome: 'g h',
|
||||
help : '?',
|
||||
new : 'n',
|
||||
search : 's',
|
||||
forceNew : 'option+n',
|
||||
focusColumn : ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
||||
reply : 'r',
|
||||
favourite : 'f',
|
||||
boost : 'b',
|
||||
mention : 'm',
|
||||
open : ['enter', 'o'],
|
||||
openProfile : 'p',
|
||||
moveDown : ['down', 'j'],
|
||||
moveUp : ['up', 'k'],
|
||||
back : 'backspace',
|
||||
goToHome : 'g h',
|
||||
goToNotifications: 'g n',
|
||||
goToLocal: 'g l',
|
||||
goToFederated: 'g t',
|
||||
goToDirect: 'g d',
|
||||
goToStart: 'g s',
|
||||
goToFavourites: 'g f',
|
||||
goToPinned: 'g p',
|
||||
goToProfile: 'g u',
|
||||
goToBlocked: 'g b',
|
||||
goToMuted: 'g m',
|
||||
goToRequests: 'g r',
|
||||
toggleHidden: 'x',
|
||||
toggleSensitive: 'h',
|
||||
openMedia: 'e',
|
||||
goToLocal : 'g l',
|
||||
goToFederated : 'g t',
|
||||
goToDirect : 'g d',
|
||||
goToStart : 'g s',
|
||||
goToFavourites : 'g f',
|
||||
goToPinned : 'g p',
|
||||
goToProfile : 'g u',
|
||||
goToBlocked : 'g b',
|
||||
goToMuted : 'g m',
|
||||
goToRequests : 'g r',
|
||||
toggleHidden : 'x',
|
||||
toggleSensitive : 'h',
|
||||
openMedia : 'e',
|
||||
};
|
||||
|
||||
class SwitchingColumnsArea extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
location: PropTypes.object,
|
||||
children : PropTypes.node,
|
||||
location : PropTypes.object,
|
||||
onLayoutChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
mobile: isMobile(window.innerWidth),
|
||||
};
|
||||
handleLayoutChange = debounce(() => {
|
||||
// The cached heights are no longer accurate, invalidate
|
||||
this.props.onLayoutChange();
|
||||
}, 500, {
|
||||
trailing: true,
|
||||
});
|
||||
|
||||
componentWillMount () {
|
||||
componentWillMount() {
|
||||
window.addEventListener('resize', this.handleResize, { passive: true });
|
||||
|
||||
if (this.state.mobile || forceSingleColumn) {
|
||||
@ -127,7 +133,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps, prevState) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
|
||||
this.node.handleChildrenContentChange();
|
||||
}
|
||||
@ -138,21 +144,14 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
|
||||
shouldUpdateScroll (_, { location }) {
|
||||
shouldUpdateScroll(_, { location }) {
|
||||
return location.state !== previewMediaState && location.state !== previewVideoState;
|
||||
}
|
||||
|
||||
handleLayoutChange = debounce(() => {
|
||||
// The cached heights are no longer accurate, invalidate
|
||||
this.props.onLayoutChange();
|
||||
}, 500, {
|
||||
trailing: true,
|
||||
})
|
||||
|
||||
handleResize = () => {
|
||||
const mobile = isMobile(window.innerWidth);
|
||||
|
||||
@ -163,61 +162,219 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||
} else {
|
||||
this.handleLayoutChange();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
if (c) {
|
||||
this.node = c.getWrappedInstance();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
const { mobile } = this.state;
|
||||
const singleColumn = forceSingleColumn || mobile;
|
||||
const redirect = singleColumn ? <Redirect from='/' to='/timelines/home' exact /> : <Redirect from='/' to='/getting-started' exact />;
|
||||
const redirect = singleColumn ? (<Redirect
|
||||
from='/'
|
||||
to='/timelines/home'
|
||||
exact
|
||||
/>) : (<Redirect
|
||||
from='/'
|
||||
to='/getting-started'
|
||||
exact
|
||||
/>);
|
||||
|
||||
return (
|
||||
<ColumnsAreaContainer ref={this.setRef} singleColumn={singleColumn}>
|
||||
<WrappedSwitch>
|
||||
<ColumnsAreaContainer
|
||||
ref={this.setRef}
|
||||
singleColumn={singleColumn}
|
||||
>
|
||||
<WrappedSwitch >
|
||||
{redirect}
|
||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/timelines/public/local' exact component={CommunityTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute
|
||||
path='/tk-example'
|
||||
component={InstantMessaging}
|
||||
content={children}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/getting-started'
|
||||
component={GettingStarted}
|
||||
content={children}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/keyboard-shortcuts'
|
||||
component={KeyboardShortcuts}
|
||||
content={children}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/timelines/home'
|
||||
component={HomeTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/timelines/public'
|
||||
exact
|
||||
component={PublicTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/timelines/public/local'
|
||||
exact
|
||||
component={CommunityTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/timelines/direct'
|
||||
component={DirectTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/timelines/tag/:id'
|
||||
component={HashtagTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/timelines/list/:id'
|
||||
component={ListTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
|
||||
<WrappedRoute path='/notifications' component={Notifications} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
|
||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute
|
||||
path='/notifications'
|
||||
component={Notifications}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/favourites'
|
||||
component={FavouritedStatuses}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/bookmarks'
|
||||
component={BookmarkedStatuses}
|
||||
content={children}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/pinned'
|
||||
component={PinnedStatuses}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
|
||||
<WrappedRoute path='/search' component={Search} content={children} />
|
||||
<WrappedRoute path='/directory' component={Directory} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute
|
||||
path='/search'
|
||||
component={Search}
|
||||
content={children}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/directory'
|
||||
component={Directory}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
|
||||
<WrappedRoute path='/statuses/new' component={Compose} content={children} />
|
||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute
|
||||
path='/statuses/new'
|
||||
component={Compose}
|
||||
content={children}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/statuses/:statusId'
|
||||
exact
|
||||
component={Status}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/statuses/:statusId/reblogs'
|
||||
component={Reblogs}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/statuses/:statusId/favourites'
|
||||
component={Favourites}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
|
||||
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll, withReplies: true }} />
|
||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute
|
||||
path='/accounts/:accountId'
|
||||
exact
|
||||
component={AccountTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/accounts/:accountId/with_replies'
|
||||
component={AccountTimeline}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll, withReplies: true }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/accounts/:accountId/followers'
|
||||
component={Followers}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/accounts/:accountId/following'
|
||||
component={Following}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/accounts/:accountId/media'
|
||||
component={AccountGallery}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
|
||||
<WrappedRoute path='/follow_requests' component={FollowRequests} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/blocks' component={Blocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/mutes' component={Mutes} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute path='/lists' component={Lists} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
||||
<WrappedRoute
|
||||
path='/follow_requests'
|
||||
component={FollowRequests}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/blocks'
|
||||
component={Blocks}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/domain_blocks'
|
||||
component={DomainBlocks}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/mutes'
|
||||
component={Mutes}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
<WrappedRoute
|
||||
path='/lists'
|
||||
component={Lists}
|
||||
content={children}
|
||||
componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }}
|
||||
/>
|
||||
|
||||
<WrappedRoute component={GenericNotFound} content={children} />
|
||||
</WrappedSwitch>
|
||||
</ColumnsAreaContainer>
|
||||
<WrappedRoute
|
||||
component={GenericNotFound}
|
||||
content={children}
|
||||
/>
|
||||
</WrappedSwitch >
|
||||
</ColumnsAreaContainer >
|
||||
);
|
||||
}
|
||||
|
||||
@ -233,15 +390,15 @@ class UI extends React.PureComponent {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
isComposing: PropTypes.bool,
|
||||
hasComposingText: PropTypes.bool,
|
||||
dispatch : PropTypes.func.isRequired,
|
||||
children : PropTypes.node,
|
||||
isComposing : PropTypes.bool,
|
||||
hasComposingText : PropTypes.bool,
|
||||
hasMediaAttachments: PropTypes.bool,
|
||||
canUploadMore: PropTypes.bool,
|
||||
location: PropTypes.object,
|
||||
intl: PropTypes.object.isRequired,
|
||||
dropdownMenuIsOpen: PropTypes.bool,
|
||||
canUploadMore : PropTypes.bool,
|
||||
location : PropTypes.object,
|
||||
intl : PropTypes.object.isRequired,
|
||||
dropdownMenuIsOpen : PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -259,20 +416,20 @@ class UI extends React.PureComponent {
|
||||
// but we set user-friendly message for other browsers, e.g. Edge.
|
||||
e.returnValue = intl.formatMessage(messages.beforeUnload);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleWindowFocus = () => {
|
||||
this.props.dispatch(focusApp());
|
||||
}
|
||||
};
|
||||
|
||||
handleWindowBlur = () => {
|
||||
this.props.dispatch(unfocusApp());
|
||||
}
|
||||
};
|
||||
|
||||
handleLayoutChange = () => {
|
||||
// The cached heights are no longer accurate, invalidate
|
||||
this.props.dispatch(clearHeight());
|
||||
}
|
||||
};
|
||||
|
||||
handleDragEnter = (e) => {
|
||||
e.preventDefault();
|
||||
@ -288,7 +445,7 @@ class UI extends React.PureComponent {
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore) {
|
||||
this.setState({ draggingOver: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDragOver = (e) => {
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
||||
@ -303,7 +460,7 @@ class UI extends React.PureComponent {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
handleDrop = (e) => {
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return;
|
||||
@ -316,7 +473,7 @@ class UI extends React.PureComponent {
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore) {
|
||||
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDragLeave = (e) => {
|
||||
e.preventDefault();
|
||||
@ -329,15 +486,15 @@ class UI extends React.PureComponent {
|
||||
}
|
||||
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
};
|
||||
|
||||
dataTransferIsText = (dataTransfer) => {
|
||||
return (dataTransfer && Array.from(dataTransfer.types).filter((type) => type === 'text/plain').length === 1);
|
||||
}
|
||||
};
|
||||
|
||||
closeUploadModal = () => {
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
};
|
||||
|
||||
handleServiceWorkerPostMessage = ({ data }) => {
|
||||
if (data.type === 'navigate') {
|
||||
@ -345,9 +502,9 @@ class UI extends React.PureComponent {
|
||||
} else {
|
||||
console.warn('Unknown message type:', data.type);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
componentWillMount() {
|
||||
window.addEventListener('focus', this.handleWindowFocus, false);
|
||||
window.addEventListener('blur', this.handleWindowBlur, false);
|
||||
window.addEventListener('beforeunload', this.handleBeforeUnload, false);
|
||||
@ -358,7 +515,7 @@ class UI extends React.PureComponent {
|
||||
document.addEventListener('dragleave', this.handleDragLeave, false);
|
||||
document.addEventListener('dragend', this.handleDragEnd, false);
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage);
|
||||
}
|
||||
|
||||
@ -372,13 +529,13 @@ class UI extends React.PureComponent {
|
||||
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
||||
return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('focus', this.handleWindowFocus);
|
||||
window.removeEventListener('blur', this.handleWindowBlur);
|
||||
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
||||
@ -392,7 +549,7 @@ class UI extends React.PureComponent {
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyNew = e => {
|
||||
e.preventDefault();
|
||||
@ -402,7 +559,7 @@ class UI extends React.PureComponent {
|
||||
if (element) {
|
||||
element.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeySearch = e => {
|
||||
e.preventDefault();
|
||||
@ -412,15 +569,15 @@ class UI extends React.PureComponent {
|
||||
if (element) {
|
||||
element.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyForceNew = e => {
|
||||
this.handleHotkeyNew(e);
|
||||
this.props.dispatch(resetCompose());
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyFocusColumn = e => {
|
||||
const index = (e.key * 1) + 1; // First child is drawer, skip that
|
||||
const index = (e.key * 1) + 1; // First child is drawer, skip that
|
||||
const column = this.node.querySelector(`.column:nth-child(${index})`);
|
||||
if (!column) return;
|
||||
const container = column.querySelector('.scrollable');
|
||||
@ -435,7 +592,7 @@ class UI extends React.PureComponent {
|
||||
status.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyBack = () => {
|
||||
if (window.history && window.history.length === 1) {
|
||||
@ -443,11 +600,11 @@ class UI extends React.PureComponent {
|
||||
} else {
|
||||
this.context.router.history.goBack();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setHotkeysRef = c => {
|
||||
this.hotkeys = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleHelp = () => {
|
||||
if (this.props.location.pathname === '/keyboard-shortcuts') {
|
||||
@ -455,95 +612,111 @@ class UI extends React.PureComponent {
|
||||
} else {
|
||||
this.context.router.history.push('/keyboard-shortcuts');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToHome = () => {
|
||||
this.context.router.history.push('/timelines/home');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToNotifications = () => {
|
||||
this.context.router.history.push('/notifications');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToLocal = () => {
|
||||
this.context.router.history.push('/timelines/public/local');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToFederated = () => {
|
||||
this.context.router.history.push('/timelines/public');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToDirect = () => {
|
||||
this.context.router.history.push('/timelines/direct');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToStart = () => {
|
||||
this.context.router.history.push('/getting-started');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToFavourites = () => {
|
||||
this.context.router.history.push('/favourites');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToPinned = () => {
|
||||
this.context.router.history.push('/pinned');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToProfile = () => {
|
||||
this.context.router.history.push(`/accounts/${me}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToBlocked = () => {
|
||||
this.context.router.history.push('/blocks');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToMuted = () => {
|
||||
this.context.router.history.push('/mutes');
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyGoToRequests = () => {
|
||||
this.context.router.history.push('/follow_requests');
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { draggingOver } = this.state;
|
||||
const { children, isComposing, location, dropdownMenuIsOpen } = this.props;
|
||||
|
||||
const handlers = {
|
||||
help: this.handleHotkeyToggleHelp,
|
||||
new: this.handleHotkeyNew,
|
||||
search: this.handleHotkeySearch,
|
||||
forceNew: this.handleHotkeyForceNew,
|
||||
focusColumn: this.handleHotkeyFocusColumn,
|
||||
back: this.handleHotkeyBack,
|
||||
goToHome: this.handleHotkeyGoToHome,
|
||||
help : this.handleHotkeyToggleHelp,
|
||||
new : this.handleHotkeyNew,
|
||||
search : this.handleHotkeySearch,
|
||||
forceNew : this.handleHotkeyForceNew,
|
||||
focusColumn : this.handleHotkeyFocusColumn,
|
||||
back : this.handleHotkeyBack,
|
||||
goToHome : this.handleHotkeyGoToHome,
|
||||
goToNotifications: this.handleHotkeyGoToNotifications,
|
||||
goToLocal: this.handleHotkeyGoToLocal,
|
||||
goToFederated: this.handleHotkeyGoToFederated,
|
||||
goToDirect: this.handleHotkeyGoToDirect,
|
||||
goToStart: this.handleHotkeyGoToStart,
|
||||
goToFavourites: this.handleHotkeyGoToFavourites,
|
||||
goToPinned: this.handleHotkeyGoToPinned,
|
||||
goToProfile: this.handleHotkeyGoToProfile,
|
||||
goToBlocked: this.handleHotkeyGoToBlocked,
|
||||
goToMuted: this.handleHotkeyGoToMuted,
|
||||
goToRequests: this.handleHotkeyGoToRequests,
|
||||
goToLocal : this.handleHotkeyGoToLocal,
|
||||
goToFederated : this.handleHotkeyGoToFederated,
|
||||
goToDirect : this.handleHotkeyGoToDirect,
|
||||
goToStart : this.handleHotkeyGoToStart,
|
||||
goToFavourites : this.handleHotkeyGoToFavourites,
|
||||
goToPinned : this.handleHotkeyGoToPinned,
|
||||
goToProfile : this.handleHotkeyGoToProfile,
|
||||
goToBlocked : this.handleHotkeyGoToBlocked,
|
||||
goToMuted : this.handleHotkeyGoToMuted,
|
||||
goToRequests : this.handleHotkeyGoToRequests,
|
||||
};
|
||||
|
||||
return (
|
||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
||||
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
|
||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange}>
|
||||
<HotKeys
|
||||
keyMap={keyMap}
|
||||
handlers={handlers}
|
||||
ref={this.setHotkeysRef}
|
||||
attach={window}
|
||||
focused
|
||||
>
|
||||
<div
|
||||
className={classNames('ui', { 'is-composing': isComposing })}
|
||||
ref={this.setRef}
|
||||
style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}
|
||||
>
|
||||
<SwitchingColumnsArea
|
||||
location={location}
|
||||
onLayoutChange={this.handleLayoutChange}
|
||||
>
|
||||
{children}
|
||||
</SwitchingColumnsArea>
|
||||
</SwitchingColumnsArea >
|
||||
|
||||
<NotificationsContainer />
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
<ModalContainer />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
<UploadArea
|
||||
active={draggingOver}
|
||||
onClose={this.closeUploadModal}
|
||||
/>
|
||||
<DocumentTitle />
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div >
|
||||
</HotKeys >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import ColumnLoading from '../components/column_loading';
|
||||
import BundleColumnError from '../components/bundle_column_error';
|
||||
@ -9,13 +9,13 @@ import BundleContainer from '../containers/bundle_container';
|
||||
// Small wrapper to pass multiColumn to the route components
|
||||
export class WrappedSwitch extends React.PureComponent {
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { multiColumn, children } = this.props;
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Switch >
|
||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
|
||||
</Switch>
|
||||
</Switch >
|
||||
);
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ export class WrappedSwitch extends React.PureComponent {
|
||||
|
||||
WrappedSwitch.propTypes = {
|
||||
multiColumn: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
children : PropTypes.node,
|
||||
};
|
||||
|
||||
// Small Wrapper to extract the params from the route and pass
|
||||
@ -32,9 +32,9 @@ WrappedSwitch.propTypes = {
|
||||
export class WrappedRoute extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
component: PropTypes.func.isRequired,
|
||||
content: PropTypes.node,
|
||||
multiColumn: PropTypes.bool,
|
||||
component : PropTypes.func.isRequired,
|
||||
content : PropTypes.node,
|
||||
multiColumn : PropTypes.bool,
|
||||
componentParams: PropTypes.object,
|
||||
};
|
||||
|
||||
@ -46,21 +46,28 @@ export class WrappedRoute extends React.Component {
|
||||
const { component, content, multiColumn, componentParams } = this.props;
|
||||
|
||||
return (
|
||||
<BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}>
|
||||
{Component => <Component params={match.params} multiColumn={multiColumn} {...componentParams}>{content}</Component>}
|
||||
</BundleContainer>
|
||||
<BundleContainer
|
||||
fetchComponent={component}
|
||||
loading={this.renderLoading}
|
||||
error={this.renderError}
|
||||
>
|
||||
{Component => (<Component
|
||||
params={match.params}
|
||||
multiColumn={multiColumn} {...componentParams}
|
||||
>{content}</Component >)}
|
||||
</BundleContainer >
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderLoading = () => {
|
||||
return <ColumnLoading />;
|
||||
}
|
||||
};
|
||||
|
||||
renderError = (props) => {
|
||||
return <BundleColumnError {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { component: Component, content, ...rest } = this.props;
|
||||
|
||||
return <Route {...rest} render={this.renderComponent} />;
|
||||
|
@ -15,9 +15,9 @@ class ReducedMotion extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
defaultStyle: PropTypes.object,
|
||||
style: PropTypes.object,
|
||||
children: PropTypes.func,
|
||||
}
|
||||
style : PropTypes.object,
|
||||
children : PropTypes.func,
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@ -33,9 +33,12 @@ class ReducedMotion extends React.Component {
|
||||
});
|
||||
|
||||
return (
|
||||
<Motion style={style} defaultStyle={defaultStyle}>
|
||||
<Motion
|
||||
style={style}
|
||||
defaultStyle={defaultStyle}
|
||||
>
|
||||
{children}
|
||||
</Motion>
|
||||
</Motion >
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -25,5 +25,5 @@ export const usePendingItems = getMeta('use_pending_items');
|
||||
export const showTrends = getMeta('trends');
|
||||
export const title = getMeta('title');
|
||||
export const cropImages = getMeta('crop_images');
|
||||
|
||||
console.log('initialState', initialState);
|
||||
export default initialState;
|
||||
|
@ -5,7 +5,7 @@
|
||||
"account.block_domain": "Tout masquer venant de {domain}",
|
||||
"account.blocked": "Bloqué·e",
|
||||
"account.cancel_follow_request": "Annuler la demande de suivi",
|
||||
"account.direct": "Envoyer un message direct à @{name}",
|
||||
"account.direct": "Envoyer un message privé à @{name}",
|
||||
"account.domain_blocked": "Domaine caché",
|
||||
"account.edit_profile": "Modifier le profil",
|
||||
"account.endorse": "Recommander sur le profil",
|
||||
@ -52,6 +52,7 @@
|
||||
"bundle_modal_error.retry": "Réessayer",
|
||||
"column.bookmarks": "Marque pages",
|
||||
"column.blocks": "Comptes bloqués",
|
||||
"column.bookmarks": "Bookmarks",
|
||||
"column.community": "Fil public local",
|
||||
"column.direct": "Messages privés",
|
||||
"column.directory": "Parcourir les profils",
|
||||
@ -139,8 +140,9 @@
|
||||
"empty_column.account_timeline": "Aucun pouet ici !",
|
||||
"empty_column.account_unavailable": "Profil non disponible",
|
||||
"empty_column.blocks": "Vous n’avez bloqué aucun·e utilisateur·rice pour le moment.",
|
||||
"empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
|
||||
"empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !",
|
||||
"empty_column.direct": "Vous n’avez pas encore de messages directs. Lorsque vous en enverrez ou recevrez un, il s’affichera ici.",
|
||||
"empty_column.direct": "Vous n’avez pas encore de messages privés. Lorsque vous en enverrez ou recevrez un, il s’affichera ici.",
|
||||
"empty_column.domain_blocks": "Il n’y a aucun domaine caché pour le moment.",
|
||||
"empty_column.favourited_statuses": "Vous n’avez aucun pouet favoris pour le moment. Lorsque vous en mettrez un en favori, il apparaîtra ici.",
|
||||
"empty_column.favourites": "Personne n’a encore mis ce pouet en favori. Lorsque quelqu’un le fera, il apparaîtra ici.",
|
||||
@ -164,7 +166,7 @@
|
||||
"getting_started.documentation": "Documentation",
|
||||
"getting_started.heading": "Pour commencer",
|
||||
"getting_started.invite": "Inviter des gens",
|
||||
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer ou faire des rapports de bogues via {github} sur GitHub.",
|
||||
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer ou faire des rapports de bogues via {forge} sur GitHub.",
|
||||
"getting_started.security": "Sécurité",
|
||||
"getting_started.terms": "Conditions d’utilisation",
|
||||
"hashtag.column_header.tag_mode.all": "et {additional}",
|
||||
@ -205,7 +207,7 @@
|
||||
"keyboard_shortcuts.column": "pour focaliser un statut dans l’une des colonnes",
|
||||
"keyboard_shortcuts.compose": "pour focaliser la zone de rédaction",
|
||||
"keyboard_shortcuts.description": "Description",
|
||||
"keyboard_shortcuts.direct": "pour ouvrir la colonne des messages directs",
|
||||
"keyboard_shortcuts.direct": "pour ouvrir la colonne des messages privés",
|
||||
"keyboard_shortcuts.down": "pour descendre dans la liste",
|
||||
"keyboard_shortcuts.enter": "pour ouvrir le statut",
|
||||
"keyboard_shortcuts.favourite": "pour ajouter aux favoris",
|
||||
@ -253,9 +255,10 @@
|
||||
"mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
|
||||
"navigation_bar.apps": "Applications mobiles",
|
||||
"navigation_bar.blocks": "Comptes bloqués",
|
||||
"navigation_bar.bookmarks": "Bookmarks",
|
||||
"navigation_bar.community_timeline": "Fil public local",
|
||||
"navigation_bar.compose": "Rédiger un nouveau pouet",
|
||||
"navigation_bar.direct": "Messages directs",
|
||||
"navigation_bar.direct": "Messages privés",
|
||||
"navigation_bar.discover": "Découvrir",
|
||||
"navigation_bar.domain_blocks": "Domaines cachés",
|
||||
"navigation_bar.edit_profile": "Modifier le profil",
|
||||
@ -352,7 +355,7 @@
|
||||
"status.copy": "Copier le lien vers le pouet",
|
||||
"status.delete": "Effacer",
|
||||
"status.detailed_status": "Vue détaillée de la conversation",
|
||||
"status.direct": "Envoyer un message direct à @{name}",
|
||||
"status.direct": "Envoyer un message privé à @{name}",
|
||||
"status.embed": "Intégrer",
|
||||
"status.favourite": "Ajouter aux favoris",
|
||||
"status.filtered": "Filtré",
|
||||
@ -371,6 +374,7 @@
|
||||
"status.reblogged_by": "{name} a partagé :",
|
||||
"status.reblogs.empty": "Personne n’a encore partagé ce pouet. Lorsque quelqu’un le fera, il apparaîtra ici.",
|
||||
"status.redraft": "Effacer et ré-écrire",
|
||||
"status.remove_bookmark": "Enlever le marque-page",
|
||||
"status.reply": "Répondre",
|
||||
"status.replyAll": "Répondre au fil",
|
||||
"status.report": "Signaler @{name}",
|
||||
@ -386,9 +390,9 @@
|
||||
"status.unpin": "Retirer du profil",
|
||||
"suggestions.dismiss": "Rejeter la suggestion",
|
||||
"suggestions.header": "Vous pourriez être intéressé·e par…",
|
||||
"tabs_bar.federated_timeline": "Fil public global",
|
||||
"tabs_bar.federated_timeline": "Global",
|
||||
"tabs_bar.home": "Accueil",
|
||||
"tabs_bar.local_timeline": "Fil public local",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"tabs_bar.search": "Chercher",
|
||||
"time_remaining.days": "{number, plural, one {# day} other {# days}} restants",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ACCOUNT_IMPORT, ACCOUNTS_IMPORT } from '../actions/importer';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
import { fromJS, Map as ImmutableMap } from 'immutable';
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
@ -22,7 +22,7 @@ const normalizeAccounts = (state, accounts) => {
|
||||
};
|
||||
|
||||
export default function accounts(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
switch (action.type) {
|
||||
case ACCOUNT_IMPORT:
|
||||
return normalizeAccount(state, action.account);
|
||||
case ACCOUNTS_IMPORT:
|
||||
|
@ -1,21 +1,16 @@
|
||||
import {
|
||||
REBLOG_REQUEST,
|
||||
REBLOG_FAIL,
|
||||
FAVOURITE_REQUEST,
|
||||
FAVOURITE_FAIL,
|
||||
UNFAVOURITE_SUCCESS,
|
||||
BOOKMARK_REQUEST,
|
||||
BOOKMARK_FAIL,
|
||||
BOOKMARK_REQUEST,
|
||||
FAVOURITE_FAIL,
|
||||
FAVOURITE_REQUEST,
|
||||
REBLOG_FAIL,
|
||||
REBLOG_REQUEST,
|
||||
UNFAVOURITE_SUCCESS,
|
||||
} from '../actions/interactions';
|
||||
import {
|
||||
STATUS_MUTE_SUCCESS,
|
||||
STATUS_UNMUTE_SUCCESS,
|
||||
STATUS_REVEAL,
|
||||
STATUS_HIDE,
|
||||
} from '../actions/statuses';
|
||||
import { STATUS_HIDE, STATUS_MUTE_SUCCESS, STATUS_REVEAL, STATUS_UNMUTE_SUCCESS } from '../actions/statuses';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
import { fromJS, Map as ImmutableMap } from 'immutable';
|
||||
|
||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||
|
||||
@ -33,7 +28,7 @@ const deleteStatus = (state, id, references) => {
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function statuses(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
switch (action.type) {
|
||||
case STATUS_IMPORT:
|
||||
return importStatus(state, action.status);
|
||||
case STATUSES_IMPORT:
|
||||
|
29
app/javascript/mastodon/utils/snowstorm-min.js
vendored
Normal file
29
app/javascript/mastodon/utils/snowstorm-min.js
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
/** @license
|
||||
|
||||
DHTML Snowstorm! JavaScript-based snow for web pages
|
||||
Making it snow on the internets since 2003. You're welcome.
|
||||
-----------------------------------------------------------
|
||||
Version 1.44.20131208 (Previous rev: 1.44.20131125)
|
||||
Copyright (c) 2007, Scott Schiller. All rights reserved.
|
||||
Code provided under the BSD License
|
||||
http://schillmania.com/projects/snowstorm/license.txt
|
||||
*/
|
||||
var snowStorm=function(g,f){function k(a,d){isNaN(d)&&(d=0);return Math.random()*a+d}function x(){g.setTimeout(function(){a.start(!0)},20);a.events.remove(m?f:g,"mousemove",x)}function y(){(!a.excludeMobile||!D)&&x();a.events.remove(g,"load",y)}this.excludeMobile=this.autoStart=!0;this.flakesMax=128;this.flakesMaxActive=64;this.animationInterval=33;this.useGPU=!0;this.className=null;this.excludeMobile=!0;this.flakeBottom=null;this.followMouse=!0;this.snowColor="#fff";this.snowCharacter="•";this.snowStick=
|
||||
!0;this.targetElement=null;this.useMeltEffect=!0;this.usePixelPosition=this.usePositionFixed=this.useTwinkleEffect=!1;this.freezeOnBlur=!0;this.flakeRightOffset=this.flakeLeftOffset=0;this.flakeHeight=this.flakeWidth=8;this.vMaxX=5;this.vMaxY=4;this.zIndex=0;var a=this,q,m=navigator.userAgent.match(/msie/i),E=navigator.userAgent.match(/msie 6/i),D=navigator.userAgent.match(/mobile|opera m(ob|in)/i),r=m&&"BackCompat"===f.compatMode||E,h=null,n=null,l=null,p=null,s=null,z=null,A=null,v=1,t=!1,w=!1,
|
||||
u;a:{try{f.createElement("div").style.opacity="0.5"}catch(F){u=!1;break a}u=!0}var B=!1,C=f.createDocumentFragment();q=function(){function c(b){g.setTimeout(b,1E3/(a.animationInterval||20))}function d(a){return void 0!==h.style[a]?a:null}var e,b=g.requestAnimationFrame||g.webkitRequestAnimationFrame||g.mozRequestAnimationFrame||g.oRequestAnimationFrame||g.msRequestAnimationFrame||c;e=b?function(){return b.apply(g,arguments)}:null;var h;h=f.createElement("div");e={transform:{ie:d("-ms-transform"),
|
||||
moz:d("MozTransform"),opera:d("OTransform"),webkit:d("webkitTransform"),w3:d("transform"),prop:null},getAnimationFrame:e};e.transform.prop=e.transform.w3||e.transform.moz||e.transform.webkit||e.transform.ie||e.transform.opera;h=null;return e}();this.timer=null;this.flakes=[];this.active=this.disabled=!1;this.meltFrameCount=20;this.meltFrames=[];this.setXY=function(c,d,e){if(!c)return!1;a.usePixelPosition||w?(c.style.left=d-a.flakeWidth+"px",c.style.top=e-a.flakeHeight+"px"):r?(c.style.right=100-100*
|
||||
(d/h)+"%",c.style.top=Math.min(e,s-a.flakeHeight)+"px"):a.flakeBottom?(c.style.right=100-100*(d/h)+"%",c.style.top=Math.min(e,s-a.flakeHeight)+"px"):(c.style.right=100-100*(d/h)+"%",c.style.bottom=100-100*(e/l)+"%")};this.events=function(){function a(c){c=b.call(c);var d=c.length;e?(c[1]="on"+c[1],3<d&&c.pop()):3===d&&c.push(!1);return c}function d(a,b){var c=a.shift(),d=[f[b]];if(e)c[d](a[0],a[1]);else c[d].apply(c,a)}var e=!g.addEventListener&&g.attachEvent,b=Array.prototype.slice,f={add:e?"attachEvent":
|
||||
"addEventListener",remove:e?"detachEvent":"removeEventListener"};return{add:function(){d(a(arguments),"add")},remove:function(){d(a(arguments),"remove")}}}();this.randomizeWind=function(){var c;c=k(a.vMaxX,0.2);z=1===parseInt(k(2),10)?-1*c:c;A=k(a.vMaxY,0.2);if(this.flakes)for(c=0;c<this.flakes.length;c++)this.flakes[c].active&&this.flakes[c].setVelocities()};this.scrollHandler=function(){var c;p=a.flakeBottom?0:parseInt(g.scrollY||f.documentElement.scrollTop||(r?f.body.scrollTop:0),10);isNaN(p)&&
|
||||
(p=0);if(!t&&!a.flakeBottom&&a.flakes)for(c=0;c<a.flakes.length;c++)0===a.flakes[c].active&&a.flakes[c].stick()};this.resizeHandler=function(){g.innerWidth||g.innerHeight?(h=g.innerWidth-16-a.flakeRightOffset,l=a.flakeBottom||g.innerHeight):(h=(f.documentElement.clientWidth||f.body.clientWidth||f.body.scrollWidth)-(!m?8:0)-a.flakeRightOffset,l=a.flakeBottom||f.documentElement.clientHeight||f.body.clientHeight||f.body.scrollHeight);s=f.body.offsetHeight;n=parseInt(h/2,10)};this.resizeHandlerAlt=function(){h=
|
||||
a.targetElement.offsetWidth-a.flakeRightOffset;l=a.flakeBottom||a.targetElement.offsetHeight;n=parseInt(h/2,10);s=f.body.offsetHeight};this.freeze=function(){if(a.disabled)return!1;a.disabled=1;a.timer=null};this.resume=function(){if(a.disabled)a.disabled=0;else return!1;a.timerInit()};this.toggleSnow=function(){a.flakes.length?(a.active=!a.active,a.active?(a.show(),a.resume()):(a.stop(),a.freeze())):a.start()};this.stop=function(){var c;this.freeze();for(c=0;c<this.flakes.length;c++)this.flakes[c].o.style.display=
|
||||
"none";a.events.remove(g,"scroll",a.scrollHandler);a.events.remove(g,"resize",a.resizeHandler);a.freezeOnBlur&&(m?(a.events.remove(f,"focusout",a.freeze),a.events.remove(f,"focusin",a.resume)):(a.events.remove(g,"blur",a.freeze),a.events.remove(g,"focus",a.resume)))};this.show=function(){var a;for(a=0;a<this.flakes.length;a++)this.flakes[a].o.style.display="block"};this.SnowFlake=function(c,d,e){var b=this;this.type=c;this.x=d||parseInt(k(h-20),10);this.y=!isNaN(e)?e:-k(l)-12;this.vY=this.vX=null;
|
||||
this.vAmpTypes=[1,1.2,1.4,1.6,1.8];this.vAmp=this.vAmpTypes[this.type]||1;this.melting=!1;this.meltFrameCount=a.meltFrameCount;this.meltFrames=a.meltFrames;this.twinkleFrame=this.meltFrame=0;this.active=1;this.fontSize=10+10*(this.type/5);this.o=f.createElement("div");this.o.innerHTML=a.snowCharacter;a.className&&this.o.setAttribute("class",a.className);this.o.style.color=a.snowColor;this.o.style.position=t?"fixed":"absolute";a.useGPU&&q.transform.prop&&(this.o.style[q.transform.prop]="translate3d(0px, 0px, 0px)");
|
||||
this.o.style.width=a.flakeWidth+"px";this.o.style.height=a.flakeHeight+"px";this.o.style.fontFamily="arial,verdana";this.o.style.cursor="default";this.o.style.overflow="hidden";this.o.style.fontWeight="normal";this.o.style.zIndex=a.zIndex;C.appendChild(this.o);this.refresh=function(){if(isNaN(b.x)||isNaN(b.y))return!1;a.setXY(b.o,b.x,b.y)};this.stick=function(){r||a.targetElement!==f.documentElement&&a.targetElement!==f.body?b.o.style.top=l+p-a.flakeHeight+"px":a.flakeBottom?b.o.style.top=a.flakeBottom+
|
||||
"px":(b.o.style.display="none",b.o.style.bottom="0%",b.o.style.position="fixed",b.o.style.display="block")};this.vCheck=function(){0<=b.vX&&0.2>b.vX?b.vX=0.2:0>b.vX&&-0.2<b.vX&&(b.vX=-0.2);0<=b.vY&&0.2>b.vY&&(b.vY=0.2)};this.move=function(){var c=b.vX*v;b.x+=c;b.y+=b.vY*b.vAmp;b.x>=h||h-b.x<a.flakeWidth?b.x=0:0>c&&b.x-a.flakeLeftOffset<-a.flakeWidth&&(b.x=h-a.flakeWidth-1);b.refresh();l+p-b.y+a.flakeHeight<a.flakeHeight?(b.active=0,a.snowStick?b.stick():b.recycle()):(a.useMeltEffect&&(b.active&&3>
|
||||
b.type&&!b.melting&&0.998<Math.random())&&(b.melting=!0,b.melt()),a.useTwinkleEffect&&(0>b.twinkleFrame?0.97<Math.random()&&(b.twinkleFrame=parseInt(8*Math.random(),10)):(b.twinkleFrame--,u?b.o.style.opacity=b.twinkleFrame&&0===b.twinkleFrame%2?0:1:b.o.style.visibility=b.twinkleFrame&&0===b.twinkleFrame%2?"hidden":"visible")))};this.animate=function(){b.move()};this.setVelocities=function(){b.vX=z+k(0.12*a.vMaxX,0.1);b.vY=A+k(0.12*a.vMaxY,0.1)};this.setOpacity=function(a,b){if(!u)return!1;a.style.opacity=
|
||||
b};this.melt=function(){!a.useMeltEffect||!b.melting?b.recycle():b.meltFrame<b.meltFrameCount?(b.setOpacity(b.o,b.meltFrames[b.meltFrame]),b.o.style.fontSize=b.fontSize-b.fontSize*(b.meltFrame/b.meltFrameCount)+"px",b.o.style.lineHeight=a.flakeHeight+2+0.75*a.flakeHeight*(b.meltFrame/b.meltFrameCount)+"px",b.meltFrame++):b.recycle()};this.recycle=function(){b.o.style.display="none";b.o.style.position=t?"fixed":"absolute";b.o.style.bottom="auto";b.setVelocities();b.vCheck();b.meltFrame=0;b.melting=
|
||||
!1;b.setOpacity(b.o,1);b.o.style.padding="0px";b.o.style.margin="0px";b.o.style.fontSize=b.fontSize+"px";b.o.style.lineHeight=a.flakeHeight+2+"px";b.o.style.textAlign="center";b.o.style.verticalAlign="baseline";b.x=parseInt(k(h-a.flakeWidth-20),10);b.y=parseInt(-1*k(l),10)-a.flakeHeight;b.refresh();b.o.style.display="block";b.active=1};this.recycle();this.refresh()};this.snow=function(){var c=0,d=null,e,d=0;for(e=a.flakes.length;d<e;d++)1===a.flakes[d].active&&(a.flakes[d].move(),c++),a.flakes[d].melting&&
|
||||
a.flakes[d].melt();c<a.flakesMaxActive&&(d=a.flakes[parseInt(k(a.flakes.length),10)],0===d.active&&(d.melting=!0));a.timer&&q.getAnimationFrame(a.snow)};this.mouseMove=function(c){if(!a.followMouse)return!0;c=parseInt(c.clientX,10);c<n?v=-2+2*(c/n):(c-=n,v=2*(c/n))};this.createSnow=function(c,d){var e;for(e=0;e<c;e++)if(a.flakes[a.flakes.length]=new a.SnowFlake(parseInt(k(6),10)),d||e>a.flakesMaxActive)a.flakes[a.flakes.length-1].active=-1;a.targetElement.appendChild(C)};this.timerInit=function(){a.timer=
|
||||
!0;a.snow()};this.init=function(){var c;for(c=0;c<a.meltFrameCount;c++)a.meltFrames.push(1-c/a.meltFrameCount);a.randomizeWind();a.createSnow(a.flakesMax);a.events.add(g,"resize",a.resizeHandler);a.events.add(g,"scroll",a.scrollHandler);a.freezeOnBlur&&(m?(a.events.add(f,"focusout",a.freeze),a.events.add(f,"focusin",a.resume)):(a.events.add(g,"blur",a.freeze),a.events.add(g,"focus",a.resume)));a.resizeHandler();a.scrollHandler();a.followMouse&&a.events.add(m?f:g,"mousemove",a.mouseMove);a.animationInterval=
|
||||
Math.max(20,a.animationInterval);a.timerInit()};this.start=function(c){if(B){if(c)return!0}else B=!0;if("string"===typeof a.targetElement&&(c=a.targetElement,a.targetElement=f.getElementById(c),!a.targetElement))throw Error('Snowstorm: Unable to get targetElement "'+c+'"');a.targetElement||(a.targetElement=f.body||f.documentElement);a.targetElement!==f.documentElement&&a.targetElement!==f.body&&(a.resizeHandler=a.resizeHandlerAlt,a.usePixelPosition=!0);a.resizeHandler();a.usePositionFixed=a.usePositionFixed&&
|
||||
!r&&!a.flakeBottom;if(g.getComputedStyle)try{w="relative"===g.getComputedStyle(a.targetElement,null).getPropertyValue("position")}catch(d){w=!1}t=a.usePositionFixed;h&&(l&&!a.disabled)&&(a.init(),a.active=!0)};a.autoStart&&a.events.add(g,"load",y,!1);return this}(window, document);
|
@ -45,7 +45,25 @@ const onDomainBlockSeverityChange = (target) => {
|
||||
|
||||
delegate(document, '#domain_block_severity', 'change', ({ target }) => onDomainBlockSeverityChange(target));
|
||||
|
||||
const onEnableBootstrapTimelineAccountsChange = (target) => {
|
||||
const bootstrapTimelineAccountsField = document.querySelector('#form_admin_settings_bootstrap_timeline_accounts');
|
||||
|
||||
if (bootstrapTimelineAccountsField) {
|
||||
bootstrapTimelineAccountsField.disabled = !target.checked;
|
||||
if (target.checked) {
|
||||
bootstrapTimelineAccountsField.parentElement.classList.remove('disabled');
|
||||
} else {
|
||||
bootstrapTimelineAccountsField.parentElement.classList.add('disabled');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
delegate(document, '#form_admin_settings_enable_bootstrap_timeline_accounts', 'change', ({ target }) => onEnableBootstrapTimelineAccountsChange(target));
|
||||
|
||||
ready(() => {
|
||||
const input = document.getElementById('domain_block_severity');
|
||||
if (input) onDomainBlockSeverityChange(input);
|
||||
const domainBlockSeverityInput = document.getElementById('domain_block_severity');
|
||||
if (domainBlockSeverityInput) onDomainBlockSeverityChange(domainBlockSeverityInput);
|
||||
|
||||
const enableBootstrapTimelineAccounts = document.getElementById('form_admin_settings_enable_bootstrap_timeline_accounts');
|
||||
if (enableBootstrapTimelineAccounts) onEnableBootstrapTimelineAccountsChange(enableBootstrapTimelineAccounts);
|
||||
});
|
||||
|
@ -1,3 +1,6 @@
|
||||
@import '~bulma/sass/utilities/_all';
|
||||
@import '~bulma/sass/grid/columns';
|
||||
|
||||
@import 'bliss/variables';
|
||||
@import 'bliss/mixins';
|
||||
@import 'bliss/variables';
|
||||
|
@ -72,3 +72,13 @@
|
||||
color: $inverted-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
|
||||
&:before {
|
||||
content: "hidden div here";
|
||||
background: yellow;
|
||||
padding: 1em;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,3 @@
|
||||
$maximum-width: 1235px;
|
||||
$fluid-breakpoint: $maximum-width + 20px;
|
||||
$column-breakpoint: 700px;
|
||||
$small-breakpoint: 960px;
|
||||
|
||||
.container {
|
||||
box-sizing: border-box;
|
||||
@ -622,7 +618,7 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 675px) {
|
||||
@media screen and (max-width: $xs-breakpoint) {
|
||||
.header-wrapper {
|
||||
padding-top: 0;
|
||||
|
||||
@ -649,7 +645,7 @@ $small-breakpoint: 960px;
|
||||
.landing {
|
||||
margin-bottom: 100px;
|
||||
|
||||
@media screen and (max-width: 738px) {
|
||||
@media screen and (max-width: $xs-top-breakpoint) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@ -715,9 +711,9 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 44px 44px;
|
||||
width: $avatar-side;
|
||||
height: $avatar-side;
|
||||
background-size: $avatar-side $avatar-side;
|
||||
}
|
||||
}
|
||||
|
||||
@ -752,13 +748,13 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
|
||||
&__grid {
|
||||
max-width: 960px;
|
||||
max-width: $small-breakpoint;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
|
||||
grid-gap: 30px;
|
||||
|
||||
@media screen and (max-width: 738px) {
|
||||
@media screen and (max-width: $xs-top-breakpoint) {
|
||||
grid-template-columns: minmax(0, 100%);
|
||||
grid-gap: 10px;
|
||||
|
||||
|
@ -13,6 +13,4 @@ $black-emojis: '8ball' 'ant' 'back' 'black_circle' 'black_heart' 'black_large_sq
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
margin-right: 1ch;
|
||||
}
|
||||
|
||||
|
@ -384,6 +384,22 @@ hr.spacer {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
background: auto;
|
||||
}
|
||||
|
||||
hr,
|
||||
.select select,
|
||||
.textarea,
|
||||
.input {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
|
||||
&:hover {
|
||||
border: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.batch-form-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@ -160,3 +160,35 @@ button {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,6 +406,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.autosuggest-textarea__textarea {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.autosuggest-textarea__textarea,
|
||||
.spoiler-input__input {
|
||||
display: block;
|
||||
@ -2193,27 +2197,16 @@ a.account__display-name {
|
||||
}
|
||||
|
||||
.notification {
|
||||
&__message {
|
||||
margin-left: 48px + 15px * 2;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
&__favourite-icon-wrapper {
|
||||
left: -32px;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding-top: 8px;
|
||||
}
|
||||
background-color: $classic-base-color !important;
|
||||
padding: 1em 0;
|
||||
color: $ui-highlight-color;
|
||||
border-left: $ui-highlight-color 5px solid;
|
||||
|
||||
.account {
|
||||
padding: 0;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.account__avatar-wrapper {
|
||||
margin-left: 17px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2261,6 +2254,7 @@ a.account__display-name {
|
||||
.columns-area__panels__pane--compositional {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px + (285px * 1) + (10px * 1)) {
|
||||
@ -2315,6 +2309,7 @@ a.account__display-name {
|
||||
flex-direction: column;
|
||||
height: calc(100% - 10px);
|
||||
overflow-y: hidden;
|
||||
z-index: 1;
|
||||
|
||||
.navigation-bar {
|
||||
padding-top: 20px;
|
||||
@ -2659,6 +2654,28 @@ a.account__display-name {
|
||||
padding: 15px;
|
||||
text-decoration: none;
|
||||
|
||||
.timelines & {
|
||||
display: inline-block;
|
||||
width: 20%;
|
||||
display: inline-block !important;
|
||||
float: left;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
opacity: 0;
|
||||
transition: opacity ease-in 1s;
|
||||
|
||||
|
||||
}
|
||||
|
||||
&:hover {
|
||||
span {
|
||||
opacity: 1;
|
||||
transition: opacity ease-in 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
|
@ -136,7 +136,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.getting-started__footer {
|
||||
.links-started__footer {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 1em;
|
||||
width: 300px;
|
||||
z-index: 10;
|
||||
text-align: right;
|
||||
color: $ui-primary-color;
|
||||
|
||||
hr {
|
||||
border-color: $ui-primary-color;
|
||||
}
|
||||
|
||||
.fa {
|
||||
margin-right: 1ch;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
@ -150,4 +166,36 @@
|
||||
i {
|
||||
margin: 0.5ch;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-left: 2ch;
|
||||
color: $ui-primary-color;
|
||||
padding: 3px;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and(min-width: $maximum-width) {
|
||||
& {
|
||||
width: 25vw;
|
||||
}
|
||||
}
|
||||
@media all and(max-width: $maximum-width) {
|
||||
& {
|
||||
width: 22vw;
|
||||
height: 50vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
@media all and(max-width: $small-breakpoint) {
|
||||
& {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,35 +1,41 @@
|
||||
$no-columns-breakpoint: 600px;
|
||||
table{
|
||||
thead{
|
||||
table {
|
||||
thead {
|
||||
|
||||
th{
|
||||
th {
|
||||
font-weight: 800;
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
td, th{
|
||||
padding: 1rem;
|
||||
}
|
||||
a{
|
||||
|
||||
td, th {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
@extend .text-btn
|
||||
}
|
||||
}
|
||||
.table-responsive{
|
||||
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
}
|
||||
.table-striped{
|
||||
|
||||
.table-striped {
|
||||
margin: 1rem 0;
|
||||
|
||||
tr{
|
||||
tr {
|
||||
|
||||
&:odd{
|
||||
&:odd {
|
||||
background: $ui-base-lighter-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
.group-form{
|
||||
|
||||
.group-form {
|
||||
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: $font-monospace, monospace;
|
||||
font-weight: 400;
|
||||
@ -114,6 +120,7 @@ code {
|
||||
position: absolute;
|
||||
margin: 0 4px;
|
||||
margin-top: -2px;
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -957,3 +964,8 @@ code {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__publish-button-wrapper {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
@ -1,84 +1 @@
|
||||
$messagingBoxWidth: 15em;
|
||||
$messagingBoxHeight: 20em;
|
||||
|
||||
.fixed-box {
|
||||
border: solid 1px white;
|
||||
padding: 1em;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.messaging-box {
|
||||
@extend .fixed-box;
|
||||
|
||||
right: 1em;
|
||||
width: $messagingBoxWidth;
|
||||
background: $ui-base-color;
|
||||
}
|
||||
|
||||
.conversations_list {
|
||||
@extend .fixed-box;
|
||||
width: $messagingBoxWidth;
|
||||
right: $messagingBoxWidth + 5em;
|
||||
background: $ui-secondary-color;
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
&.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;
|
||||
|
||||
}
|
||||
}
|
||||
@import "../mastodon/messaging/main";
|
||||
|
@ -1,16 +1,16 @@
|
||||
// Commonly used web colors
|
||||
$black: #000000; // Black
|
||||
$white: #ffffff; // White
|
||||
$success-green: #79bd9a !default; // Padua
|
||||
$error-red: #df405a !default; // Cerise
|
||||
$warning-red: #ff5050 !default; // Sunset Orange
|
||||
$gold-star: #ca8f04 !default; // Dark Goldenrod
|
||||
$black: #111; // 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
|
||||
|
||||
// Values from the classic Mastodon UI
|
||||
$classic-base-color: #282c37; // Midnight Express
|
||||
$classic-primary-color: #9baec8; // Echo Blue
|
||||
$classic-secondary-color: #d9e1e8; // Pattens Blue
|
||||
$classic-highlight-color: #2b90d9; // Summer Sky
|
||||
$classic-base-color: #282c37; // Midnight Express
|
||||
$classic-primary-color: #9baec8; // Echo Blue
|
||||
$classic-secondary-color: #d9e1e8; // Pattens Blue
|
||||
$classic-highlight-color: #2b90d9; // Summer Sky
|
||||
|
||||
// Variables for defaults in UI
|
||||
$base-shadow-color: $black !default;
|
||||
@ -21,10 +21,10 @@ $valid-value-color: $success-green !default;
|
||||
$error-value-color: $error-red !default;
|
||||
|
||||
// Tell UI to use selected colors
|
||||
$ui-base-color: $classic-base-color !default; // Darkest
|
||||
$ui-base-color: $classic-base-color !default; // Darkest
|
||||
$ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
|
||||
$ui-primary-color: $classic-primary-color !default; // Lighter
|
||||
$ui-secondary-color: $classic-secondary-color !default; // Lightest
|
||||
$ui-primary-color: $classic-primary-color !default; // Lighter
|
||||
$ui-secondary-color: $classic-secondary-color !default; // Lightest
|
||||
$ui-highlight-color: $classic-highlight-color !default;
|
||||
|
||||
// Variables for texts
|
||||
@ -48,7 +48,15 @@ $media-modal-media-max-width: 100%;
|
||||
$media-modal-media-max-height: 80%;
|
||||
|
||||
$no-gap-breakpoint: 415px;
|
||||
$maximum-width: 1235px;
|
||||
$fluid-breakpoint: $maximum-width + 20px;
|
||||
$column-breakpoint: 700px;
|
||||
$small-breakpoint: 960px;
|
||||
$xs-top-breakpoint: 738px;
|
||||
$xs-breakpoint: 675px;
|
||||
|
||||
// pictures
|
||||
$avatar-side: 55px;
|
||||
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
||||
$font-display: 'mastodon-font-display' !default;
|
||||
$font-monospace: 'mastodon-font-monospace' !default;
|
||||
|
@ -95,9 +95,9 @@
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 44px 44px;
|
||||
width: $avatar-side;
|
||||
height: $avatar-side;
|
||||
background-size: $avatar-side $avatar-side;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -537,3 +537,32 @@ $fluid-breakpoint: $maximum-width + 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -22,3 +22,4 @@ $action-button-color: #8d9ac2;
|
||||
$inverted-text-color: $black !default;
|
||||
$lighter-text-color: darken($ui-base-color, 6%) !default;
|
||||
$light-text-color: darken($ui-primary-color, 40%) !default;
|
||||
$avatar-side: 55px;
|
||||
|
@ -1,30 +1,26 @@
|
||||
@import 'large_center';
|
||||
// custom sheet made on https://www.cipherbliss.com
|
||||
// Commonly used web colors
|
||||
$black: #fff; // Black
|
||||
$white: #000; // White
|
||||
$success-green: #6bbd77 !default; // Padua
|
||||
$error-red: #d4839b !default; // Cerise
|
||||
$warning-red: #528dc8 !default; // Sunset Orange
|
||||
$gold-star: #00ec84 !default; // Dark Goldenrod
|
||||
|
||||
// User Interface Colors
|
||||
$ui-base-color: #313644; // Midnight Express
|
||||
$ui-base-lighter-color: #bdd2d6;
|
||||
$ui-primary-color: #a1ccff; // Echo Blue
|
||||
$ui-secondary-color: #7fc0ff; // Pattens Blue
|
||||
$ui-highlight-color: #00a7d1; // Summer Sky
|
||||
|
||||
// Variables for components
|
||||
$media-modal-media-max-width: 100%;
|
||||
// put margins on top and bottom of image to avoid the screen covered by image.
|
||||
$media-modal-media-max-height: 80%;
|
||||
// fix
|
||||
|
||||
.media-gallery {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
// then we import the rest of the world
|
||||
@import 'application';
|
||||
@import 'bliss/messaging';
|
||||
//@import 'large_center';
|
||||
//// custom sheet made on https://www.cipherbliss.com
|
||||
//// Commonly used web colors
|
||||
//$black: #fff; // Black
|
||||
//$white: #000; // White
|
||||
//$success-green: #6bbd77 !default; // Padua
|
||||
//$error-red: #d4839b !default; // Cerise
|
||||
//$warning-red: #528dc8 !default; // Sunset Orange
|
||||
//$gold-star: #00ec84 !default; // Dark Goldenrod
|
||||
//
|
||||
//// User Interface Colors
|
||||
//$ui-base-color: #313644; // Midnight Express
|
||||
//$ui-base-lighter-color: #bdd2d6;
|
||||
//$ui-primary-color: #a1ccff; // Echo Blue
|
||||
//$ui-secondary-color: #7fc0ff; // Pattens Blue
|
||||
//$ui-highlight-color: #00a7d1; // Summer Sky
|
||||
//$avatar-side: 55px;
|
||||
//// 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
|
||||
//
|
||||
//// then we import the rest of the world
|
||||
//@import 'application';
|
||||
//@import 'bliss/messaging';
|
||||
|
@ -751,9 +751,9 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-size: 44px 44px;
|
||||
width: $avatar-side;
|
||||
height: $avatar-side;
|
||||
background-size: $avatar-side $avatar-side;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,18 +181,39 @@ $content-width: 840px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
&-heading {
|
||||
display: flex;
|
||||
|
||||
padding-bottom: 40px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
margin-bottom: 40px;
|
||||
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
&-actions {
|
||||
display: inline-flex;
|
||||
|
||||
& > * {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
@ -229,3 +229,7 @@ button {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.padded {
|
||||
padding: 1em;
|
||||
}
|
||||
|
@ -949,8 +949,12 @@
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
opacity: 1;
|
||||
@ -2175,7 +2179,7 @@ a.account__display-name {
|
||||
.scrollable {
|
||||
overflow: visible;
|
||||
|
||||
@supports(display: grid) {
|
||||
@supports (display: grid) {
|
||||
contain: content;
|
||||
}
|
||||
}
|
||||
@ -2333,6 +2337,9 @@ a.account__display-name {
|
||||
.columns-area__panels__pane--navigational {
|
||||
display: none;
|
||||
}
|
||||
.getting-started__footer {
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px + (285px * 2) + (10px * 2)) {
|
||||
@ -2512,13 +2519,12 @@ a.account__display-name {
|
||||
overflow-x: hidden;
|
||||
flex: 1 1 auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
will-change: transform; // improves perf in mobile Chrome
|
||||
|
||||
&.optionally-scrollable {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@supports(display: grid) { // hack to fix Chrome <57
|
||||
@supports (display: grid) { // hack to fix Chrome <57
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
@ -2535,7 +2541,7 @@ a.account__display-name {
|
||||
}
|
||||
|
||||
.scrollable.fullscreen {
|
||||
@supports(display: grid) { // hack to fix Chrome <57
|
||||
@supports (display: grid) { // hack to fix Chrome <57
|
||||
contain: none;
|
||||
}
|
||||
}
|
||||
@ -3465,9 +3471,15 @@ a.status-card.compact:hover {
|
||||
}
|
||||
|
||||
@keyframes loader-label {
|
||||
0% { opacity: 0.25; }
|
||||
30% { opacity: 1; }
|
||||
100% { opacity: 0.25; }
|
||||
0% {
|
||||
opacity: 0.25;
|
||||
}
|
||||
30% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
.video-error-cover {
|
||||
@ -3756,7 +3768,7 @@ a.status-card.compact:hover {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@supports(display: grid) { // hack to fix Chrome <57
|
||||
@supports (display: grid) { // hack to fix Chrome <57
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
@ -5161,6 +5173,7 @@ a.status-card.compact:hover {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* End Media Gallery */
|
||||
|
||||
.detailed,
|
||||
@ -5832,9 +5845,15 @@ noscript {
|
||||
}
|
||||
|
||||
@keyframes flicker {
|
||||
0% { opacity: 1; }
|
||||
30% { opacity: 0.75; }
|
||||
100% { opacity: 1; }
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
30% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 630px) and (max-height: 400px) {
|
||||
@ -5867,13 +5886,13 @@ noscript {
|
||||
& > .icon-button.close {
|
||||
will-change: opacity transform;
|
||||
transition: opacity $duration * 0.5 $delay,
|
||||
transform $duration $delay;
|
||||
transform $duration $delay;
|
||||
}
|
||||
|
||||
& > .compose__action-bar .icon-button {
|
||||
will-change: opacity transform;
|
||||
transition: opacity $duration * 0.5 $delay + $duration * 0.5,
|
||||
transform $duration $delay;
|
||||
transform $duration $delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
> svg {
|
||||
fill: $primary-text-color;
|
||||
height: 42px;
|
||||
margin-right: 10px;
|
||||
|
3
app/javascript/styles/mastodon/messaging/config.scss
Normal file
3
app/javascript/styles/mastodon/messaging/config.scss
Normal file
@ -0,0 +1,3 @@
|
||||
$messagingBoxWidth: 20em;
|
||||
$messagingBoxHeight: 100%;
|
||||
$conversationBoxHeight: 20em;
|
16
app/javascript/styles/mastodon/messaging/contacts.scss
Normal file
16
app/javascript/styles/mastodon/messaging/contacts.scss
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
.contact-list-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.contact-list {
|
||||
display: block;
|
||||
padding: 1em;
|
||||
min-height: 5em;
|
||||
list-style-type: none;
|
||||
color: white;
|
||||
|
||||
.contact {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
16
app/javascript/styles/mastodon/messaging/helpers.scss
Normal file
16
app/javascript/styles/mastodon/messaging/helpers.scss
Normal file
@ -0,0 +1,16 @@
|
||||
.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);
|
||||
border-right-width: 0;
|
||||
border-bottom-width: 0;
|
||||
border-top-width: 0;
|
||||
|
||||
}
|
6
app/javascript/styles/mastodon/messaging/main.scss
Normal file
6
app/javascript/styles/mastodon/messaging/main.scss
Normal file
@ -0,0 +1,6 @@
|
||||
@import "config";
|
||||
@import "helpers";
|
||||
@import "messaging";
|
||||
@import "contacts";
|
||||
@import "messaging-conversation";
|
||||
@import "messaging-stream";
|
@ -0,0 +1,87 @@
|
||||
.main-instant-messaging {
|
||||
|
||||
.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 {
|
||||
@extend .fixed-box;
|
||||
right: $messagingBoxWidth + 2em;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
overflow-x: auto;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.conversation-item {
|
||||
float: right;
|
||||
width: 20em;
|
||||
margin-left: 2em;
|
||||
padding: 1em;
|
||||
border-radius: 15px;
|
||||
border: 3px solid $classic-primary-color;
|
||||
-moz-border-radius-bottomleft: 0;
|
||||
-moz-border-radius-bottomright: 0;
|
||||
background: $classic-base-color;
|
||||
|
||||
|
||||
&.has-new-message {
|
||||
background: $ui-highlight-color;
|
||||
color: $classic-primary-color;
|
||||
}
|
||||
|
||||
&.is-focused {
|
||||
border-color: $ui-base-color;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.conversation-stream,
|
||||
.conversation_input {
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
.conversation-stream,
|
||||
.conversation_input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: $classic-base-color;
|
||||
color: $ui-base-color;
|
||||
float: right;
|
||||
padding: 0 1em;
|
||||
margin-left: 1em;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
|
||||
.conversation-stream {
|
||||
padding-top: 1em;
|
||||
height: 5em;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
|
||||
|
||||
.message {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.messages >
|
||||
.message {
|
||||
-webkit-border-radius: 0.5rem;
|
||||
-moz-border-radius: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0.5em 1em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ours {
|
||||
text-align: right;
|
||||
background: $light-text-color;
|
||||
float: right;
|
||||
|
||||
.arrow-down {
|
||||
border-top-color: $light-text-color;
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
}
|
||||
}
|
54
app/javascript/styles/mastodon/messaging/messaging.scss
Normal file
54
app/javascript/styles/mastodon/messaging/messaging.scss
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
.status-direct,
|
||||
.item-list .conversation {
|
||||
@extend .airmail-border;
|
||||
}
|
||||
|
||||
.main-instant-messaging {
|
||||
|
||||
.column-header__icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.messaging-box {
|
||||
@extend .fixed-box;
|
||||
|
||||
right: 1em;
|
||||
width: $messagingBoxWidth;
|
||||
background: $ui-base-color;
|
||||
height: 4em;
|
||||
color: white;
|
||||
z-index: 10;
|
||||
|
||||
&.active {
|
||||
height: $messagingBoxHeight;
|
||||
}
|
||||
|
||||
.messager-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: white;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.toggle-list {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
background: $ui-primary-color;
|
||||
border: 0;
|
||||
padding: .5em;
|
||||
width: 2em;
|
||||
|
||||
&.active {
|
||||
background: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.conversation_created-at {
|
||||
margin-right: 1em;
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
// Commonly used web colors
|
||||
$black: #000000; // Black
|
||||
$white: #ffffff; // White
|
||||
$success-green: #79bd9a !default; // Padua
|
||||
$error-red: #df405a !default; // Cerise
|
||||
$warning-red: #ff5050 !default; // Sunset Orange
|
||||
$gold-star: #ca8f04 !default; // Dark Goldenrod
|
||||
$black: #000000; // Black
|
||||
$white: #ffffff; // White
|
||||
$success-green: #79bd9a !default; // Padua
|
||||
$error-red: #df405a !default; // Cerise
|
||||
$warning-red: #ff5050 !default; // Sunset Orange
|
||||
$gold-star: #ca8f04 !default; // Dark Goldenrod
|
||||
|
||||
$red-bookmark: $warning-red;
|
||||
|
||||
// Values from the classic Mastodon UI
|
||||
$classic-base-color: #282c37; // Midnight Express
|
||||
$classic-primary-color: #9baec8; // Echo Blue
|
||||
$classic-secondary-color: #d9e1e8; // Pattens Blue
|
||||
$classic-highlight-color: #2b90d9; // Summer Sky
|
||||
$classic-base-color: #282c37; // Midnight Express
|
||||
$classic-primary-color: #9baec8; // Echo Blue
|
||||
$classic-secondary-color: #d9e1e8; // Pattens Blue
|
||||
$classic-highlight-color: #2b90d9; // Summer Sky
|
||||
|
||||
// Variables for defaults in UI
|
||||
$base-shadow-color: $black !default;
|
||||
@ -23,10 +23,10 @@ $valid-value-color: $success-green !default;
|
||||
$error-value-color: $error-red !default;
|
||||
|
||||
// Tell UI to use selected colors
|
||||
$ui-base-color: $classic-base-color !default; // Darkest
|
||||
$ui-base-color: $classic-base-color !default; // Darkest
|
||||
$ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
|
||||
$ui-primary-color: $classic-primary-color !default; // Lighter
|
||||
$ui-secondary-color: $classic-secondary-color !default; // Lightest
|
||||
$ui-primary-color: $classic-primary-color !default; // Lighter
|
||||
$ui-secondary-color: $classic-secondary-color !default; // Lightest
|
||||
$ui-highlight-color: $classic-highlight-color !default;
|
||||
|
||||
// Variables for texts
|
||||
@ -76,3 +76,5 @@ $no-gap-breakpoint: 415px;
|
||||
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
||||
$font-display: 'mastodon-font-display' !default;
|
||||
$font-monospace: 'mastodon-font-monospace' !default;
|
||||
|
||||
$avatar-side: 55px;
|
||||
|
@ -372,8 +372,8 @@
|
||||
|
||||
.account__avatar {
|
||||
flex: 0 0 auto;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
width: $avatar-side;
|
||||
height: $avatar-side;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-left: -10px;
|
||||
|
@ -5,7 +5,7 @@ class ActivityPub::Activity
|
||||
include Redisable
|
||||
|
||||
SUPPORTED_TYPES = %w(Note Question).freeze
|
||||
CONVERTED_TYPES = %w(Image Audio Video Article Page).freeze
|
||||
CONVERTED_TYPES = %w(Image Audio Video Article Page Event).freeze
|
||||
|
||||
def initialize(json, account, **options)
|
||||
@json = json
|
||||
|
@ -157,7 +157,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
return if tag['name'].blank?
|
||||
|
||||
Tag.find_or_create_by_names(tag['name']) do |hashtag|
|
||||
@tags << hashtag unless @tags.include?(hashtag)
|
||||
@tags << hashtag unless @tags.include?(hashtag) || !hashtag.valid?
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
nil
|
||||
@ -167,7 +167,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||
return if tag['href'].blank?
|
||||
|
||||
account = account_from_uri(tag['href'])
|
||||
account = ::FetchRemoteAccountService.new.call(tag['href']) if account.nil?
|
||||
account = ActivityPub::FetchRemoteAccountService.new.call(tag['href']) if account.nil?
|
||||
|
||||
return if account.nil?
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
# user_id :bigint(8)
|
||||
# dump_file_name :string
|
||||
# dump_content_type :string
|
||||
# dump_file_size :integer
|
||||
# dump_file_size :bigint
|
||||
# dump_updated_at :datetime
|
||||
# processed :boolean default(FALSE), not null
|
||||
# created_at :datetime not null
|
||||
|
@ -16,6 +16,7 @@ class Form::AdminSettings
|
||||
open_deletion
|
||||
timeline_preview
|
||||
show_staff_badge
|
||||
enable_bootstrap_timeline_accounts
|
||||
bootstrap_timeline_accounts
|
||||
theme
|
||||
min_invite_role
|
||||
@ -40,6 +41,7 @@ class Form::AdminSettings
|
||||
open_deletion
|
||||
timeline_preview
|
||||
show_staff_badge
|
||||
enable_bootstrap_timeline_accounts
|
||||
activity_api_enabled
|
||||
peers_api_enabled
|
||||
show_known_fediverse_at_about_page
|
||||
|
@ -40,7 +40,7 @@ class Form::CustomEmojiBatch
|
||||
if category_id.present?
|
||||
CustomEmojiCategory.find(category_id)
|
||||
elsif category_name.present?
|
||||
CustomEmojiCategory.create!(name: category_name)
|
||||
CustomEmojiCategory.find_or_create_by!(name: category_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -117,7 +117,7 @@ class Tag < ApplicationRecord
|
||||
class << self
|
||||
def find_or_create_by_names(name_or_names)
|
||||
Array(name_or_names).map(&method(:normalize)).uniq { |str| str.mb_chars.downcase.to_s }.map do |normalized_name|
|
||||
tag = matching_name(normalized_name).first || create!(name: normalized_name)
|
||||
tag = matching_name(normalized_name).first || create(name: normalized_name)
|
||||
|
||||
yield tag if block_given?
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
require 'rubygems/package'
|
||||
|
||||
class BackupService < BaseService
|
||||
include Payloadable
|
||||
|
||||
attr_reader :account, :backup, :collection
|
||||
|
||||
def call(backup)
|
||||
@ -20,7 +22,7 @@ class BackupService < BaseService
|
||||
|
||||
account.statuses.with_includes.reorder(nil).find_in_batches do |statuses|
|
||||
statuses.each do |status|
|
||||
item = serialize(status, ActivityPub::ActivitySerializer)
|
||||
item = serialize_payload(status, ActivityPub::ActivitySerializer, signer: @account)
|
||||
item.delete(:'@context')
|
||||
|
||||
unless item[:type] == 'Announce' || item[:object][:attachment].blank?
|
||||
|
@ -5,7 +5,7 @@ class BootstrapTimelineService < BaseService
|
||||
@source_account = source_account
|
||||
|
||||
autofollow_inviter!
|
||||
autofollow_bootstrap_timeline_accounts!
|
||||
autofollow_bootstrap_timeline_accounts! if Setting.enable_bootstrap_timeline_accounts
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -45,7 +45,7 @@ class FetchLinkCardService < BaseService
|
||||
def html
|
||||
return @html if defined?(@html)
|
||||
|
||||
Request.new(:get, @url).perform do |res|
|
||||
Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
|
||||
if res.code == 200 && res.mime_type == 'text/html'
|
||||
@html = res.body_with_limit
|
||||
@html_charset = res.charset
|
||||
|
@ -93,7 +93,7 @@ class FetchOEmbedService
|
||||
def html
|
||||
return @html if defined?(@html)
|
||||
|
||||
@html = @options[:html] || Request.new(:get, @url).perform do |res|
|
||||
@html = @options[:html] || Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
|
||||
res.code != 200 || res.mime_type != 'text/html' ? nil : res.body_with_limit
|
||||
end
|
||||
end
|
||||
|
@ -1,17 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FetchRemoteAccountService < BaseService
|
||||
def call(url, prefetched_body = nil, protocol = :ostatus)
|
||||
if prefetched_body.nil?
|
||||
resource_url, resource_options, protocol = FetchResourceService.new.call(url)
|
||||
else
|
||||
resource_url = url
|
||||
resource_options = { prefetched_body: prefetched_body }
|
||||
end
|
||||
|
||||
case protocol
|
||||
when :activitypub
|
||||
ActivityPub::FetchRemoteAccountService.new.call(resource_url, **resource_options)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,17 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FetchRemoteStatusService < BaseService
|
||||
def call(url, prefetched_body = nil, protocol = :ostatus)
|
||||
def call(url, prefetched_body = nil)
|
||||
if prefetched_body.nil?
|
||||
resource_url, resource_options, protocol = FetchResourceService.new.call(url)
|
||||
resource_url, resource_options = FetchResourceService.new.call(url)
|
||||
else
|
||||
resource_url = url
|
||||
resource_options = { prefetched_body: prefetched_body }
|
||||
end
|
||||
|
||||
case protocol
|
||||
when :activitypub
|
||||
ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options)
|
||||
end
|
||||
ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options) unless resource_url.nil?
|
||||
end
|
||||
end
|
||||
|
@ -33,7 +33,7 @@ class FetchResourceService < BaseService
|
||||
body = response.body_with_limit
|
||||
json = body_to_json(body)
|
||||
|
||||
[json['id'], { prefetched_body: body, id: true }, :activitypub] if supported_context?(json) && (equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) || expected_type?(json))
|
||||
[json['id'], { prefetched_body: body, id: true }] if supported_context?(json) && (equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) || expected_type?(json))
|
||||
elsif !terminal
|
||||
link_header = response['Link'] && parse_link_header(response)
|
||||
|
||||
|
@ -19,9 +19,9 @@ class ResolveURLService < BaseService
|
||||
|
||||
def process_url
|
||||
if equals_or_includes_any?(type, ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES)
|
||||
FetchRemoteAccountService.new.call(resource_url, body, protocol)
|
||||
ActivityPub::FetchRemoteAccountService.new.call(resource_url, prefetched_body: body)
|
||||
elsif equals_or_includes_any?(type, ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES)
|
||||
status = FetchRemoteStatusService.new.call(resource_url, body, protocol)
|
||||
status = FetchRemoteStatusService.new.call(resource_url, body)
|
||||
authorize_with @on_behalf_of, status, :show? unless status.nil?
|
||||
status
|
||||
elsif fetched_resource.nil? && @on_behalf_of.present?
|
||||
@ -45,12 +45,8 @@ class ResolveURLService < BaseService
|
||||
fetched_resource.second[:prefetched_body]
|
||||
end
|
||||
|
||||
def protocol
|
||||
fetched_resource.third
|
||||
end
|
||||
|
||||
def type
|
||||
return json_data['type'] if protocol == :activitypub
|
||||
json_data['type']
|
||||
end
|
||||
|
||||
def json_data
|
||||
|
@ -4,21 +4,12 @@
|
||||
- content_for :page_title do
|
||||
= t('admin.reports.report', id: @report.id)
|
||||
|
||||
%div{ style: 'overflow: hidden; margin-bottom: 20px' }
|
||||
- content_for :page_heading_actions do
|
||||
- if @report.unresolved?
|
||||
%div{ style: 'float: right' }
|
||||
- if @report.target_account.local?
|
||||
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@report.target_account_id, type: 'none', report_id: @report.id), class: 'button'
|
||||
= link_to t('admin.accounts.disable'), new_admin_account_action_path(@report.target_account_id, type: 'disable', report_id: @report.id), class: 'button button--destructive'
|
||||
= link_to t('admin.accounts.silence'), new_admin_account_action_path(@report.target_account_id, type: 'silence', report_id: @report.id), class: 'button button--destructive'
|
||||
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@report.target_account_id, type: 'suspend', report_id: @report.id), class: 'button button--destructive'
|
||||
%div{ style: 'float: left' }
|
||||
= link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
|
||||
= link_to t('admin.reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
|
||||
- else
|
||||
= link_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), method: :post, class: 'button'
|
||||
|
||||
%hr.spacer
|
||||
|
||||
.table-wrapper
|
||||
%table.table.inline-table
|
||||
%tbody
|
||||
@ -77,6 +68,17 @@
|
||||
|
||||
%hr.spacer
|
||||
|
||||
%div{ style: 'overflow: hidden; margin-bottom: 20px; clear: both' }
|
||||
- if @report.unresolved?
|
||||
%div{ style: 'float: right' }
|
||||
- if @report.target_account.local?
|
||||
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@report.target_account_id, type: 'none', report_id: @report.id), class: 'button'
|
||||
= link_to t('admin.accounts.disable'), new_admin_account_action_path(@report.target_account_id, type: 'disable', report_id: @report.id), class: 'button button--destructive'
|
||||
= link_to t('admin.accounts.silence'), new_admin_account_action_path(@report.target_account_id, type: 'silence', report_id: @report.id), class: 'button button--destructive'
|
||||
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@report.target_account_id, type: 'suspend', report_id: @report.id), class: 'button button--destructive'
|
||||
|
||||
%hr.spacer
|
||||
|
||||
.speech-bubble
|
||||
.speech-bubble__bubble= simple_format(@report.comment.presence || t('admin.reports.comment.none'))
|
||||
.speech-bubble__owner
|
||||
|
@ -1,3 +1,6 @@
|
||||
- content_for :header_tags do
|
||||
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
|
||||
|
||||
- content_for :page_title do
|
||||
= t('admin.settings.title')
|
||||
|
||||
@ -38,7 +41,9 @@
|
||||
%hr.spacer/
|
||||
|
||||
.fields-group
|
||||
= f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html')
|
||||
= f.input :enable_bootstrap_timeline_accounts, as: :boolean, wrapper: :with_label, label: t('admin.settings.enable_bootstrap_timeline_accounts.title')
|
||||
.fields-group
|
||||
= f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html'), disabled: !Setting.enable_bootstrap_timeline_accounts
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
|
@ -21,7 +21,12 @@
|
||||
|
||||
.content-wrapper
|
||||
.content
|
||||
%h2= yield :page_title
|
||||
.content-heading
|
||||
%h2= yield :page_title
|
||||
|
||||
- if :page_heading_actions
|
||||
.content-heading-actions
|
||||
= yield :page_heading_actions
|
||||
|
||||
= render 'application/flashes'
|
||||
|
||||
|
@ -9,11 +9,11 @@
|
||||
%td= number_to_human_size @export.total_storage
|
||||
%td
|
||||
%tr
|
||||
%th= t('accounts.posts', count: @export.total_statuses)
|
||||
%th= t('accounts.posts_tab_heading')
|
||||
%td= number_with_delimiter @export.total_statuses
|
||||
%td
|
||||
%tr
|
||||
%th= t('exports.follows')
|
||||
%th= t('admin.accounts.follows')
|
||||
%td= number_with_delimiter @export.total_follows
|
||||
%td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv)
|
||||
%tr
|
||||
@ -21,7 +21,7 @@
|
||||
%td= number_with_delimiter @export.total_lists
|
||||
%td= table_link_to 'download', t('exports.csv'), settings_exports_lists_path(format: :csv)
|
||||
%tr
|
||||
%th= t('accounts.followers', count: @export.total_followers)
|
||||
%th= t('admin.accounts.followers')
|
||||
%td= number_with_delimiter @export.total_followers
|
||||
%td
|
||||
%tr
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user