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
|
/db/*.sqlite3-journal
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
|
.eslintcache
|
||||||
/log/*
|
/log/*
|
||||||
!/log/.keep
|
!/log/.keep
|
||||||
/tmp
|
/tmp
|
||||||
|
@ -3,6 +3,7 @@ Authors
|
|||||||
|
|
||||||
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
||||||
and provided thanks to the work of the following contributors:
|
and provided thanks to the work of the following contributors:
|
||||||
|
* [Tykayn](https://framagit.org/tykayn) for the Bliss version
|
||||||
|
|
||||||
* [Gargron](https://github.com/Gargron)
|
* [Gargron](https://github.com/Gargron)
|
||||||
* [ThibG](https://github.com/ThibG)
|
* [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 'pkg-config', '~> 1.4'
|
||||||
|
|
||||||
gem 'puma', '~> 4.3'
|
gem 'puma', '~> 4.3'
|
||||||
gem 'rails', '~> 5.2.3'
|
gem 'rails', '~> 5.2.4'
|
||||||
|
gem 'sprockets', '~> 3.7'
|
||||||
gem 'thor', '~> 0.20'
|
gem 'thor', '~> 0.20'
|
||||||
|
|
||||||
gem 'hamlit-rails', '~> 0.2'
|
gem 'hamlit-rails', '~> 0.2'
|
||||||
@ -122,7 +123,7 @@ group :test do
|
|||||||
gem 'rspec-sidekiq', '~> 3.0'
|
gem 'rspec-sidekiq', '~> 3.0'
|
||||||
gem 'simplecov', '~> 0.17', require: false
|
gem 'simplecov', '~> 0.17', require: false
|
||||||
gem 'webmock', '~> 3.7'
|
gem 'webmock', '~> 3.7'
|
||||||
gem 'parallel_tests', '~> 2.29'
|
gem 'parallel_tests', '~> 2.30'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
97
Gemfile.lock
97
Gemfile.lock
@ -44,25 +44,25 @@ GIT
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (5.2.3)
|
actioncable (5.2.4)
|
||||||
actionpack (= 5.2.3)
|
actionpack (= 5.2.4)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailer (5.2.3)
|
actionmailer (5.2.4)
|
||||||
actionpack (= 5.2.3)
|
actionpack (= 5.2.4)
|
||||||
actionview (= 5.2.3)
|
actionview (= 5.2.4)
|
||||||
activejob (= 5.2.3)
|
activejob (= 5.2.4)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.2.3)
|
actionpack (5.2.4)
|
||||||
actionview (= 5.2.3)
|
actionview (= 5.2.4)
|
||||||
activesupport (= 5.2.3)
|
activesupport (= 5.2.4)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.2.3)
|
actionview (5.2.4)
|
||||||
activesupport (= 5.2.3)
|
activesupport (= 5.2.4)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
@ -73,20 +73,20 @@ GEM
|
|||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
active_record_query_trace (1.7)
|
active_record_query_trace (1.7)
|
||||||
activejob (5.2.3)
|
activejob (5.2.4)
|
||||||
activesupport (= 5.2.3)
|
activesupport (= 5.2.4)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.2.3)
|
activemodel (5.2.4)
|
||||||
activesupport (= 5.2.3)
|
activesupport (= 5.2.4)
|
||||||
activerecord (5.2.3)
|
activerecord (5.2.4)
|
||||||
activemodel (= 5.2.3)
|
activemodel (= 5.2.4)
|
||||||
activesupport (= 5.2.3)
|
activesupport (= 5.2.4)
|
||||||
arel (>= 9.0)
|
arel (>= 9.0)
|
||||||
activestorage (5.2.3)
|
activestorage (5.2.4)
|
||||||
actionpack (= 5.2.3)
|
actionpack (= 5.2.4)
|
||||||
activerecord (= 5.2.3)
|
activerecord (= 5.2.4)
|
||||||
marcel (~> 0.3.1)
|
marcel (~> 0.3.1)
|
||||||
activesupport (5.2.3)
|
activesupport (5.2.4)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
@ -218,7 +218,7 @@ GEM
|
|||||||
docile (1.3.2)
|
docile (1.3.2)
|
||||||
domain_name (0.5.20180417)
|
domain_name (0.5.20180417)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
doorkeeper (5.2.2)
|
doorkeeper (5.2.3)
|
||||||
railties (>= 5)
|
railties (>= 5)
|
||||||
dotenv (2.7.5)
|
dotenv (2.7.5)
|
||||||
dotenv-rails (2.7.5)
|
dotenv-rails (2.7.5)
|
||||||
@ -238,9 +238,9 @@ GEM
|
|||||||
erubi (1.9.0)
|
erubi (1.9.0)
|
||||||
et-orbi (1.1.6)
|
et-orbi (1.1.6)
|
||||||
tzinfo
|
tzinfo
|
||||||
excon (0.62.0)
|
excon (0.71.0)
|
||||||
fabrication (2.21.0)
|
fabrication (2.21.0)
|
||||||
faker (2.8.0)
|
faker (2.8.1)
|
||||||
i18n (>= 1.6, < 1.8)
|
i18n (>= 1.6, < 1.8)
|
||||||
faraday (0.15.4)
|
faraday (0.15.4)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
@ -324,7 +324,7 @@ GEM
|
|||||||
jmespath (1.4.0)
|
jmespath (1.4.0)
|
||||||
json (2.2.0)
|
json (2.2.0)
|
||||||
json-canonicalization (0.1.0)
|
json-canonicalization (0.1.0)
|
||||||
json-ld-preloaded (3.0.4)
|
json-ld-preloaded (3.0.6)
|
||||||
json-ld (~> 3.0)
|
json-ld (~> 3.0)
|
||||||
multi_json (~> 1.12)
|
multi_json (~> 1.12)
|
||||||
rdf (~> 3.0)
|
rdf (~> 3.0)
|
||||||
@ -380,7 +380,7 @@ GEM
|
|||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
minitest (5.13.0)
|
minitest (5.13.0)
|
||||||
msgpack (1.3.1)
|
msgpack (1.3.1)
|
||||||
multi_json (1.13.1)
|
multi_json (1.14.1)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
necromancer (0.5.1)
|
necromancer (0.5.1)
|
||||||
net-ldap (0.16.2)
|
net-ldap (0.16.2)
|
||||||
@ -424,7 +424,7 @@ GEM
|
|||||||
av (~> 0.9.0)
|
av (~> 0.9.0)
|
||||||
paperclip (>= 2.5.2)
|
paperclip (>= 2.5.2)
|
||||||
parallel (1.19.1)
|
parallel (1.19.1)
|
||||||
parallel_tests (2.29.2)
|
parallel_tests (2.30.0)
|
||||||
parallel
|
parallel
|
||||||
parser (2.6.5.0)
|
parser (2.6.5.0)
|
||||||
ast (~> 2.4.0)
|
ast (~> 2.4.0)
|
||||||
@ -469,18 +469,18 @@ GEM
|
|||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rails (5.2.3)
|
rails (5.2.4)
|
||||||
actioncable (= 5.2.3)
|
actioncable (= 5.2.4)
|
||||||
actionmailer (= 5.2.3)
|
actionmailer (= 5.2.4)
|
||||||
actionpack (= 5.2.3)
|
actionpack (= 5.2.4)
|
||||||
actionview (= 5.2.3)
|
actionview (= 5.2.4)
|
||||||
activejob (= 5.2.3)
|
activejob (= 5.2.4)
|
||||||
activemodel (= 5.2.3)
|
activemodel (= 5.2.4)
|
||||||
activerecord (= 5.2.3)
|
activerecord (= 5.2.4)
|
||||||
activestorage (= 5.2.3)
|
activestorage (= 5.2.4)
|
||||||
activesupport (= 5.2.3)
|
activesupport (= 5.2.4)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 5.2.3)
|
railties (= 5.2.4)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.4)
|
rails-controller-testing (1.0.4)
|
||||||
actionpack (>= 5.0.1.x)
|
actionpack (>= 5.0.1.x)
|
||||||
@ -496,15 +496,15 @@ GEM
|
|||||||
railties (>= 5.0, < 6)
|
railties (>= 5.0, < 6)
|
||||||
rails-settings-cached (0.6.6)
|
rails-settings-cached (0.6.6)
|
||||||
rails (>= 4.2.0)
|
rails (>= 4.2.0)
|
||||||
railties (5.2.3)
|
railties (5.2.4)
|
||||||
actionpack (= 5.2.3)
|
actionpack (= 5.2.4)
|
||||||
activesupport (= 5.2.3)
|
activesupport (= 5.2.4)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.19.0, < 2.0)
|
thor (>= 0.19.0, < 2.0)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
rake (13.0.1)
|
rake (13.0.1)
|
||||||
rdf (3.0.12)
|
rdf (3.0.13)
|
||||||
hamster (~> 3.0)
|
hamster (~> 3.0)
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
rdf-normalize (0.3.3)
|
rdf-normalize (0.3.3)
|
||||||
@ -615,7 +615,7 @@ GEM
|
|||||||
sshkit (1.20.0)
|
sshkit (1.20.0)
|
||||||
net-scp (>= 1.1.2)
|
net-scp (>= 1.1.2)
|
||||||
net-ssh (>= 2.8.0)
|
net-ssh (>= 2.8.0)
|
||||||
stackprof (0.2.13)
|
stackprof (0.2.14)
|
||||||
statsd-ruby (1.4.0)
|
statsd-ruby (1.4.0)
|
||||||
stoplight (2.2.0)
|
stoplight (2.2.0)
|
||||||
streamio-ffmpeg (3.0.2)
|
streamio-ffmpeg (3.0.2)
|
||||||
@ -667,9 +667,9 @@ GEM
|
|||||||
webpush (0.3.8)
|
webpush (0.3.8)
|
||||||
hkdf (~> 0.2)
|
hkdf (~> 0.2)
|
||||||
jwt (~> 2.0)
|
jwt (~> 2.0)
|
||||||
websocket-driver (0.7.0)
|
websocket-driver (0.7.1)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.3)
|
websocket-extensions (0.1.4)
|
||||||
wisper (2.0.1)
|
wisper (2.0.1)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
@ -753,7 +753,7 @@ DEPENDENCIES
|
|||||||
paperclip (~> 6.0)
|
paperclip (~> 6.0)
|
||||||
paperclip-av-transcoder (~> 0.6)
|
paperclip-av-transcoder (~> 0.6)
|
||||||
parallel (~> 1.19)
|
parallel (~> 1.19)
|
||||||
parallel_tests (~> 2.29)
|
parallel_tests (~> 2.30)
|
||||||
parslet
|
parslet
|
||||||
pg (~> 1.1)
|
pg (~> 1.1)
|
||||||
pghero (~> 2.4)
|
pghero (~> 2.4)
|
||||||
@ -767,7 +767,7 @@ DEPENDENCIES
|
|||||||
pundit (~> 2.1)
|
pundit (~> 2.1)
|
||||||
rack-attack (~> 6.2)
|
rack-attack (~> 6.2)
|
||||||
rack-cors (~> 1.1)
|
rack-cors (~> 1.1)
|
||||||
rails (~> 5.2.3)
|
rails (~> 5.2.4)
|
||||||
rails-controller-testing (~> 1.0)
|
rails-controller-testing (~> 1.0)
|
||||||
rails-i18n (~> 5.1)
|
rails-i18n (~> 5.1)
|
||||||
rails-settings-cached (~> 0.6)
|
rails-settings-cached (~> 0.6)
|
||||||
@ -789,6 +789,7 @@ DEPENDENCIES
|
|||||||
simple-navigation (~> 4.1)
|
simple-navigation (~> 4.1)
|
||||||
simple_form (~> 5.0)
|
simple_form (~> 5.0)
|
||||||
simplecov (~> 0.17)
|
simplecov (~> 0.17)
|
||||||
|
sprockets (~> 3.7)
|
||||||
sprockets-rails (~> 3.2)
|
sprockets-rails (~> 3.2)
|
||||||
stackprof
|
stackprof
|
||||||
stoplight (~> 2.2.0)
|
stoplight (~> 2.2.0)
|
||||||
|
20
README.md
20
README.md
@ -1,5 +1,23 @@
|
|||||||
![Mastodon](https://i.imgur.com/NhZc40l.png)
|
![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]
|
[![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]
|
[![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+
|
- **Ruby** 2.4+
|
||||||
- **Node.js** 8+
|
- **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.
|
A **Vagrant** configuration is included for development purposes.
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ export function fetchAccount(id) {
|
|||||||
dispatch,
|
dispatch,
|
||||||
getState,
|
getState,
|
||||||
db.transaction('accounts', 'read').objectStore('accounts').index('id'),
|
db.transaction('accounts', 'read').objectStore('accounts').index('id'),
|
||||||
id
|
id,
|
||||||
).then(() => db.close(), error => {
|
).then(() => db.close(), error => {
|
||||||
db.close();
|
db.close();
|
||||||
throw error;
|
throw error;
|
||||||
@ -118,29 +118,29 @@ export function fetchAccount(id) {
|
|||||||
dispatch(fetchAccountFail(id, error));
|
dispatch(fetchAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchAccountRequest(id) {
|
export function fetchAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FETCH_REQUEST,
|
type: ACCOUNT_FETCH_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchAccountSuccess() {
|
export function fetchAccountSuccess() {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FETCH_SUCCESS,
|
type: ACCOUNT_FETCH_SUCCESS,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchAccountFail(id, error) {
|
export function fetchAccountFail(id, error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FETCH_FAIL,
|
type : ACCOUNT_FETCH_FAIL,
|
||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
skipAlert: true,
|
skipAlert: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function followAccount(id, reblogs = true) {
|
export function followAccount(id, reblogs = true) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -155,7 +155,7 @@ export function followAccount(id, reblogs = true) {
|
|||||||
dispatch(followAccountFail(error, locked));
|
dispatch(followAccountFail(error, locked));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unfollowAccount(id) {
|
export function unfollowAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -167,59 +167,59 @@ export function unfollowAccount(id) {
|
|||||||
dispatch(unfollowAccountFail(error));
|
dispatch(unfollowAccountFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function followAccountRequest(id, locked) {
|
export function followAccountRequest(id, locked) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FOLLOW_REQUEST,
|
type : ACCOUNT_FOLLOW_REQUEST,
|
||||||
id,
|
id,
|
||||||
locked,
|
locked,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function followAccountSuccess(relationship, alreadyFollowing) {
|
export function followAccountSuccess(relationship, alreadyFollowing) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FOLLOW_SUCCESS,
|
type : ACCOUNT_FOLLOW_SUCCESS,
|
||||||
relationship,
|
relationship,
|
||||||
alreadyFollowing,
|
alreadyFollowing,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function followAccountFail(error, locked) {
|
export function followAccountFail(error, locked) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_FOLLOW_FAIL,
|
type : ACCOUNT_FOLLOW_FAIL,
|
||||||
error,
|
error,
|
||||||
locked,
|
locked,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unfollowAccountRequest(id) {
|
export function unfollowAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNFOLLOW_REQUEST,
|
type : ACCOUNT_UNFOLLOW_REQUEST,
|
||||||
id,
|
id,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unfollowAccountSuccess(relationship, statuses) {
|
export function unfollowAccountSuccess(relationship, statuses) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNFOLLOW_SUCCESS,
|
type : ACCOUNT_UNFOLLOW_SUCCESS,
|
||||||
relationship,
|
relationship,
|
||||||
statuses,
|
statuses,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unfollowAccountFail(error) {
|
export function unfollowAccountFail(error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNFOLLOW_FAIL,
|
type : ACCOUNT_UNFOLLOW_FAIL,
|
||||||
error,
|
error,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function blockAccount(id) {
|
export function blockAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -232,7 +232,7 @@ export function blockAccount(id) {
|
|||||||
dispatch(blockAccountFail(id, error));
|
dispatch(blockAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unblockAccount(id) {
|
export function unblockAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -244,14 +244,14 @@ export function unblockAccount(id) {
|
|||||||
dispatch(unblockAccountFail(id, error));
|
dispatch(unblockAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function blockAccountRequest(id) {
|
export function blockAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_BLOCK_REQUEST,
|
type: ACCOUNT_BLOCK_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function blockAccountSuccess(relationship, statuses) {
|
export function blockAccountSuccess(relationship, statuses) {
|
||||||
return {
|
return {
|
||||||
@ -259,36 +259,35 @@ export function blockAccountSuccess(relationship, statuses) {
|
|||||||
relationship,
|
relationship,
|
||||||
statuses,
|
statuses,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function blockAccountFail(error) {
|
export function blockAccountFail(error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_BLOCK_FAIL,
|
type: ACCOUNT_BLOCK_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unblockAccountRequest(id) {
|
export function unblockAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNBLOCK_REQUEST,
|
type: ACCOUNT_UNBLOCK_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unblockAccountSuccess(relationship) {
|
export function unblockAccountSuccess(relationship) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNBLOCK_SUCCESS,
|
type: ACCOUNT_UNBLOCK_SUCCESS,
|
||||||
relationship,
|
relationship,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unblockAccountFail(error) {
|
export function unblockAccountFail(error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNBLOCK_FAIL,
|
type: ACCOUNT_UNBLOCK_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
export function muteAccount(id, notifications) {
|
export function muteAccount(id, notifications) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -301,7 +300,7 @@ export function muteAccount(id, notifications) {
|
|||||||
dispatch(muteAccountFail(id, error));
|
dispatch(muteAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteAccount(id) {
|
export function unmuteAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -313,14 +312,14 @@ export function unmuteAccount(id) {
|
|||||||
dispatch(unmuteAccountFail(id, error));
|
dispatch(unmuteAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function muteAccountRequest(id) {
|
export function muteAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_MUTE_REQUEST,
|
type: ACCOUNT_MUTE_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function muteAccountSuccess(relationship, statuses) {
|
export function muteAccountSuccess(relationship, statuses) {
|
||||||
return {
|
return {
|
||||||
@ -328,36 +327,35 @@ export function muteAccountSuccess(relationship, statuses) {
|
|||||||
relationship,
|
relationship,
|
||||||
statuses,
|
statuses,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function muteAccountFail(error) {
|
export function muteAccountFail(error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_MUTE_FAIL,
|
type: ACCOUNT_MUTE_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteAccountRequest(id) {
|
export function unmuteAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNMUTE_REQUEST,
|
type: ACCOUNT_UNMUTE_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteAccountSuccess(relationship) {
|
export function unmuteAccountSuccess(relationship) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNMUTE_SUCCESS,
|
type: ACCOUNT_UNMUTE_SUCCESS,
|
||||||
relationship,
|
relationship,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteAccountFail(error) {
|
export function unmuteAccountFail(error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNMUTE_FAIL,
|
type: ACCOUNT_UNMUTE_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
export function fetchFollowers(id) {
|
export function fetchFollowers(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -373,14 +371,14 @@ export function fetchFollowers(id) {
|
|||||||
dispatch(fetchFollowersFail(id, error));
|
dispatch(fetchFollowersFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowersRequest(id) {
|
export function fetchFollowersRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOWERS_FETCH_REQUEST,
|
type: FOLLOWERS_FETCH_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowersSuccess(id, accounts, next) {
|
export function fetchFollowersSuccess(id, accounts, next) {
|
||||||
return {
|
return {
|
||||||
@ -389,7 +387,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
|
|||||||
accounts,
|
accounts,
|
||||||
next,
|
next,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowersFail(id, error) {
|
export function fetchFollowersFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -397,7 +395,7 @@ export function fetchFollowersFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowers(id) {
|
export function expandFollowers(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -419,14 +417,14 @@ export function expandFollowers(id) {
|
|||||||
dispatch(expandFollowersFail(id, error));
|
dispatch(expandFollowersFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowersRequest(id) {
|
export function expandFollowersRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOWERS_EXPAND_REQUEST,
|
type: FOLLOWERS_EXPAND_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowersSuccess(id, accounts, next) {
|
export function expandFollowersSuccess(id, accounts, next) {
|
||||||
return {
|
return {
|
||||||
@ -435,7 +433,7 @@ export function expandFollowersSuccess(id, accounts, next) {
|
|||||||
accounts,
|
accounts,
|
||||||
next,
|
next,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowersFail(id, error) {
|
export function expandFollowersFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -443,7 +441,7 @@ export function expandFollowersFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowing(id) {
|
export function fetchFollowing(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -459,14 +457,14 @@ export function fetchFollowing(id) {
|
|||||||
dispatch(fetchFollowingFail(id, error));
|
dispatch(fetchFollowingFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowingRequest(id) {
|
export function fetchFollowingRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOWING_FETCH_REQUEST,
|
type: FOLLOWING_FETCH_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowingSuccess(id, accounts, next) {
|
export function fetchFollowingSuccess(id, accounts, next) {
|
||||||
return {
|
return {
|
||||||
@ -475,7 +473,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
|
|||||||
accounts,
|
accounts,
|
||||||
next,
|
next,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowingFail(id, error) {
|
export function fetchFollowingFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -483,7 +481,7 @@ export function fetchFollowingFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowing(id) {
|
export function expandFollowing(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -505,14 +503,14 @@ export function expandFollowing(id) {
|
|||||||
dispatch(expandFollowingFail(id, error));
|
dispatch(expandFollowingFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowingRequest(id) {
|
export function expandFollowingRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOWING_EXPAND_REQUEST,
|
type: FOLLOWING_EXPAND_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowingSuccess(id, accounts, next) {
|
export function expandFollowingSuccess(id, accounts, next) {
|
||||||
return {
|
return {
|
||||||
@ -521,7 +519,7 @@ export function expandFollowingSuccess(id, accounts, next) {
|
|||||||
accounts,
|
accounts,
|
||||||
next,
|
next,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowingFail(id, error) {
|
export function expandFollowingFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -529,7 +527,7 @@ export function expandFollowingFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchRelationships(accountIds) {
|
export function fetchRelationships(accountIds) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -548,31 +546,31 @@ export function fetchRelationships(accountIds) {
|
|||||||
dispatch(fetchRelationshipsFail(error));
|
dispatch(fetchRelationshipsFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchRelationshipsRequest(ids) {
|
export function fetchRelationshipsRequest(ids) {
|
||||||
return {
|
return {
|
||||||
type: RELATIONSHIPS_FETCH_REQUEST,
|
type : RELATIONSHIPS_FETCH_REQUEST,
|
||||||
ids,
|
ids,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchRelationshipsSuccess(relationships) {
|
export function fetchRelationshipsSuccess(relationships) {
|
||||||
return {
|
return {
|
||||||
type: RELATIONSHIPS_FETCH_SUCCESS,
|
type : RELATIONSHIPS_FETCH_SUCCESS,
|
||||||
relationships,
|
relationships,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchRelationshipsFail(error) {
|
export function fetchRelationshipsFail(error) {
|
||||||
return {
|
return {
|
||||||
type: RELATIONSHIPS_FETCH_FAIL,
|
type : RELATIONSHIPS_FETCH_FAIL,
|
||||||
error,
|
error,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowRequests() {
|
export function fetchFollowRequests() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -584,13 +582,13 @@ export function fetchFollowRequests() {
|
|||||||
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => dispatch(fetchFollowRequestsFail(error)));
|
}).catch(error => dispatch(fetchFollowRequestsFail(error)));
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowRequestsRequest() {
|
export function fetchFollowRequestsRequest() {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUESTS_FETCH_REQUEST,
|
type: FOLLOW_REQUESTS_FETCH_REQUEST,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowRequestsSuccess(accounts, next) {
|
export function fetchFollowRequestsSuccess(accounts, next) {
|
||||||
return {
|
return {
|
||||||
@ -598,14 +596,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
|
|||||||
accounts,
|
accounts,
|
||||||
next,
|
next,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchFollowRequestsFail(error) {
|
export function fetchFollowRequestsFail(error) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUESTS_FETCH_FAIL,
|
type: FOLLOW_REQUESTS_FETCH_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowRequests() {
|
export function expandFollowRequests() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -623,13 +621,13 @@ export function expandFollowRequests() {
|
|||||||
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
|
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => dispatch(expandFollowRequestsFail(error)));
|
}).catch(error => dispatch(expandFollowRequestsFail(error)));
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowRequestsRequest() {
|
export function expandFollowRequestsRequest() {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUESTS_EXPAND_REQUEST,
|
type: FOLLOW_REQUESTS_EXPAND_REQUEST,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowRequestsSuccess(accounts, next) {
|
export function expandFollowRequestsSuccess(accounts, next) {
|
||||||
return {
|
return {
|
||||||
@ -637,14 +635,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
|
|||||||
accounts,
|
accounts,
|
||||||
next,
|
next,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function expandFollowRequestsFail(error) {
|
export function expandFollowRequestsFail(error) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUESTS_EXPAND_FAIL,
|
type: FOLLOW_REQUESTS_EXPAND_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function authorizeFollowRequest(id) {
|
export function authorizeFollowRequest(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -655,21 +653,21 @@ export function authorizeFollowRequest(id) {
|
|||||||
.then(() => dispatch(authorizeFollowRequestSuccess(id)))
|
.then(() => dispatch(authorizeFollowRequestSuccess(id)))
|
||||||
.catch(error => dispatch(authorizeFollowRequestFail(id, error)));
|
.catch(error => dispatch(authorizeFollowRequestFail(id, error)));
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function authorizeFollowRequestRequest(id) {
|
export function authorizeFollowRequestRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
|
type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function authorizeFollowRequestSuccess(id) {
|
export function authorizeFollowRequestSuccess(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function authorizeFollowRequestFail(id, error) {
|
export function authorizeFollowRequestFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -677,8 +675,7 @@ export function authorizeFollowRequestFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
export function rejectFollowRequest(id) {
|
export function rejectFollowRequest(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -689,21 +686,21 @@ export function rejectFollowRequest(id) {
|
|||||||
.then(() => dispatch(rejectFollowRequestSuccess(id)))
|
.then(() => dispatch(rejectFollowRequestSuccess(id)))
|
||||||
.catch(error => dispatch(rejectFollowRequestFail(id, error)));
|
.catch(error => dispatch(rejectFollowRequestFail(id, error)));
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function rejectFollowRequestRequest(id) {
|
export function rejectFollowRequestRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUEST_REJECT_REQUEST,
|
type: FOLLOW_REQUEST_REJECT_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function rejectFollowRequestSuccess(id) {
|
export function rejectFollowRequestSuccess(id) {
|
||||||
return {
|
return {
|
||||||
type: FOLLOW_REQUEST_REJECT_SUCCESS,
|
type: FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function rejectFollowRequestFail(id, error) {
|
export function rejectFollowRequestFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -711,7 +708,7 @@ export function rejectFollowRequestFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function pinAccount(id) {
|
export function pinAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -723,7 +720,7 @@ export function pinAccount(id) {
|
|||||||
dispatch(pinAccountFail(error));
|
dispatch(pinAccountFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unpinAccount(id) {
|
export function unpinAccount(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -735,46 +732,46 @@ export function unpinAccount(id) {
|
|||||||
dispatch(unpinAccountFail(error));
|
dispatch(unpinAccountFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function pinAccountRequest(id) {
|
export function pinAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_PIN_REQUEST,
|
type: ACCOUNT_PIN_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function pinAccountSuccess(relationship) {
|
export function pinAccountSuccess(relationship) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_PIN_SUCCESS,
|
type: ACCOUNT_PIN_SUCCESS,
|
||||||
relationship,
|
relationship,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function pinAccountFail(error) {
|
export function pinAccountFail(error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_PIN_FAIL,
|
type: ACCOUNT_PIN_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unpinAccountRequest(id) {
|
export function unpinAccountRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNPIN_REQUEST,
|
type: ACCOUNT_UNPIN_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unpinAccountSuccess(relationship) {
|
export function unpinAccountSuccess(relationship) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNPIN_SUCCESS,
|
type: ACCOUNT_UNPIN_SUCCESS,
|
||||||
relationship,
|
relationship,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unpinAccountFail(error) {
|
export function unpinAccountFail(error) {
|
||||||
return {
|
return {
|
||||||
type: ACCOUNT_UNPIN_FAIL,
|
type: ACCOUNT_UNPIN_FAIL,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
@ -7,8 +7,7 @@ import { useEmoji } from './emojis';
|
|||||||
import resizeImage from '../utils/resize_image';
|
import resizeImage from '../utils/resize_image';
|
||||||
import { importFetchedAccounts } from './importer';
|
import { importFetchedAccounts } from './importer';
|
||||||
import { updateTimeline } from './timelines';
|
import { updateTimeline } from './timelines';
|
||||||
import { showAlertForError } from './alerts';
|
import { showAlert, showAlertForError } from './alerts';
|
||||||
import { showAlert } from './alerts';
|
|
||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags;
|
let cancelFetchComposeSuggestionsAccounts, cancelFetchComposeSuggestionsTags;
|
||||||
@ -60,7 +59,7 @@ export const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE';
|
|||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
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);
|
const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1);
|
||||||
@ -76,52 +75,52 @@ export function changeCompose(text) {
|
|||||||
type: COMPOSE_CHANGE,
|
type: COMPOSE_CHANGE,
|
||||||
text: text,
|
text: text,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function replyCompose(status, routerHistory) {
|
export function replyCompose(status, routerHistory) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: COMPOSE_REPLY,
|
type : COMPOSE_REPLY,
|
||||||
status: status,
|
status: status,
|
||||||
});
|
});
|
||||||
|
|
||||||
ensureComposeIsVisible(getState, routerHistory);
|
ensureComposeIsVisible(getState, routerHistory);
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function cancelReplyCompose() {
|
export function cancelReplyCompose() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_REPLY_CANCEL,
|
type: COMPOSE_REPLY_CANCEL,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function resetCompose() {
|
export function resetCompose() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_RESET,
|
type: COMPOSE_RESET,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function mentionCompose(account, routerHistory) {
|
export function mentionCompose(account, routerHistory) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: COMPOSE_MENTION,
|
type : COMPOSE_MENTION,
|
||||||
account: account,
|
account: account,
|
||||||
});
|
});
|
||||||
|
|
||||||
ensureComposeIsVisible(getState, routerHistory);
|
ensureComposeIsVisible(getState, routerHistory);
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function directCompose(account, routerHistory) {
|
export function directCompose(account, routerHistory) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: COMPOSE_DIRECT,
|
type : COMPOSE_DIRECT,
|
||||||
account: account,
|
account: account,
|
||||||
});
|
});
|
||||||
|
|
||||||
ensureComposeIsVisible(getState, routerHistory);
|
ensureComposeIsVisible(getState, routerHistory);
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function submitCompose(routerHistory) {
|
export function submitCompose(routerHistory) {
|
||||||
return function (dispatch, getState) {
|
return function (dispatch, getState) {
|
||||||
@ -137,11 +136,11 @@ export function submitCompose(routerHistory) {
|
|||||||
api(getState).post('/api/v1/statuses', {
|
api(getState).post('/api/v1/statuses', {
|
||||||
status,
|
status,
|
||||||
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
||||||
media_ids: media.map(item => item.get('id')),
|
media_ids : media.map(item => item.get('id')),
|
||||||
sensitive: getState().getIn(['compose', 'sensitive']),
|
sensitive : getState().getIn(['compose', 'sensitive']),
|
||||||
spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
spoiler_text : getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
||||||
visibility: getState().getIn(['compose', 'privacy']),
|
visibility : getState().getIn(['compose', 'privacy']),
|
||||||
poll: getState().getIn(['compose', 'poll'], null),
|
poll : getState().getIn(['compose', 'poll'], null),
|
||||||
}, {
|
}, {
|
||||||
headers: {
|
headers: {
|
||||||
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
||||||
@ -179,27 +178,27 @@ export function submitCompose(routerHistory) {
|
|||||||
dispatch(submitComposeFail(error));
|
dispatch(submitComposeFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function submitComposeRequest() {
|
export function submitComposeRequest() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_SUBMIT_REQUEST,
|
type: COMPOSE_SUBMIT_REQUEST,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function submitComposeSuccess(status) {
|
export function submitComposeSuccess(status) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_SUBMIT_SUCCESS,
|
type : COMPOSE_SUBMIT_SUCCESS,
|
||||||
status: status,
|
status: status,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function submitComposeFail(error) {
|
export function submitComposeFail(error) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_SUBMIT_FAIL,
|
type : COMPOSE_SUBMIT_FAIL,
|
||||||
error: error,
|
error: error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function uploadCompose(files) {
|
export function uploadCompose(files) {
|
||||||
return function (dispatch, getState) {
|
return function (dispatch, getState) {
|
||||||
@ -231,15 +230,16 @@ export function uploadCompose(files) {
|
|||||||
total += file.size - f.size;
|
total += file.size - f.size;
|
||||||
|
|
||||||
return api(getState).post('/api/v1/media', data, {
|
return api(getState).post('/api/v1/media', data, {
|
||||||
onUploadProgress: function({ loaded }){
|
onUploadProgress: function ({ loaded }) {
|
||||||
progress[i] = loaded;
|
progress[i] = loaded;
|
||||||
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
||||||
},
|
},
|
||||||
}).then(({ data }) => dispatch(uploadComposeSuccess(data, f)));
|
}).then(({ data }) => dispatch(uploadComposeSuccess(data, f)));
|
||||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export function changeUploadCompose(id, params) {
|
export function changeUploadCompose(id, params) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -251,68 +251,69 @@ export function changeUploadCompose(id, params) {
|
|||||||
dispatch(changeUploadComposeFail(id, error));
|
dispatch(changeUploadComposeFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeUploadComposeRequest() {
|
export function changeUploadComposeRequest() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_CHANGE_REQUEST,
|
type : COMPOSE_UPLOAD_CHANGE_REQUEST,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeUploadComposeSuccess(media) {
|
export function changeUploadComposeSuccess(media) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
type : COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||||
media: media,
|
media : media,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeUploadComposeFail(error) {
|
export function changeUploadComposeFail(error) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_CHANGE_FAIL,
|
type : COMPOSE_UPLOAD_CHANGE_FAIL,
|
||||||
error: error,
|
error : error,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function uploadComposeRequest() {
|
export function uploadComposeRequest() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_REQUEST,
|
type : COMPOSE_UPLOAD_REQUEST,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function uploadComposeProgress(loaded, total) {
|
export function uploadComposeProgress(loaded, total) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_PROGRESS,
|
type : COMPOSE_UPLOAD_PROGRESS,
|
||||||
loaded: loaded,
|
loaded: loaded,
|
||||||
total: total,
|
total : total,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function uploadComposeSuccess(media, file) {
|
export function uploadComposeSuccess(media, file) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_SUCCESS,
|
type : COMPOSE_UPLOAD_SUCCESS,
|
||||||
media: media,
|
media : media,
|
||||||
file: file,
|
file : file,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function uploadComposeFail(error) {
|
export function uploadComposeFail(error) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_FAIL,
|
type : COMPOSE_UPLOAD_FAIL,
|
||||||
error: error,
|
error : error,
|
||||||
skipLoading: true,
|
skipLoading: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function undoUploadCompose(media_id) {
|
export function undoUploadCompose(media_id) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UPLOAD_UNDO,
|
type : COMPOSE_UPLOAD_UNDO,
|
||||||
media_id: media_id,
|
media_id: media_id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function clearComposeSuggestions() {
|
export function clearComposeSuggestions() {
|
||||||
if (cancelFetchComposeSuggestionsAccounts) {
|
if (cancelFetchComposeSuggestionsAccounts) {
|
||||||
@ -321,7 +322,7 @@ export function clearComposeSuggestions() {
|
|||||||
return {
|
return {
|
||||||
type: COMPOSE_SUGGESTIONS_CLEAR,
|
type: COMPOSE_SUGGESTIONS_CLEAR,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
||||||
if (cancelFetchComposeSuggestionsAccounts) {
|
if (cancelFetchComposeSuggestionsAccounts) {
|
||||||
@ -334,9 +335,9 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) =>
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
q: token.slice(1),
|
q : token.slice(1),
|
||||||
resolve: false,
|
resolve: false,
|
||||||
limit: 4,
|
limit : 4,
|
||||||
},
|
},
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
dispatch(importFetchedAccounts(response.data));
|
dispatch(importFetchedAccounts(response.data));
|
||||||
@ -366,10 +367,10 @@ const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
type: 'hashtags',
|
type : 'hashtags',
|
||||||
q: token.slice(1),
|
q : token.slice(1),
|
||||||
resolve: false,
|
resolve : false,
|
||||||
limit: 4,
|
limit : 4,
|
||||||
exclude_unreviewed: true,
|
exclude_unreviewed: true,
|
||||||
},
|
},
|
||||||
}).then(({ data }) => {
|
}).then(({ data }) => {
|
||||||
@ -395,7 +396,7 @@ export function fetchComposeSuggestions(token) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function readyComposeSuggestionsEmojis(token, emojis) {
|
export function readyComposeSuggestionsEmojis(token, emojis) {
|
||||||
return {
|
return {
|
||||||
@ -403,7 +404,7 @@ export function readyComposeSuggestionsEmojis(token, emojis) {
|
|||||||
token,
|
token,
|
||||||
emojis,
|
emojis,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function readyComposeSuggestionsAccounts(token, accounts) {
|
export function readyComposeSuggestionsAccounts(token, accounts) {
|
||||||
return {
|
return {
|
||||||
@ -411,7 +412,7 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
|
|||||||
token,
|
token,
|
||||||
accounts,
|
accounts,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export const readyComposeSuggestionsTags = (token, tags) => ({
|
export const readyComposeSuggestionsTags = (token, tags) => ({
|
||||||
type: COMPOSE_SUGGESTIONS_READY,
|
type: COMPOSE_SUGGESTIONS_READY,
|
||||||
@ -437,14 +438,14 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: COMPOSE_SUGGESTION_SELECT,
|
type : COMPOSE_SUGGESTION_SELECT,
|
||||||
position: startPosition,
|
position: startPosition,
|
||||||
token,
|
token,
|
||||||
completion,
|
completion,
|
||||||
path,
|
path,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function updateSuggestionTags(token) {
|
export function updateSuggestionTags(token) {
|
||||||
return {
|
return {
|
||||||
@ -492,39 +493,39 @@ export function mountCompose() {
|
|||||||
return {
|
return {
|
||||||
type: COMPOSE_MOUNT,
|
type: COMPOSE_MOUNT,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmountCompose() {
|
export function unmountCompose() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_UNMOUNT,
|
type: COMPOSE_UNMOUNT,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeComposeSensitivity() {
|
export function changeComposeSensitivity() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_SENSITIVITY_CHANGE,
|
type: COMPOSE_SENSITIVITY_CHANGE,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeComposeSpoilerness() {
|
export function changeComposeSpoilerness() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_SPOILERNESS_CHANGE,
|
type: COMPOSE_SPOILERNESS_CHANGE,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeComposeSpoilerText(text) {
|
export function changeComposeSpoilerText(text) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_SPOILER_TEXT_CHANGE,
|
type: COMPOSE_SPOILER_TEXT_CHANGE,
|
||||||
text,
|
text,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeComposeVisibility(value) {
|
export function changeComposeVisibility(value) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_VISIBILITY_CHANGE,
|
type: COMPOSE_VISIBILITY_CHANGE,
|
||||||
value,
|
value,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function insertEmojiCompose(position, emoji, needsSpace) {
|
export function insertEmojiCompose(position, emoji, needsSpace) {
|
||||||
return {
|
return {
|
||||||
@ -533,33 +534,33 @@ export function insertEmojiCompose(position, emoji, needsSpace) {
|
|||||||
emoji,
|
emoji,
|
||||||
needsSpace,
|
needsSpace,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changeComposing(value) {
|
export function changeComposing(value) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_COMPOSING_CHANGE,
|
type: COMPOSE_COMPOSING_CHANGE,
|
||||||
value,
|
value,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function addPoll() {
|
export function addPoll() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_POLL_ADD,
|
type: COMPOSE_POLL_ADD,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function removePoll() {
|
export function removePoll() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_POLL_REMOVE,
|
type: COMPOSE_POLL_REMOVE,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function addPollOption(title) {
|
export function addPollOption(title) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_POLL_OPTION_ADD,
|
type: COMPOSE_POLL_OPTION_ADD,
|
||||||
title,
|
title,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changePollOption(index, title) {
|
export function changePollOption(index, title) {
|
||||||
return {
|
return {
|
||||||
@ -567,14 +568,14 @@ export function changePollOption(index, title) {
|
|||||||
index,
|
index,
|
||||||
title,
|
title,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function removePollOption(index) {
|
export function removePollOption(index) {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_POLL_OPTION_REMOVE,
|
type: COMPOSE_POLL_OPTION_REMOVE,
|
||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function changePollSettings(expiresIn, isMultiple) {
|
export function changePollSettings(expiresIn, isMultiple) {
|
||||||
return {
|
return {
|
||||||
@ -582,4 +583,4 @@ export function changePollSettings(expiresIn, isMultiple) {
|
|||||||
expiresIn,
|
expiresIn,
|
||||||
isMultiple,
|
isMultiple,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
import {
|
import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer';
|
||||||
importFetchedAccounts,
|
|
||||||
importFetchedStatuses,
|
|
||||||
importFetchedStatus,
|
|
||||||
} from './importer';
|
|
||||||
|
|
||||||
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
|
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
|
||||||
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
|
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
|
||||||
@ -30,7 +26,7 @@ export const unmountConversations = () => ({
|
|||||||
export const markConversationRead = conversationId => (dispatch, getState) => {
|
export const markConversationRead = conversationId => (dispatch, getState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONVERSATIONS_READ,
|
type: CONVERSATIONS_READ,
|
||||||
id: conversationId,
|
id : conversationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
api(getState).post(`/api/v1/conversations/${conversationId}/read`);
|
api(getState).post(`/api/v1/conversations/${conversationId}/read`);
|
||||||
|
@ -3,7 +3,7 @@ import openDB from '../storage/db';
|
|||||||
import { evictStatus } from '../storage/modifier';
|
import { evictStatus } from '../storage/modifier';
|
||||||
|
|
||||||
import { deleteFromTimelines } from './timelines';
|
import { deleteFromTimelines } from './timelines';
|
||||||
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer';
|
import { importAccount, importFetchedStatus, importFetchedStatuses, importStatus } from './importer';
|
||||||
import { ensureComposeIsVisible } from './compose';
|
import { ensureComposeIsVisible } from './compose';
|
||||||
|
|
||||||
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
|
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
|
||||||
@ -37,7 +37,7 @@ export function fetchStatusRequest(id, skipLoading) {
|
|||||||
id,
|
id,
|
||||||
skipLoading,
|
skipLoading,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
function getFromDB(dispatch, getState, accountIndex, index, id) {
|
function getFromDB(dispatch, getState, accountIndex, index, id) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -113,24 +113,24 @@ export function fetchStatus(id) {
|
|||||||
dispatch(fetchStatusFail(id, error, skipLoading));
|
dispatch(fetchStatusFail(id, error, skipLoading));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchStatusSuccess(skipLoading) {
|
export function fetchStatusSuccess(skipLoading) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_FETCH_SUCCESS,
|
type: STATUS_FETCH_SUCCESS,
|
||||||
skipLoading,
|
skipLoading,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchStatusFail(id, error, skipLoading) {
|
export function fetchStatusFail(id, error, skipLoading) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_FETCH_FAIL,
|
type : STATUS_FETCH_FAIL,
|
||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
skipLoading,
|
skipLoading,
|
||||||
skipAlert: true,
|
skipAlert: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function redraft(status, raw_text) {
|
export function redraft(status, raw_text) {
|
||||||
return {
|
return {
|
||||||
@ -138,7 +138,7 @@ export function redraft(status, raw_text) {
|
|||||||
status,
|
status,
|
||||||
raw_text,
|
raw_text,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function deleteStatus(id, routerHistory, withRedraft = false) {
|
export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -163,29 +163,29 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
|
|||||||
dispatch(deleteStatusFail(id, error));
|
dispatch(deleteStatusFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function deleteStatusRequest(id) {
|
export function deleteStatusRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_DELETE_REQUEST,
|
type: STATUS_DELETE_REQUEST,
|
||||||
id: id,
|
id : id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function deleteStatusSuccess(id) {
|
export function deleteStatusSuccess(id) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_DELETE_SUCCESS,
|
type: STATUS_DELETE_SUCCESS,
|
||||||
id: id,
|
id : id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function deleteStatusFail(id, error) {
|
export function deleteStatusFail(id, error) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_DELETE_FAIL,
|
type : STATUS_DELETE_FAIL,
|
||||||
id: id,
|
id : id,
|
||||||
error: error,
|
error: error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchContext(id) {
|
export function fetchContext(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -203,33 +203,33 @@ export function fetchContext(id) {
|
|||||||
dispatch(fetchContextFail(id, error));
|
dispatch(fetchContextFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchContextRequest(id) {
|
export function fetchContextRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: CONTEXT_FETCH_REQUEST,
|
type: CONTEXT_FETCH_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchContextSuccess(id, ancestors, descendants) {
|
export function fetchContextSuccess(id, ancestors, descendants) {
|
||||||
return {
|
return {
|
||||||
type: CONTEXT_FETCH_SUCCESS,
|
type : CONTEXT_FETCH_SUCCESS,
|
||||||
id,
|
id,
|
||||||
ancestors,
|
ancestors,
|
||||||
descendants,
|
descendants,
|
||||||
statuses: ancestors.concat(descendants),
|
statuses: ancestors.concat(descendants),
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function fetchContextFail(id, error) {
|
export function fetchContextFail(id, error) {
|
||||||
return {
|
return {
|
||||||
type: CONTEXT_FETCH_FAIL,
|
type : CONTEXT_FETCH_FAIL,
|
||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
skipAlert: true,
|
skipAlert: true,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function muteStatus(id) {
|
export function muteStatus(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -241,21 +241,21 @@ export function muteStatus(id) {
|
|||||||
dispatch(muteStatusFail(id, error));
|
dispatch(muteStatusFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function muteStatusRequest(id) {
|
export function muteStatusRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_MUTE_REQUEST,
|
type: STATUS_MUTE_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function muteStatusSuccess(id) {
|
export function muteStatusSuccess(id) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_MUTE_SUCCESS,
|
type: STATUS_MUTE_SUCCESS,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function muteStatusFail(id, error) {
|
export function muteStatusFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -263,7 +263,7 @@ export function muteStatusFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteStatus(id) {
|
export function unmuteStatus(id) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
@ -275,21 +275,21 @@ export function unmuteStatus(id) {
|
|||||||
dispatch(unmuteStatusFail(id, error));
|
dispatch(unmuteStatusFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteStatusRequest(id) {
|
export function unmuteStatusRequest(id) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_UNMUTE_REQUEST,
|
type: STATUS_UNMUTE_REQUEST,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteStatusSuccess(id) {
|
export function unmuteStatusSuccess(id) {
|
||||||
return {
|
return {
|
||||||
type: STATUS_UNMUTE_SUCCESS,
|
type: STATUS_UNMUTE_SUCCESS,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function unmuteStatusFail(id, error) {
|
export function unmuteStatusFail(id, error) {
|
||||||
return {
|
return {
|
||||||
@ -297,7 +297,7 @@ export function unmuteStatusFail(id, error) {
|
|||||||
id,
|
id,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function hideStatus(ids) {
|
export function hideStatus(ids) {
|
||||||
if (!Array.isArray(ids)) {
|
if (!Array.isArray(ids)) {
|
||||||
@ -308,7 +308,7 @@ export function hideStatus(ids) {
|
|||||||
type: STATUS_HIDE,
|
type: STATUS_HIDE,
|
||||||
ids,
|
ids,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function revealStatus(ids) {
|
export function revealStatus(ids) {
|
||||||
if (!Array.isArray(ids)) {
|
if (!Array.isArray(ids)) {
|
||||||
@ -319,4 +319,4 @@ export function revealStatus(ids) {
|
|||||||
type: STATUS_REVEAL,
|
type: STATUS_REVEAL,
|
||||||
ids,
|
ids,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
@ -37,27 +37,27 @@ class Account extends ImmutablePureComponent {
|
|||||||
|
|
||||||
handleFollow = () => {
|
handleFollow = () => {
|
||||||
this.props.onFollow(this.props.account);
|
this.props.onFollow(this.props.account);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBlock = () => {
|
handleBlock = () => {
|
||||||
this.props.onBlock(this.props.account);
|
this.props.onBlock(this.props.account);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMute = () => {
|
handleMute = () => {
|
||||||
this.props.onMute(this.props.account);
|
this.props.onMute(this.props.account);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMuteNotifications = () => {
|
handleMuteNotifications = () => {
|
||||||
this.props.onMuteNotifications(this.props.account, true);
|
this.props.onMuteNotifications(this.props.account, true);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleUnmuteNotifications = () => {
|
handleUnmuteNotifications = () => {
|
||||||
this.props.onMuteNotifications(this.props.account, false);
|
this.props.onMuteNotifications(this.props.account, false);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleAction = () => {
|
handleAction = () => {
|
||||||
this.props.onActionClick(this.props.account);
|
this.props.onActionClick(this.props.account);
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, intl, hidden, onActionClick, actionIcon, actionTitle } = this.props;
|
const { account, intl, hidden, onActionClick, actionIcon, actionTitle } = this.props;
|
||||||
@ -111,7 +111,10 @@ class Account extends ImmutablePureComponent {
|
|||||||
<div className='account'>
|
<div className='account'>
|
||||||
<div className='account__wrapper'>
|
<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')}`}>
|
<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} />
|
<DisplayName account={account} />
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
|
@ -7,16 +7,16 @@ export default class Avatar extends React.PureComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
size: PropTypes.number.isRequired,
|
size : PropTypes.number.isRequired,
|
||||||
style: PropTypes.object,
|
style : PropTypes.object,
|
||||||
inline: PropTypes.bool,
|
inline : PropTypes.bool,
|
||||||
animate: PropTypes.bool,
|
animate: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
animate: autoPlayGif,
|
animate: autoPlayGif,
|
||||||
size: 20,
|
size : 20,
|
||||||
inline: false,
|
inline : false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -26,14 +26,14 @@ export default class Avatar extends React.PureComponent {
|
|||||||
handleMouseEnter = () => {
|
handleMouseEnter = () => {
|
||||||
if (this.props.animate) return;
|
if (this.props.animate) return;
|
||||||
this.setState({ hovering: true });
|
this.setState({ hovering: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMouseLeave = () => {
|
handleMouseLeave = () => {
|
||||||
if (this.props.animate) return;
|
if (this.props.animate) return;
|
||||||
this.setState({ hovering: false });
|
this.setState({ hovering: false });
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { account, size, animate, inline } = this.props;
|
const { account, size, animate, inline } = this.props;
|
||||||
const { hovering } = this.state;
|
const { hovering } = this.state;
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ export default class Avatar extends React.PureComponent {
|
|||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
...this.props.style,
|
...this.props.style,
|
||||||
width: `${size}px`,
|
width : `${size}px`,
|
||||||
height: `${size}px`,
|
height : `${size}px`,
|
||||||
backgroundSize: `${size}px ${size}px`,
|
backgroundSize: `${size}px ${size}px`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FormattedMessage } from 'react-intl';
|
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 {
|
export default class ErrorBoundary extends React.PureComponent {
|
||||||
|
|
||||||
@ -10,17 +10,17 @@ export default class ErrorBoundary extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
hasError: false,
|
hasError : false,
|
||||||
stackTrace: undefined,
|
stackTrace : undefined,
|
||||||
componentStack: undefined,
|
componentStack: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidCatch (error, info) {
|
componentDidCatch(error, info) {
|
||||||
this.setState({
|
this.setState({
|
||||||
hasError: true,
|
hasError : true,
|
||||||
stackTrace: error.stack,
|
stackTrace : error.stack,
|
||||||
componentStack: info && info.componentStack,
|
componentStack: info && info.componentStack,
|
||||||
copied: false,
|
copied : false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
|||||||
|
|
||||||
this.setState({ copied: true });
|
this.setState({ copied: true });
|
||||||
setTimeout(() => this.setState({ copied: false }), 700);
|
setTimeout(() => this.setState({ copied: false }), 700);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { hasError, copied } = this.state;
|
const { hasError, copied } = this.state;
|
||||||
@ -54,13 +54,32 @@ export default class ErrorBoundary extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='error-boundary'>
|
<div className='error-boundary hero text-center padded content'>
|
||||||
<div>
|
<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 className='error-boundary__error title content-heading'><FormattedMessage
|
||||||
<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>
|
id='error.unexpected_crash.explanation'
|
||||||
<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>
|
defaultMessage='Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.'
|
||||||
</div>
|
/></p >
|
||||||
</div>
|
<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 {
|
export default class IconButton extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
className: PropTypes.string,
|
className : PropTypes.string,
|
||||||
title: PropTypes.string.isRequired,
|
title : PropTypes.string.isRequired,
|
||||||
icon: PropTypes.string.isRequired,
|
icon : PropTypes.string.isRequired,
|
||||||
onClick: PropTypes.func,
|
onClick : PropTypes.func,
|
||||||
onMouseDown: PropTypes.func,
|
onMouseDown: PropTypes.func,
|
||||||
onKeyDown: PropTypes.func,
|
onKeyDown : PropTypes.func,
|
||||||
onKeyPress: PropTypes.func,
|
onKeyPress : PropTypes.func,
|
||||||
size: PropTypes.number,
|
size : PropTypes.number,
|
||||||
active: PropTypes.bool,
|
active : PropTypes.bool,
|
||||||
pressed: PropTypes.bool,
|
pressed : PropTypes.bool,
|
||||||
expanded: PropTypes.bool,
|
expanded : PropTypes.bool,
|
||||||
style: PropTypes.object,
|
style : PropTypes.object,
|
||||||
activeStyle: PropTypes.object,
|
activeStyle: PropTypes.object,
|
||||||
disabled: PropTypes.bool,
|
disabled : PropTypes.bool,
|
||||||
inverted: PropTypes.bool,
|
inverted : PropTypes.bool,
|
||||||
animate: PropTypes.bool,
|
animate : PropTypes.bool,
|
||||||
overlay: PropTypes.bool,
|
overlay : PropTypes.bool,
|
||||||
tabIndex: PropTypes.string,
|
tabIndex : PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
size: 18,
|
size : 18,
|
||||||
active: false,
|
active : false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
animate: false,
|
animate : false,
|
||||||
overlay: false,
|
overlay : false,
|
||||||
tabIndex: '0',
|
tabIndex: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
activate: false,
|
activate : false,
|
||||||
deactivate: false,
|
deactivate: false,
|
||||||
}
|
};
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (!nextProps.animate) return;
|
if (!nextProps.animate) return;
|
||||||
|
|
||||||
if (this.props.active && !nextProps.active) {
|
if (this.props.active && !nextProps.active) {
|
||||||
@ -56,32 +56,32 @@ export default class IconButton extends React.PureComponent {
|
|||||||
if (!this.props.disabled) {
|
if (!this.props.disabled) {
|
||||||
this.props.onClick(e);
|
this.props.onClick(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
handleKeyPress = (e) => {
|
||||||
if (this.props.onKeyPress && !this.props.disabled) {
|
if (this.props.onKeyPress && !this.props.disabled) {
|
||||||
this.props.onKeyPress(e);
|
this.props.onKeyPress(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMouseDown = (e) => {
|
handleMouseDown = (e) => {
|
||||||
if (!this.props.disabled && this.props.onMouseDown) {
|
if (!this.props.disabled && this.props.onMouseDown) {
|
||||||
this.props.onMouseDown(e);
|
this.props.onMouseDown(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleKeyDown = (e) => {
|
handleKeyDown = (e) => {
|
||||||
if (!this.props.disabled && this.props.onKeyDown) {
|
if (!this.props.disabled && this.props.onKeyDown) {
|
||||||
this.props.onKeyDown(e);
|
this.props.onKeyDown(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const style = {
|
const style = {
|
||||||
fontSize: `${this.props.size}px`,
|
// fontSize: `${this.props.size}px`,
|
||||||
width: `${this.props.size * 1.28571429}px`,
|
// width: `${this.props.size * 1.28571429}px`,
|
||||||
height: `${this.props.size * 1.28571429}px`,
|
// height: `${this.props.size * 1.28571429}px`,
|
||||||
lineHeight: `${this.props.size}px`,
|
// lineHeight: `${this.props.size}px`,
|
||||||
...this.props.style,
|
...this.props.style,
|
||||||
...(this.props.active ? this.props.activeStyle : {}),
|
...(this.props.active ? this.props.activeStyle : {}),
|
||||||
};
|
};
|
||||||
@ -128,8 +128,12 @@ export default class IconButton extends React.PureComponent {
|
|||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<Icon id={icon} fixedWidth aria-hidden='true' />
|
<Icon
|
||||||
</button>
|
id={icon}
|
||||||
|
fixedWidth
|
||||||
|
aria-hidden='true'
|
||||||
|
/>
|
||||||
|
</button >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import { Audio, MediaGallery, Video } from '../features/ui/util/async-components
|
|||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icon from 'mastodon/components/icon';
|
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
|
// We use the component (and not the container) since we do not want
|
||||||
// to use the progress bar to show download progress
|
// to use the progress bar to show download progress
|
||||||
import Bundle from '../features/ui/components/bundle';
|
import Bundle from '../features/ui/components/bundle';
|
||||||
@ -82,11 +82,15 @@ class Status extends ImmutablePureComponent {
|
|||||||
onMoveUp : PropTypes.func,
|
onMoveUp : PropTypes.func,
|
||||||
onMoveDown : PropTypes.func,
|
onMoveDown : PropTypes.func,
|
||||||
showThread : PropTypes.bool,
|
showThread : PropTypes.bool,
|
||||||
|
threadsCompile : PropTypes.bool,
|
||||||
getScrollPosition : PropTypes.func,
|
getScrollPosition : PropTypes.func,
|
||||||
updateScrollBottom: PropTypes.func,
|
updateScrollBottom: PropTypes.func,
|
||||||
cacheMediaWidth : PropTypes.func,
|
cacheMediaWidth : PropTypes.func,
|
||||||
cachedMediaWidth : PropTypes.number,
|
cachedMediaWidth : PropTypes.number,
|
||||||
};
|
};
|
||||||
|
static defaultProps = {
|
||||||
|
threadsCompile: true,
|
||||||
|
};
|
||||||
|
|
||||||
// Avoid checking props that are functions (and whose equality will always
|
// Avoid checking props that are functions (and whose equality will always
|
||||||
// evaluate to false. See react-immutable-pure-component for usage.
|
// evaluate to false. See react-immutable-pure-component for usage.
|
||||||
@ -199,24 +203,24 @@ class Status extends ImmutablePureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderLoadingMediaGallery() {
|
renderLoadingMediaGallery() {
|
||||||
return <div
|
return (<div
|
||||||
className='media-gallery'
|
className='media-gallery'
|
||||||
style={{ height: '110px' }}
|
style={{ height: '110px' }}
|
||||||
/>;
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingVideoPlayer() {
|
renderLoadingVideoPlayer() {
|
||||||
return <div
|
return (<div
|
||||||
className='video-player'
|
className='video-player'
|
||||||
style={{ height: '110px' }}
|
style={{ height: '110px' }}
|
||||||
/>;
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingAudioPlayer() {
|
renderLoadingAudioPlayer() {
|
||||||
return <div
|
return (<div
|
||||||
className='audio-player'
|
className='audio-player'
|
||||||
style={{ height: '110px' }}
|
style={{ height: '110px' }}
|
||||||
/>;
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOpenVideo = (media, startTime) => {
|
handleOpenVideo = (media, startTime) => {
|
||||||
@ -497,20 +501,20 @@ class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (otherAccounts && otherAccounts.size > 0) {
|
if (otherAccounts && otherAccounts.size > 0) {
|
||||||
statusAvatar = <AvatarComposite
|
statusAvatar = (<AvatarComposite
|
||||||
accounts={otherAccounts}
|
accounts={otherAccounts}
|
||||||
size={48}
|
size={55}
|
||||||
/>;
|
/>);
|
||||||
} else if (account === undefined || account === null) {
|
} else if (account === undefined || account === null) {
|
||||||
statusAvatar = <Avatar
|
statusAvatar = (<Avatar
|
||||||
account={status.get('account')}
|
account={status.get('account')}
|
||||||
size={48}
|
size={55}
|
||||||
/>;
|
/>);
|
||||||
} else {
|
} else {
|
||||||
statusAvatar = <AvatarOverlay
|
statusAvatar = (<AvatarOverlay
|
||||||
account={status.get('account')}
|
account={status.get('account')}
|
||||||
friend={account}
|
friend={account}
|
||||||
/>;
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -542,6 +546,12 @@ class Status extends ImmutablePureComponent {
|
|||||||
role='presentation'
|
role='presentation'
|
||||||
/>
|
/>
|
||||||
<div className='status__info'>
|
<div className='status__info'>
|
||||||
|
|
||||||
|
{isStaff && (<div className='administrate-stuff pull-left'>
|
||||||
|
|
||||||
|
<i className='fa fa-gears' />
|
||||||
|
</div >
|
||||||
|
)}
|
||||||
<a
|
<a
|
||||||
href={status.get('url')}
|
href={status.get('url')}
|
||||||
className='status__relative-time'
|
className='status__relative-time'
|
||||||
@ -595,10 +605,19 @@ class Status extends ImmutablePureComponent {
|
|||||||
/>
|
/>
|
||||||
</button >
|
</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
|
<StatusActionBar
|
||||||
status={status}
|
status={status}
|
||||||
account={account} {...other} />
|
account={account} {...other}
|
||||||
|
/>
|
||||||
</div >
|
</div >
|
||||||
</div >
|
</div >
|
||||||
</HotKeys >
|
</HotKeys >
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import Button from 'mastodon/components/button';
|
import Button from 'mastodon/components/button';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { autoPlayGif, me, isStaff } from 'mastodon/initial_state';
|
import { autoPlayGif, isStaff, me } from 'mastodon/initial_state';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import Avatar from 'mastodon/components/avatar';
|
import Avatar from 'mastodon/components/avatar';
|
||||||
@ -70,7 +70,7 @@ class Header extends ImmutablePureComponent {
|
|||||||
|
|
||||||
openEditProfile = () => {
|
openEditProfile = () => {
|
||||||
window.open('/settings/profile', '_blank');
|
window.open('/settings/profile', '_blank');
|
||||||
}
|
};
|
||||||
|
|
||||||
isStatusesPageActive = (match, location) => {
|
isStatusesPageActive = (match, location) => {
|
||||||
if (!match) {
|
if (!match) {
|
||||||
@ -78,7 +78,7 @@ class Header extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !location.pathname.match(/\/(followers|following)\/?$/);
|
return !location.pathname.match(/\/(followers|following)\/?$/);
|
||||||
}
|
};
|
||||||
|
|
||||||
_updateEmojis () {
|
_updateEmojis () {
|
||||||
const node = this.node;
|
const node = this.node;
|
||||||
@ -111,15 +111,15 @@ class Header extends ImmutablePureComponent {
|
|||||||
|
|
||||||
handleEmojiMouseEnter = ({ target }) => {
|
handleEmojiMouseEnter = ({ target }) => {
|
||||||
target.src = target.getAttribute('data-original');
|
target.src = target.getAttribute('data-original');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleEmojiMouseLeave = ({ target }) => {
|
handleEmojiMouseLeave = ({ target }) => {
|
||||||
target.src = target.getAttribute('data-static');
|
target.src = target.getAttribute('data-static');
|
||||||
}
|
};
|
||||||
|
|
||||||
setRef = (c) => {
|
setRef = (c) => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, intl, domain, identity_proofs } = this.props;
|
const { account, intl, domain, identity_proofs } = this.props;
|
||||||
@ -263,7 +263,10 @@ class Header extends ImmutablePureComponent {
|
|||||||
<div className='account__header__bar'>
|
<div className='account__header__bar'>
|
||||||
<div className='account__header__tabs'>
|
<div className='account__header__tabs'>
|
||||||
<a className='avatar' href={account.get('url')} rel='noopener noreferrer' target='_blank'>
|
<a className='avatar' href={account.get('url')} rel='noopener noreferrer' target='_blank'>
|
||||||
<Avatar account={account} size={90} />
|
<Avatar
|
||||||
|
account={account}
|
||||||
|
size={120}
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div className='spacer' />
|
<div className='spacer' />
|
||||||
|
@ -21,12 +21,13 @@ const mapStateToProps = (state, { params: { accountId }, withReplies = false })
|
|||||||
const path = withReplies ? `${accountId}:with_replies` : accountId;
|
const path = withReplies ? `${accountId}:with_replies` : accountId;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount : !!state.getIn(['accounts', accountId]),
|
||||||
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
account : state.getIn(['accounts', accountId]),
|
||||||
|
statusIds : state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
||||||
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
||||||
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
isLoading : state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
||||||
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
hasMore : state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
||||||
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
blockedBy : state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,20 +35,20 @@ export default @connect(mapStateToProps)
|
|||||||
class AccountTimeline extends ImmutablePureComponent {
|
class AccountTimeline extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params : PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch : PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
shouldUpdateScroll: PropTypes.func,
|
||||||
statusIds: ImmutablePropTypes.list,
|
statusIds : ImmutablePropTypes.list,
|
||||||
featuredStatusIds: ImmutablePropTypes.list,
|
featuredStatusIds : ImmutablePropTypes.list,
|
||||||
isLoading: PropTypes.bool,
|
isLoading : PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore : PropTypes.bool,
|
||||||
withReplies: PropTypes.bool,
|
withReplies : PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy : PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount : PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn : PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
const { params: { accountId }, withReplies } = this.props;
|
const { params: { accountId }, withReplies } = this.props;
|
||||||
|
|
||||||
this.props.dispatch(fetchAccount(accountId));
|
this.props.dispatch(fetchAccount(accountId));
|
||||||
@ -60,7 +61,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||||||
this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
|
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) {
|
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(fetchAccount(nextProps.params.accountId));
|
||||||
this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
|
this.props.dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
|
||||||
@ -74,33 +75,42 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = maxId => {
|
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;
|
const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount, multiColumn } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
<MissingIndicator />
|
<MissingIndicator />
|
||||||
</Column>
|
</Column >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!statusIds && isLoading) {
|
if (!statusIds && isLoading) {
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<LoadingIndicator />
|
<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 (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
@ -116,7 +126,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
spoilerText : PropTypes.string,
|
spoilerText : PropTypes.string,
|
||||||
focusDate : PropTypes.instanceOf(Date),
|
focusDate : PropTypes.instanceOf(Date),
|
||||||
caretPosition : PropTypes.number,
|
caretPosition : PropTypes.number,
|
||||||
|
maxTootCharsLimit : PropTypes.number,
|
||||||
preselectDate : PropTypes.instanceOf(Date),
|
preselectDate : PropTypes.instanceOf(Date),
|
||||||
isSubmitting : PropTypes.bool,
|
isSubmitting : PropTypes.bool,
|
||||||
isChangingUpload : PropTypes.bool,
|
isChangingUpload : PropTypes.bool,
|
||||||
@ -64,7 +65,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
showSearch: false,
|
showSearch : false,
|
||||||
|
maxTootCharsLimit: 7777,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChange = (e) => {
|
handleChange = (e) => {
|
||||||
@ -88,7 +90,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
||||||
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('');
|
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||||
|
|
||||||
if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,23 +183,26 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
const { intl, onPaste, showSearch, anyMedia } = this.props;
|
const { intl, onPaste, showSearch, anyMedia } = this.props;
|
||||||
const disabled = this.props.isSubmitting;
|
const disabled = this.props.isSubmitting;
|
||||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||||
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > 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 = '';
|
let publishText = '';
|
||||||
|
|
||||||
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
|
||||||
publishText =
|
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 {
|
} else {
|
||||||
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
|
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='compose-form'>
|
<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
|
<AutosuggestInput
|
||||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||||
value={this.props.spoilerText}
|
value={this.props.spoilerText}
|
||||||
@ -213,7 +218,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
id='cw-spoiler-input'
|
id='cw-spoiler-input'
|
||||||
className='spoiler-input__input'
|
className='spoiler-input__input'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div >
|
||||||
|
|
||||||
<AutosuggestTextarea
|
<AutosuggestTextarea
|
||||||
ref={this.setAutosuggestTextarea}
|
ref={this.setAutosuggestTextarea}
|
||||||
@ -230,31 +235,36 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
onPaste={onPaste}
|
onPaste={onPaste}
|
||||||
autoFocus={!showSearch && !isMobile(window.innerWidth)}
|
autoFocus={!showSearch && !isMobile(window.innerWidth)}
|
||||||
>
|
>
|
||||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick}/>
|
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||||
<div className='compose-form__modifiers'>
|
<div className='compose-form__modifiers'>
|
||||||
<UploadFormContainer/>
|
<UploadFormContainer />
|
||||||
<PollFormContainer/>
|
<PollFormContainer />
|
||||||
</div>
|
</div >
|
||||||
</AutosuggestTextarea>
|
</AutosuggestTextarea >
|
||||||
|
|
||||||
<div className='compose-form__buttons-wrapper'>
|
<div className='compose-form__buttons-wrapper'>
|
||||||
<div className='compose-form__buttons'>
|
<div className='compose-form__buttons'>
|
||||||
<UploadButtonContainer/>
|
<UploadButtonContainer />
|
||||||
<PollButtonContainer/>
|
<PollButtonContainer />
|
||||||
<PrivacyDropdownContainer/>
|
<PrivacyDropdownContainer />
|
||||||
<SpoilerButtonContainer/>
|
<SpoilerButtonContainer />
|
||||||
</div>
|
</div >
|
||||||
<div className='character-counter__wrapper'><CharacterCounter max={7777} text={text}/></div>
|
<div className='character-counter__wrapper'><CharacterCounter
|
||||||
</div>
|
max={this.props.maxTootCharsLimit}
|
||||||
|
text={text}
|
||||||
|
/></div >
|
||||||
|
</div >
|
||||||
|
|
||||||
<div className='compose-form__publish'>
|
<div className='compose-form__publish'>
|
||||||
<div className='compose-form__publish-button-wrapper'><Button
|
<div className='compose-form__publish-button-wrapper'><Button
|
||||||
text={publishText} onClick={this.handleSubmit}
|
text={publishText}
|
||||||
disabled={disabledButton} block
|
onClick={this.handleSubmit}
|
||||||
/></div>
|
disabled={disabledButton}
|
||||||
</div>
|
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 >
|
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span >
|
||||||
<Avatar
|
<Avatar
|
||||||
account={this.props.account}
|
account={this.props.account}
|
||||||
size={48}
|
size={55}
|
||||||
/>
|
/>
|
||||||
</Permalink >
|
</Permalink >
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import IconButton from 'mastodon/components/icon_button';
|
import IconButton from 'mastodon/components/icon_button';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
||||||
@ -56,19 +56,19 @@ class Option extends React.PureComponent {
|
|||||||
if (e.key === 'Enter' || e.key === ' ') {
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
this.handleToggleMultiple(e);
|
this.handleToggleMultiple(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onSuggestionsClearRequested = () => {
|
onSuggestionsClearRequested = () => {
|
||||||
this.props.onClearSuggestions();
|
this.props.onClearSuggestions();
|
||||||
}
|
};
|
||||||
|
|
||||||
onSuggestionsFetchRequested = (token) => {
|
onSuggestionsFetchRequested = (token) => {
|
||||||
this.props.onFetchSuggestions(token);
|
this.props.onFetchSuggestions(token);
|
||||||
}
|
};
|
||||||
|
|
||||||
onSuggestionSelected = (tokenStart, token, value) => {
|
onSuggestionSelected = (tokenStart, token, value) => {
|
||||||
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
|
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { isPollMultiple, title, index, intl } = this.props;
|
const { isPollMultiple, title, index, intl } = this.props;
|
||||||
@ -82,8 +82,8 @@ class Option extends React.PureComponent {
|
|||||||
onKeyPress={this.handleCheckboxKeypress}
|
onKeyPress={this.handleCheckboxKeypress}
|
||||||
role='button'
|
role='button'
|
||||||
tabIndex='0'
|
tabIndex='0'
|
||||||
title={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
|
title={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
|
||||||
aria-label={intl.formatMessage(isPollMultiple ? messages.switchToMultiple : messages.switchToSingle)}
|
aria-label={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AutosuggestInput
|
<AutosuggestInput
|
||||||
|
@ -2,62 +2,62 @@ import { connect } from 'react-redux';
|
|||||||
import ComposeForm from '../components/compose_form';
|
import ComposeForm from '../components/compose_form';
|
||||||
import {
|
import {
|
||||||
changeCompose,
|
changeCompose,
|
||||||
submitCompose,
|
changeComposeSpoilerText,
|
||||||
clearComposeSuggestions,
|
clearComposeSuggestions,
|
||||||
fetchComposeSuggestions,
|
fetchComposeSuggestions,
|
||||||
selectComposeSuggestion,
|
|
||||||
changeComposeSpoilerText,
|
|
||||||
insertEmojiCompose,
|
insertEmojiCompose,
|
||||||
|
selectComposeSuggestion,
|
||||||
|
submitCompose,
|
||||||
uploadCompose,
|
uploadCompose,
|
||||||
} from '../../../actions/compose';
|
} from '../../../actions/compose';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
text: state.getIn(['compose', 'text']),
|
text : state.getIn(['compose', 'text']),
|
||||||
suggestions: state.getIn(['compose', 'suggestions']),
|
suggestions : state.getIn(['compose', 'suggestions']),
|
||||||
spoiler: state.getIn(['compose', 'spoiler']),
|
spoiler : state.getIn(['compose', 'spoiler']),
|
||||||
spoilerText: state.getIn(['compose', 'spoiler_text']),
|
spoilerText : state.getIn(['compose', 'spoiler_text']),
|
||||||
privacy: state.getIn(['compose', 'privacy']),
|
privacy : state.getIn(['compose', 'privacy']),
|
||||||
focusDate: state.getIn(['compose', 'focusDate']),
|
focusDate : state.getIn(['compose', 'focusDate']),
|
||||||
caretPosition: state.getIn(['compose', 'caretPosition']),
|
caretPosition : state.getIn(['compose', 'caretPosition']),
|
||||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
preselectDate : state.getIn(['compose', 'preselectDate']),
|
||||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
isSubmitting : state.getIn(['compose', 'is_submitting']),
|
||||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
isUploading : state.getIn(['compose', 'is_uploading']),
|
||||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
showSearch : state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
anyMedia : state.getIn(['compose', 'media_attachments']).size > 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
|
||||||
onChange (text) {
|
onChange(text) {
|
||||||
dispatch(changeCompose(text));
|
dispatch(changeCompose(text));
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmit (router) {
|
onSubmit(router) {
|
||||||
dispatch(submitCompose(router));
|
dispatch(submitCompose(router));
|
||||||
},
|
},
|
||||||
|
|
||||||
onClearSuggestions () {
|
onClearSuggestions() {
|
||||||
dispatch(clearComposeSuggestions());
|
dispatch(clearComposeSuggestions());
|
||||||
},
|
},
|
||||||
|
|
||||||
onFetchSuggestions (token) {
|
onFetchSuggestions(token) {
|
||||||
dispatch(fetchComposeSuggestions(token));
|
dispatch(fetchComposeSuggestions(token));
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuggestionSelected (position, token, suggestion, path) {
|
onSuggestionSelected(position, token, suggestion, path) {
|
||||||
dispatch(selectComposeSuggestion(position, token, suggestion, path));
|
dispatch(selectComposeSuggestion(position, token, suggestion, path));
|
||||||
},
|
},
|
||||||
|
|
||||||
onChangeSpoilerText (checked) {
|
onChangeSpoilerText(checked) {
|
||||||
dispatch(changeComposeSpoilerText(checked));
|
dispatch(changeComposeSpoilerText(checked));
|
||||||
},
|
},
|
||||||
|
|
||||||
onPaste (files) {
|
onPaste(files) {
|
||||||
dispatch(uploadCompose(files));
|
dispatch(uploadCompose(files));
|
||||||
},
|
},
|
||||||
|
|
||||||
onPickEmoji (position, data, needsSpace) {
|
onPickEmoji(position, data, needsSpace) {
|
||||||
dispatch(insertEmojiCompose(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 });
|
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')}`}
|
to={`/accounts/${a.get('id')}`}
|
||||||
href={a.get('url')}
|
href={a.get('url')}
|
||||||
key={a.get('id')}
|
key={a.get('id')}
|
||||||
@ -160,7 +160,7 @@ class Conversation extends ImmutablePureComponent {
|
|||||||
className='display-name__html'
|
className='display-name__html'
|
||||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
||||||
/></bdi >
|
/></bdi >
|
||||||
</Permalink >).reduce((prev, cur) => [prev, ', ', cur]);
|
</Permalink >)).reduce((prev, cur) => [prev, ', ', cur]);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
reply : this.handleReply,
|
reply : this.handleReply,
|
||||||
@ -179,7 +179,7 @@ class Conversation extends ImmutablePureComponent {
|
|||||||
<div className='conversation__avatar'>
|
<div className='conversation__avatar'>
|
||||||
<AvatarComposite
|
<AvatarComposite
|
||||||
accounts={accounts}
|
accounts={accounts}
|
||||||
size={48}
|
size={55}
|
||||||
/>
|
/>
|
||||||
</div >
|
</div >
|
||||||
|
|
||||||
@ -221,23 +221,24 @@ class Conversation extends ImmutablePureComponent {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='status__action-bar'>
|
<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'>
|
<div className='status__action-bar-dropdown'>
|
||||||
<DropdownMenuContainer
|
<DropdownMenuContainer
|
||||||
status={lastStatus}
|
status={lastStatus}
|
||||||
items={menu}
|
items={menu}
|
||||||
icon='ellipsis-h'
|
icon='ellipsis-h'
|
||||||
size={18}
|
|
||||||
direction='right'
|
direction='right'
|
||||||
|
style={{ width: '15em' }}
|
||||||
|
size={18}
|
||||||
title={intl.formatMessage(messages.more)}
|
title={intl.formatMessage(messages.more)}
|
||||||
/>
|
/>
|
||||||
</div >
|
</div >
|
||||||
|
<IconButton
|
||||||
|
className='status__action-bar-button conversation_reply'
|
||||||
|
title={intl.formatMessage(messages.reply)}
|
||||||
|
icon='reply'
|
||||||
|
size={40}
|
||||||
|
onClick={this.handleReply}
|
||||||
|
/>
|
||||||
</div >
|
</div >
|
||||||
</div >
|
</div >
|
||||||
</div >
|
</div >
|
||||||
|
@ -9,10 +9,10 @@ import DisplayName from 'mastodon/components/display_name';
|
|||||||
import Permalink from 'mastodon/components/permalink';
|
import Permalink from 'mastodon/components/permalink';
|
||||||
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
|
import RelativeTimestamp from 'mastodon/components/relative_timestamp';
|
||||||
import IconButton from 'mastodon/components/icon_button';
|
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 { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state';
|
||||||
import { shortNumberFormat } from 'mastodon/utils/numbers';
|
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 { openModal } from 'mastodon/actions/modal';
|
||||||
import { initMuteModal } from 'mastodon/actions/mutes';
|
import { initMuteModal } from 'mastodon/actions/mutes';
|
||||||
|
|
||||||
@ -113,27 +113,27 @@ class AccountCard extends ImmutablePureComponent {
|
|||||||
|
|
||||||
handleEmojiMouseEnter = ({ target }) => {
|
handleEmojiMouseEnter = ({ target }) => {
|
||||||
target.src = target.getAttribute('data-original');
|
target.src = target.getAttribute('data-original');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleEmojiMouseLeave = ({ target }) => {
|
handleEmojiMouseLeave = ({ target }) => {
|
||||||
target.src = target.getAttribute('data-static');
|
target.src = target.getAttribute('data-static');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleFollow = () => {
|
handleFollow = () => {
|
||||||
this.props.onFollow(this.props.account);
|
this.props.onFollow(this.props.account);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleBlock = () => {
|
handleBlock = () => {
|
||||||
this.props.onBlock(this.props.account);
|
this.props.onBlock(this.props.account);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMute = () => {
|
handleMute = () => {
|
||||||
this.props.onMute(this.props.account);
|
this.props.onMute(this.props.account);
|
||||||
}
|
};
|
||||||
|
|
||||||
setRef = (c) => {
|
setRef = (c) => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, intl } = this.props;
|
const { account, intl } = this.props;
|
||||||
@ -165,7 +165,10 @@ class AccountCard extends ImmutablePureComponent {
|
|||||||
|
|
||||||
<div className='directory__card__bar'>
|
<div className='directory__card__bar'>
|
||||||
<Permalink className='directory__card__bar__name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
|
<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} />
|
<DisplayName account={account} />
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
|
@ -31,7 +31,10 @@ class AccountAuthorize extends ImmutablePureComponent {
|
|||||||
<div className='account-authorize__wrapper'>
|
<div className='account-authorize__wrapper'>
|
||||||
<div className='account-authorize'>
|
<div className='account-authorize'>
|
||||||
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
|
<Permalink 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} />
|
<DisplayName account={account} />
|
||||||
</Permalink>
|
</Permalink>
|
||||||
|
|
||||||
|
@ -5,11 +5,7 @@ import PropTypes from 'prop-types';
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import LoadingIndicator from '../../components/loading_indicator';
|
import LoadingIndicator from '../../components/loading_indicator';
|
||||||
import {
|
import { expandFollowing, fetchAccount, fetchFollowing } from '../../actions/accounts';
|
||||||
fetchAccount,
|
|
||||||
fetchFollowing,
|
|
||||||
expandFollowing,
|
|
||||||
} from '../../actions/accounts';
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import AccountContainer from '../../containers/account_container';
|
import AccountContainer from '../../containers/account_container';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
@ -19,34 +15,34 @@ import ScrollableList from '../../components/scrollable_list';
|
|||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
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']),
|
accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
|
||||||
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
|
hasMore : !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
|
||||||
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
blockedBy : state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
class Following extends ImmutablePureComponent {
|
class Following extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params : PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch : PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
shouldUpdateScroll: PropTypes.func,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds : ImmutablePropTypes.list,
|
||||||
hasMore: PropTypes.bool,
|
hasMore : PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy : PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount : PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn : PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
if (!this.props.accountIds) {
|
if (!this.props.accountIds) {
|
||||||
this.props.dispatch(fetchAccount(this.props.params.accountId));
|
this.props.dispatch(fetchAccount(this.props.params.accountId));
|
||||||
this.props.dispatch(fetchFollowing(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) {
|
if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
|
||||||
this.props.dispatch(fetchAccount(nextProps.params.accountId));
|
this.props.dispatch(fetchAccount(nextProps.params.accountId));
|
||||||
this.props.dispatch(fetchFollowing(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));
|
this.props.dispatch(expandFollowing(this.props.params.accountId));
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props;
|
const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<MissingIndicator />
|
<MissingIndicator />
|
||||||
</Column>
|
</Column >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!accountIds) {
|
if (!accountIds) {
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<LoadingIndicator />
|
<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 (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnBackButton multiColumn={multiColumn} />
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
@ -87,16 +89,23 @@ class Following extends ImmutablePureComponent {
|
|||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
shouldUpdateScroll={shouldUpdateScroll}
|
shouldUpdateScroll={shouldUpdateScroll}
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
prepend={<HeaderContainer
|
||||||
|
accountId={this.props.params.accountId}
|
||||||
|
hideTabs
|
||||||
|
/>}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
{blockedBy ? [] : accountIds.map(id =>
|
{blockedBy ? [] : accountIds.map(id =>
|
||||||
<AccountContainer key={id} id={id} withNote={false} />
|
(<AccountContainer
|
||||||
|
key={id}
|
||||||
|
id={id}
|
||||||
|
withNote={false}
|
||||||
|
/>),
|
||||||
)}
|
)}
|
||||||
</ScrollableList>
|
</ScrollableList >
|
||||||
</Column>
|
</Column >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
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 { HotKeys } from 'react-hotkeys';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
@ -13,10 +13,10 @@ import Permalink from 'mastodon/components/permalink';
|
|||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' },
|
favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' },
|
||||||
follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
|
follow : { id: 'notification.follow', defaultMessage: '{name} followed you' },
|
||||||
ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
|
ownPoll : { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
|
||||||
poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
|
poll : { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
|
||||||
reblog: { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
|
reblog : { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const notificationForScreenReader = (intl, message, timestamp) => {
|
const notificationForScreenReader = (intl, message, timestamp) => {
|
||||||
@ -35,31 +35,31 @@ class Notification extends ImmutablePureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
notification: ImmutablePropTypes.map.isRequired,
|
notification : ImmutablePropTypes.map.isRequired,
|
||||||
hidden: PropTypes.bool,
|
hidden : PropTypes.bool,
|
||||||
onMoveUp: PropTypes.func.isRequired,
|
onMoveUp : PropTypes.func.isRequired,
|
||||||
onMoveDown: PropTypes.func.isRequired,
|
onMoveDown : PropTypes.func.isRequired,
|
||||||
onMention: PropTypes.func.isRequired,
|
onMention : PropTypes.func.isRequired,
|
||||||
onFavourite: PropTypes.func.isRequired,
|
onFavourite : PropTypes.func.isRequired,
|
||||||
onReblog: PropTypes.func.isRequired,
|
onReblog : PropTypes.func.isRequired,
|
||||||
onToggleHidden: PropTypes.func.isRequired,
|
onToggleHidden : PropTypes.func.isRequired,
|
||||||
status: ImmutablePropTypes.map,
|
status : ImmutablePropTypes.map,
|
||||||
intl: PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
getScrollPosition: PropTypes.func,
|
getScrollPosition : PropTypes.func,
|
||||||
updateScrollBottom: PropTypes.func,
|
updateScrollBottom: PropTypes.func,
|
||||||
cacheMediaWidth: PropTypes.func,
|
cacheMediaWidth : PropTypes.func,
|
||||||
cachedMediaWidth: PropTypes.number,
|
cachedMediaWidth : PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMoveUp = () => {
|
handleMoveUp = () => {
|
||||||
const { notification, onMoveUp } = this.props;
|
const { notification, onMoveUp } = this.props;
|
||||||
onMoveUp(notification.get('id'));
|
onMoveUp(notification.get('id'));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMoveDown = () => {
|
handleMoveDown = () => {
|
||||||
const { notification, onMoveDown } = this.props;
|
const { notification, onMoveDown } = this.props;
|
||||||
onMoveDown(notification.get('id'));
|
onMoveDown(notification.get('id'));
|
||||||
}
|
};
|
||||||
|
|
||||||
handleOpen = () => {
|
handleOpen = () => {
|
||||||
const { notification } = this.props;
|
const { notification } = this.props;
|
||||||
@ -69,94 +69,131 @@ class Notification extends ImmutablePureComponent {
|
|||||||
} else {
|
} else {
|
||||||
this.handleOpenProfile();
|
this.handleOpenProfile();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleOpenProfile = () => {
|
handleOpenProfile = () => {
|
||||||
const { notification } = this.props;
|
const { notification } = this.props;
|
||||||
this.context.router.history.push(`/accounts/${notification.getIn(['account', 'id'])}`);
|
this.context.router.history.push(`/accounts/${notification.getIn(['account', 'id'])}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleMention = e => {
|
handleMention = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const { notification, onMention } = this.props;
|
const { notification, onMention } = this.props;
|
||||||
onMention(notification.get('account'), this.context.router.history);
|
onMention(notification.get('account'), this.context.router.history);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyFavourite = () => {
|
handleHotkeyFavourite = () => {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
if (status) this.props.onFavourite(status);
|
if (status) this.props.onFavourite(status);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyBoost = e => {
|
handleHotkeyBoost = e => {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
if (status) this.props.onReblog(status, e);
|
if (status) this.props.onReblog(status, e);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyToggleHidden = () => {
|
handleHotkeyToggleHidden = () => {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
if (status) this.props.onToggleHidden(status);
|
if (status) this.props.onToggleHidden(status);
|
||||||
}
|
};
|
||||||
|
|
||||||
getHandlers () {
|
getHandlers() {
|
||||||
return {
|
return {
|
||||||
reply: this.handleMention,
|
reply : this.handleMention,
|
||||||
favourite: this.handleHotkeyFavourite,
|
favourite : this.handleHotkeyFavourite,
|
||||||
boost: this.handleHotkeyBoost,
|
boost : this.handleHotkeyBoost,
|
||||||
mention: this.handleMention,
|
mention : this.handleMention,
|
||||||
open: this.handleOpen,
|
open : this.handleOpen,
|
||||||
openProfile: this.handleOpenProfile,
|
openProfile : this.handleOpenProfile,
|
||||||
moveUp: this.handleMoveUp,
|
moveUp : this.handleMoveUp,
|
||||||
moveDown: this.handleMoveDown,
|
moveDown : this.handleMoveDown,
|
||||||
toggleHidden: this.handleHotkeyToggleHidden,
|
toggleHidden: this.handleHotkeyToggleHidden,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFollow (notification, account, link) {
|
renderFollow(notification, account, link) {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
<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
|
||||||
<div className='notification__message'>
|
className='notification notification-follow focusable'
|
||||||
<div className='notification__favourite-icon-wrapper'>
|
tabIndex='0'
|
||||||
<Icon id='user-plus' fixedWidth />
|
aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}
|
||||||
</div>
|
>
|
||||||
|
|
||||||
|
|
||||||
<span title={notification.get('created_at')}>
|
<span title={notification.get('created_at')}>
|
||||||
<FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} />
|
<span className='media'>
|
||||||
</span>
|
<span className='media-left'>
|
||||||
</div>
|
<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 >
|
||||||
|
|
||||||
<AccountContainer id={account.get('id')} hidden={this.props.hidden} />
|
</div >
|
||||||
</div>
|
</HotKeys >
|
||||||
</HotKeys>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFollowRequest (notification, account, link) {
|
renderFollowRequest(notification, account, link) {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
<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__message'>
|
||||||
<div className='notification__favourite-icon-wrapper'>
|
<div className='notification__favourite-icon-wrapper'>
|
||||||
<Icon id='user' fixedWidth />
|
<Icon
|
||||||
</div>
|
id='user'
|
||||||
|
fixedWidth
|
||||||
|
/>
|
||||||
|
</div >
|
||||||
|
|
||||||
<span title={notification.get('created_at')}>
|
<span title={notification.get('created_at')}>
|
||||||
<FormattedMessage id='notification.follow_request' defaultMessage='{name} has requested to follow you' values={{ name: link }} />
|
<FormattedMessage
|
||||||
</span>
|
id='notification.follow_request'
|
||||||
</div>
|
defaultMessage='{name} has requested to follow you'
|
||||||
|
values={{ name: link }}
|
||||||
|
/>
|
||||||
|
</span >
|
||||||
|
</div >
|
||||||
|
|
||||||
<FollowRequestContainer id={account.get('id')} withNote={false} hidden={this.props.hidden} />
|
<FollowRequestContainer
|
||||||
</div>
|
id={account.get('id')}
|
||||||
</HotKeys>
|
withNote={false}
|
||||||
|
hidden={this.props.hidden}
|
||||||
|
/>
|
||||||
|
</div >
|
||||||
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMention (notification) {
|
renderMention(notification) {
|
||||||
return (
|
return (
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
id={notification.get('status')}
|
id={notification.get('status')}
|
||||||
@ -173,21 +210,33 @@ class Notification extends ImmutablePureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFavourite (notification, link) {
|
renderFavourite(notification, link) {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
<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__message'>
|
||||||
<div className='notification__favourite-icon-wrapper'>
|
<div className='notification__favourite-icon-wrapper'>
|
||||||
<Icon id='star' className='star-icon' fixedWidth />
|
<Icon
|
||||||
</div>
|
id='star'
|
||||||
|
className='star-icon'
|
||||||
|
fixedWidth
|
||||||
|
/>
|
||||||
|
</div >
|
||||||
|
|
||||||
<span title={notification.get('created_at')}>
|
<span title={notification.get('created_at')}>
|
||||||
<FormattedMessage id='notification.favourite' defaultMessage='{name} favourited your status' values={{ name: link }} />
|
<FormattedMessage
|
||||||
</span>
|
id='notification.favourite'
|
||||||
</div>
|
defaultMessage='{name} favourited your status'
|
||||||
|
values={{ name: link }}
|
||||||
|
/>
|
||||||
|
</span >
|
||||||
|
</div >
|
||||||
|
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
id={notification.get('status')}
|
id={notification.get('status')}
|
||||||
@ -200,26 +249,37 @@ class Notification extends ImmutablePureComponent {
|
|||||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div >
|
||||||
</HotKeys>
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderReblog (notification, link) {
|
renderReblog(notification, link) {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
<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__message'>
|
||||||
<div className='notification__favourite-icon-wrapper'>
|
<div className='notification__favourite-icon-wrapper'>
|
||||||
<Icon id='retweet' fixedWidth />
|
<Icon
|
||||||
</div>
|
id='retweet'
|
||||||
|
fixedWidth
|
||||||
|
/>
|
||||||
|
</div >
|
||||||
|
|
||||||
<span title={notification.get('created_at')}>
|
<span title={notification.get('created_at')}>
|
||||||
<FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} />
|
<FormattedMessage
|
||||||
</span>
|
id='notification.reblog'
|
||||||
</div>
|
defaultMessage='{name} boosted your status'
|
||||||
|
values={{ name: link }}
|
||||||
|
/>
|
||||||
|
</span >
|
||||||
|
</div >
|
||||||
|
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
id={notification.get('status')}
|
id={notification.get('status')}
|
||||||
@ -232,32 +292,45 @@ class Notification extends ImmutablePureComponent {
|
|||||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div >
|
||||||
</HotKeys>
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPoll (notification, account) {
|
renderPoll(notification, account) {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
const ownPoll = me === account.get('id');
|
const ownPoll = me === account.get('id');
|
||||||
const message = ownPoll ? intl.formatMessage(messages.ownPoll) : intl.formatMessage(messages.poll);
|
const message = ownPoll ? intl.formatMessage(messages.ownPoll) : intl.formatMessage(messages.poll);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
<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__message'>
|
||||||
<div className='notification__favourite-icon-wrapper'>
|
<div className='notification__favourite-icon-wrapper'>
|
||||||
<Icon id='tasks' fixedWidth />
|
<Icon
|
||||||
</div>
|
id='tasks'
|
||||||
|
fixedWidth
|
||||||
|
/>
|
||||||
|
</div >
|
||||||
|
|
||||||
<span title={notification.get('created_at')}>
|
<span title={notification.get('created_at')}>
|
||||||
{ownPoll ? (
|
{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>
|
</span >
|
||||||
</div>
|
</div >
|
||||||
|
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
id={notification.get('status')}
|
id={notification.get('status')}
|
||||||
@ -270,18 +343,24 @@ class Notification extends ImmutablePureComponent {
|
|||||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div >
|
||||||
</HotKeys>
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { notification } = this.props;
|
const { notification } = this.props;
|
||||||
const account = notification.get('account');
|
const account = notification.get('account');
|
||||||
const displayNameHtml = { __html: account.get('display_name_html') };
|
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 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':
|
case 'follow':
|
||||||
return this.renderFollow(notification, account, link);
|
return this.renderFollow(notification, account, link);
|
||||||
case 'follow_request':
|
case 'follow_request':
|
||||||
|
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import LoadingIndicator from '../../components/loading_indicator';
|
import LoadingIndicator from '../../components/loading_indicator';
|
||||||
import { fetchReblogs } from '../../actions/interactions';
|
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 AccountContainer from '../../containers/account_container';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
@ -25,15 +25,15 @@ export default @connect(mapStateToProps)
|
|||||||
class Reblogs extends ImmutablePureComponent {
|
class Reblogs extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params : PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch : PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
shouldUpdateScroll: PropTypes.func,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds : ImmutablePropTypes.list,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn : PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
if (!this.props.accountIds) {
|
if (!this.props.accountIds) {
|
||||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||||
}
|
}
|
||||||
@ -47,20 +47,23 @@ class Reblogs extends ImmutablePureComponent {
|
|||||||
|
|
||||||
handleRefresh = () => {
|
handleRefresh = () => {
|
||||||
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
this.props.dispatch(fetchReblogs(this.props.params.statusId));
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
|
const { intl, shouldUpdateScroll, accountIds, multiColumn } = this.props;
|
||||||
|
|
||||||
if (!accountIds) {
|
if (!accountIds) {
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column >
|
||||||
<LoadingIndicator />
|
<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 (
|
return (
|
||||||
<Column bindToDocument={!multiColumn}>
|
<Column bindToDocument={!multiColumn}>
|
||||||
@ -68,7 +71,12 @@ class Reblogs extends ImmutablePureComponent {
|
|||||||
showBackButton
|
showBackButton
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
extraButton={(
|
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}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
{accountIds.map(id =>
|
{accountIds.map(id =>
|
||||||
<AccountContainer key={id} id={id} withNote={false} />
|
(<AccountContainer
|
||||||
|
key={id}
|
||||||
|
id={id}
|
||||||
|
withNote={false}
|
||||||
|
/>),
|
||||||
)}
|
)}
|
||||||
</ScrollableList>
|
</ScrollableList >
|
||||||
</Column>
|
</Column >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,15 +45,15 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
};
|
||||||
|
|
||||||
handleOpenVideo = (media, startTime) => {
|
handleOpenVideo = (media, startTime) => {
|
||||||
this.props.onOpenVideo(media, startTime);
|
this.props.onOpenVideo(media, startTime);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleExpandedToggle = () => {
|
handleExpandedToggle = () => {
|
||||||
this.props.onToggleHidden(this.props.status);
|
this.props.onToggleHidden(this.props.status);
|
||||||
}
|
};
|
||||||
|
|
||||||
_measureHeight (heightJustChanged) {
|
_measureHeight (heightJustChanged) {
|
||||||
if (this.props.measureHeight && this.node) {
|
if (this.props.measureHeight && this.node) {
|
||||||
@ -68,7 +68,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
|||||||
setRef = c => {
|
setRef = c => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
this._measureHeight();
|
this._measureHeight();
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidUpdate (prevProps, prevState) {
|
componentDidUpdate (prevProps, prevState) {
|
||||||
this._measureHeight(prevState.height !== this.state.height);
|
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');
|
window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
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 style={outerStyle}>
|
||||||
<div ref={this.setRef} className={classNames('detailed-status', { compact })}>
|
<div ref={this.setRef} className={classNames('detailed-status', { compact })}>
|
||||||
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
|
<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>
|
<div className='detailed-status__display-avatar'><Avatar
|
||||||
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
|
account={status.get('account')}
|
||||||
|
size={55}
|
||||||
|
/></div >
|
||||||
|
<DisplayName
|
||||||
|
account={status.get('account')}
|
||||||
|
localDomain={this.props.domain}
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
||||||
|
@ -551,8 +551,9 @@ class Status extends ImmutablePureComponent {
|
|||||||
className={classNames('scrollable', { fullscreen })}
|
className={classNames('scrollable', { fullscreen })}
|
||||||
ref={this.setRef}
|
ref={this.setRef}
|
||||||
>
|
>
|
||||||
|
{/*<h2 className='debug'>ancestors:</h2 >*/}
|
||||||
{ancestors}
|
{ancestors}
|
||||||
|
{/*<h2 className='debug'>common:</h2 >*/}
|
||||||
<HotKeys handlers={handlers}>
|
<HotKeys handlers={handlers}>
|
||||||
<div
|
<div
|
||||||
className={classNames('focusable', 'detailed-status__wrapper')}
|
className={classNames('focusable', 'detailed-status__wrapper')}
|
||||||
@ -591,7 +592,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
/>
|
/>
|
||||||
</div >
|
</div >
|
||||||
</HotKeys >
|
</HotKeys >
|
||||||
|
{/*<h2 className='debug'>Descendants:</h2 >*/}
|
||||||
{descendants}
|
{descendants}
|
||||||
</div >
|
</div >
|
||||||
</ScrollContainer >
|
</ScrollContainer >
|
||||||
|
@ -26,16 +26,29 @@ export default class ActionsModal extends ImmutablePureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={`${text}-${i}`}>
|
<li key={`${text}-${i}`}>
|
||||||
<a href={href} target='_blank' rel='noopener noreferrer' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
|
<a
|
||||||
{icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
|
href={href}
|
||||||
<div>
|
target='_blank'
|
||||||
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
|
rel='noopener noreferrer'
|
||||||
<div>{meta}</div>
|
onClick={this.props.onClick}
|
||||||
</div>
|
data-index={i}
|
||||||
</a>
|
className={classNames({ active })}
|
||||||
</li>
|
>
|
||||||
|
{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 () {
|
render () {
|
||||||
const status = this.props.status && (
|
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'>
|
<a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
|
||||||
<div className='status__avatar'>
|
<div className='status__avatar'>
|
||||||
<Avatar account={this.props.status.get('account')} size={48} />
|
<Avatar
|
||||||
|
account={this.props.status.get('account')}
|
||||||
|
size={55}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DisplayName account={this.props.status.get('account')} />
|
<DisplayName account={this.props.status.get('account')} />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import Button from '../../../components/button';
|
import Button from '../../../components/button';
|
||||||
import StatusContent from '../../../components/status_content';
|
import StatusContent from '../../../components/status_content';
|
||||||
import Avatar from '../../../components/avatar';
|
import Avatar from '../../../components/avatar';
|
||||||
@ -37,7 +37,7 @@ class BoostModal extends ImmutablePureComponent {
|
|||||||
handleReblog = () => {
|
handleReblog = () => {
|
||||||
this.props.onReblog(this.props.status);
|
this.props.onReblog(this.props.status);
|
||||||
this.props.onClose();
|
this.props.onClose();
|
||||||
}
|
};
|
||||||
|
|
||||||
handleAccountClick = (e) => {
|
handleAccountClick = (e) => {
|
||||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
@ -45,11 +45,11 @@ class BoostModal extends ImmutablePureComponent {
|
|||||||
this.props.onClose();
|
this.props.onClose();
|
||||||
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
setRef = (c) => {
|
setRef = (c) => {
|
||||||
this.button = c;
|
this.button = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, intl } = this.props;
|
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'>
|
<a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
|
||||||
<div className='status__avatar'>
|
<div className='status__avatar'>
|
||||||
<Avatar account={status.get('account')} size={48} />
|
<Avatar
|
||||||
|
account={status.get('account')}
|
||||||
|
size={55}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DisplayName account={status.get('account')} />
|
<DisplayName account={status.get('account')} />
|
||||||
|
@ -31,6 +31,8 @@ import NavigationPanel from './navigation_panel';
|
|||||||
|
|
||||||
import detectPassiveEvents from 'detect-passive-events';
|
import detectPassiveEvents from 'detect-passive-events';
|
||||||
import { scrollRight } from '../../../scroll';
|
import { scrollRight } from '../../../scroll';
|
||||||
|
import LinkFooter from './link_footer';
|
||||||
|
import InstantMessaging from './messaging/instantMessaging';
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
'COMPOSE' : Compose,
|
'COMPOSE' : Compose,
|
||||||
@ -157,10 +159,10 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||||||
|
|
||||||
const view = (index === columnIndex) ?
|
const view = (index === columnIndex) ?
|
||||||
React.cloneElement(this.props.children) :
|
React.cloneElement(this.props.children) :
|
||||||
<ColumnLoading
|
(<ColumnLoading
|
||||||
title={title}
|
title={title}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
/>;
|
/>);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -187,12 +189,12 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||||
|
|
||||||
if (singleColumn) {
|
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'
|
key='floating-action-button'
|
||||||
to='/statuses/new'
|
to='/statuses/new'
|
||||||
className='floating-action-button'
|
className='floating-action-button'
|
||||||
aria-label={intl.formatMessage(messages.publish)}
|
aria-label={intl.formatMessage(messages.publish)}
|
||||||
><Icon id='pencil' /></Link >;
|
><Icon id='pencil' /></Link >);
|
||||||
|
|
||||||
const content = columnIndex !== -1 ? (
|
const content = columnIndex !== -1 ? (
|
||||||
<ReactSwipeableViews
|
<ReactSwipeableViews
|
||||||
@ -217,10 +219,13 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='columns-area__panels'>
|
<div className='columns-area__panels'>
|
||||||
|
|
||||||
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
|
<div className='columns-area__panels__pane columns-area__panels__pane--compositional'>
|
||||||
<div className='columns-area__panels__pane__inner'>
|
<div className='columns-area__panels__pane__inner'>
|
||||||
<ComposePanel />
|
<ComposePanel />
|
||||||
|
|
||||||
</div >
|
</div >
|
||||||
|
|
||||||
</div >
|
</div >
|
||||||
|
|
||||||
<div className='columns-area__panels__main'>
|
<div className='columns-area__panels__main'>
|
||||||
@ -233,8 +238,12 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||||||
<NavigationPanel />
|
<NavigationPanel />
|
||||||
</div >
|
</div >
|
||||||
</div >
|
</div >
|
||||||
|
<LinkFooter withHotkeys />
|
||||||
{floatingActionButton}
|
{floatingActionButton}
|
||||||
|
<div className='hidden_nope'>
|
||||||
|
|
||||||
|
<InstantMessaging />
|
||||||
|
</div >
|
||||||
</div >
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -255,17 +264,20 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||||||
loading={this.renderLoading(column.get('id'))}
|
loading={this.renderLoading(column.get('id'))}
|
||||||
error={this.renderError}
|
error={this.renderError}
|
||||||
>
|
>
|
||||||
{SpecificComponent => <SpecificComponent
|
{SpecificComponent => (<SpecificComponent
|
||||||
columnId={column.get('uuid')}
|
columnId={column.get('uuid')}
|
||||||
params={params}
|
params={params}
|
||||||
multiColumn {...other} />}
|
multiColumn {...other}
|
||||||
|
/>)}
|
||||||
|
|
||||||
</BundleContainer >
|
</BundleContainer >
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
|
{React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
|
||||||
</div >
|
</div >
|
||||||
);
|
)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,13 @@ import React from 'react';
|
|||||||
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
import SearchContainer from 'mastodon/features/compose/containers/search_container';
|
||||||
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
|
||||||
import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
|
import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
|
||||||
import LinkFooter from './link_footer';
|
|
||||||
|
|
||||||
const ComposePanel = () => (
|
const ComposePanel = () => (
|
||||||
<div className='compose-panel'>
|
<div className='compose-panel'>
|
||||||
<SearchContainer openInRoute />
|
<SearchContainer openInRoute />
|
||||||
<NavigationContainer />
|
<NavigationContainer />
|
||||||
<ComposeFormContainer singleColumn />
|
<ComposeFormContainer singleColumn />
|
||||||
<LinkFooter withHotkeys />
|
</div >
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default ComposePanel;
|
export default ComposePanel;
|
||||||
|
@ -2,10 +2,11 @@ import { connect } from 'react-redux';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
import { invitesEnabled, repository, source_url, version } from 'mastodon/initial_state';
|
import { invitesEnabled, repository, source_url, version } from 'mastodon/initial_state';
|
||||||
import { logOut } from 'mastodon/utils/log_out';
|
import { logOut } from 'mastodon/utils/log_out';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
|
import { isStaff } from '../../../initial_state';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
|
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
|
||||||
@ -22,15 +23,27 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// const displaythemetoggler = true;
|
||||||
export default @injectIntl
|
export default @injectIntl
|
||||||
@connect(null, mapDispatchToProps)
|
@connect(null, mapDispatchToProps)
|
||||||
class LinkFooter extends React.PureComponent {
|
class LinkFooter extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
withHotkeys: PropTypes.bool,
|
enableChristmasSnow : PropTypes.bool,
|
||||||
|
minimumWeekToShowSnow: PropTypes.number,
|
||||||
|
snowActive : PropTypes.bool,
|
||||||
|
withHotkeys : PropTypes.bool,
|
||||||
|
snow : PropTypes.func,
|
||||||
|
themeIsDark : PropTypes.bool,
|
||||||
|
theme : PropTypes.string,
|
||||||
onLogout : PropTypes.func.isRequired,
|
onLogout : PropTypes.func.isRequired,
|
||||||
intl : PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
static defaultProps = {
|
||||||
|
enableChristmasSnow : true,
|
||||||
|
themeIsDark : true,
|
||||||
|
minimumWeekToShowSnow: 48,
|
||||||
|
};
|
||||||
|
|
||||||
handleLogoutClick = e => {
|
handleLogoutClick = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -41,35 +54,177 @@ class LinkFooter extends React.PureComponent {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
Date.prototype.getWeek = function () {
|
||||||
|
var onejan = new Date(this.getFullYear(), 0, 1);
|
||||||
|
return Math.ceil((((this - onejan) / 86400000) + onejan.getDay() + 1) / 7);
|
||||||
|
};
|
||||||
|
|
||||||
|
var weekNumber = (new Date()).getWeek();
|
||||||
|
// display snow during the last two weeks of the year
|
||||||
|
const shouldWeDisplaySnow = (weekNumber > props.minimumWeekToShowSnow) && props.enableChristmasSnow;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
enableChristmasSnow: shouldWeDisplaySnow,
|
||||||
|
theme : props.theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
// make snow effect
|
||||||
|
if (shouldWeDisplaySnow) {
|
||||||
|
import('../../../utils/snowstorm-min')
|
||||||
|
.then((snowstorm) => {
|
||||||
|
Window.snowstorm = snowstorm.default;
|
||||||
|
this.state.snow = Window.snowstorm;
|
||||||
|
// snowstorm.start();
|
||||||
|
this.state.snowActive = true;
|
||||||
|
})
|
||||||
|
.catch((err) => console.error(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
render() {
|
||||||
|
const HashTagNavlinks = ['Mastoart', 'OpenStreetMaps', 'Ironèmes', 'vélo'];
|
||||||
const { withHotkeys } = this.props;
|
const { withHotkeys } = this.props;
|
||||||
|
var snowClasses = this.props.enableChristmasSnow ? 'snow-button active' : 'snow-button ';
|
||||||
|
const navToTags = HashTagNavlinks.map(element => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
className='tag-element btn-small btn'
|
||||||
|
key={element}
|
||||||
|
>
|
||||||
|
|
||||||
|
<NavLink
|
||||||
|
exact
|
||||||
|
activeClassName='active'
|
||||||
|
to={'/timelines/tag/' + element}
|
||||||
|
title='Mastoart'
|
||||||
|
>
|
||||||
|
#{element}
|
||||||
|
</NavLink >
|
||||||
|
</li >
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='getting-started__footer'>
|
|
||||||
<ul >
|
<div className='links-started__footer desktop-only'>
|
||||||
<li >
|
|
||||||
<a href='https://liberapay.com/cipherbliss'>Supportez Cipherbliss</a >
|
<div className='extras'>
|
||||||
</li >
|
|
||||||
<li >
|
{/*<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'>
|
<a href='https://mastodon.cipherbliss.com/@tykayn'>
|
||||||
<i className='fa fa-paper-plane' />
|
<i className='fa fa-paper-plane' />
|
||||||
contactez nous</a >
|
contactez nous
|
||||||
</li >
|
</a >
|
||||||
<li >
|
<a href='https://liberapay.com/cipherbliss'><i className='fa fa-coffee' /> Supportez
|
||||||
|
Cipherbliss</a >
|
||||||
|
|
||||||
<a href='/admin/tags?pending_review=1'>
|
<a href='https://peertube.cipherbliss.com'> <i className='fa fa-play ' /> Videos</a >
|
||||||
<i className='fa fa-fire' />
|
<a href='https://framadate.org/'> <i className='fa fa-calendar' /> FramaDate</a >
|
||||||
Trending hashtags</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 />
|
<hr />
|
||||||
</li >
|
<div className='suggested-tags'>
|
||||||
{invitesEnabled && <li ><a
|
<ul >
|
||||||
|
{navToTags}
|
||||||
|
</ul >
|
||||||
|
</div >
|
||||||
|
</div >
|
||||||
|
</div >
|
||||||
|
<ul >
|
||||||
|
{invitesEnabled && (
|
||||||
|
<li >
|
||||||
|
<a
|
||||||
href='/invites'
|
href='/invites'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
><FormattedMessage
|
>
|
||||||
|
<FormattedMessage
|
||||||
id='getting_started.invite'
|
id='getting_started.invite'
|
||||||
defaultMessage='Invite people'
|
defaultMessage='Invite people'
|
||||||
/> ·</a >
|
/> ·</a >
|
||||||
</li >}
|
</li >
|
||||||
|
)}
|
||||||
{withHotkeys && <li ><Link to='/keyboard-shortcuts'>
|
{withHotkeys && <li ><Link to='/keyboard-shortcuts'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='navigation_bar.keyboard_shortcuts'
|
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,11 +8,9 @@ import FollowRequestsNavLink from './follow_requests_nav_link';
|
|||||||
import ListPanel from './list_panel';
|
import ListPanel from './list_panel';
|
||||||
import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container';
|
import TrendsContainer from 'mastodon/features/getting_started/containers/trends_container';
|
||||||
|
|
||||||
const showMessaging = true;
|
|
||||||
|
|
||||||
const NavigationPanel = () => (
|
const NavigationPanel = () => (
|
||||||
<div className='navigation-panel'>
|
<div className='navigation-panel'>
|
||||||
|
<div className='small-texts timelines'>
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent'
|
className='column-link column-link--transparent'
|
||||||
to='/timelines/home'
|
to='/timelines/home'
|
||||||
@ -26,18 +24,6 @@ const NavigationPanel = () => (
|
|||||||
id='tabs_bar.home'
|
id='tabs_bar.home'
|
||||||
defaultMessage='Home'
|
defaultMessage='Home'
|
||||||
/></NavLink >
|
/></NavLink >
|
||||||
<NavLink
|
|
||||||
className='column-link column-link--transparent'
|
|
||||||
to='/notifications'
|
|
||||||
data-preview-title-id='column.notifications'
|
|
||||||
data-preview-icon='bell'
|
|
||||||
><NotificationsCounterIcon
|
|
||||||
className='column-link__icon'
|
|
||||||
/><FormattedMessage
|
|
||||||
id='tabs_bar.notifications'
|
|
||||||
defaultMessage='Notifications'
|
|
||||||
/></NavLink >
|
|
||||||
<FollowRequestsNavLink />
|
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent'
|
className='column-link column-link--transparent'
|
||||||
to='/timelines/public/local'
|
to='/timelines/public/local'
|
||||||
@ -51,6 +37,7 @@ const NavigationPanel = () => (
|
|||||||
id='tabs_bar.local_timeline'
|
id='tabs_bar.local_timeline'
|
||||||
defaultMessage='Local'
|
defaultMessage='Local'
|
||||||
/></NavLink >
|
/></NavLink >
|
||||||
|
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent'
|
className='column-link column-link--transparent'
|
||||||
exact
|
exact
|
||||||
@ -65,6 +52,20 @@ const NavigationPanel = () => (
|
|||||||
id='tabs_bar.federated_timeline'
|
id='tabs_bar.federated_timeline'
|
||||||
defaultMessage='Federated'
|
defaultMessage='Federated'
|
||||||
/></NavLink >
|
/></NavLink >
|
||||||
|
|
||||||
|
</div >
|
||||||
|
<div className='spacer'></div >
|
||||||
|
<NavLink
|
||||||
|
className='column-link column-link--transparent'
|
||||||
|
to='/notifications'
|
||||||
|
data-preview-title-id='column.notifications'
|
||||||
|
data-preview-icon='bell'
|
||||||
|
><NotificationsCounterIcon
|
||||||
|
className='column-link__icon'
|
||||||
|
/><FormattedMessage
|
||||||
|
id='tabs_bar.notifications'
|
||||||
|
defaultMessage='Notifications'
|
||||||
|
/></NavLink >
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent'
|
className='column-link column-link--transparent'
|
||||||
to='/timelines/direct'
|
to='/timelines/direct'
|
||||||
@ -76,6 +77,10 @@ const NavigationPanel = () => (
|
|||||||
id='navigation_bar.direct'
|
id='navigation_bar.direct'
|
||||||
defaultMessage='Direct messages'
|
defaultMessage='Direct messages'
|
||||||
/></NavLink >
|
/></NavLink >
|
||||||
|
<FollowRequestsNavLink />
|
||||||
|
|
||||||
|
<div className='spacer'></div >
|
||||||
|
|
||||||
<NavLink
|
<NavLink
|
||||||
className='column-link column-link--transparent'
|
className='column-link column-link--transparent'
|
||||||
to='/favourites'
|
to='/favourites'
|
||||||
@ -151,8 +156,6 @@ const NavigationPanel = () => (
|
|||||||
|
|
||||||
{showTrends && <div className='flex-spacer' />}
|
{showTrends && <div className='flex-spacer' />}
|
||||||
{showTrends && <TrendsContainer />}
|
{showTrends && <TrendsContainer />}
|
||||||
{/*{showMessaging && <Messaging />}*/}
|
|
||||||
{/*<Messaging />*/}
|
|
||||||
|
|
||||||
</div >
|
</div >
|
||||||
);
|
);
|
||||||
|
@ -8,19 +8,81 @@ import Icon from 'mastodon/components/icon';
|
|||||||
import NotificationsCounterIcon from './notifications_counter_icon';
|
import NotificationsCounterIcon from './notifications_counter_icon';
|
||||||
|
|
||||||
export const links = [
|
export const links = [
|
||||||
<NavLink className='tabs-bar__link' to='/timelines/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
|
<NavLink
|
||||||
<NavLink className='tabs-bar__link' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
|
className='tabs-bar__link'
|
||||||
<NavLink className='tabs-bar__link' to='/timelines/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
|
to='/timelines/home'
|
||||||
<NavLink className='tabs-bar__link' exact to='/timelines/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
|
data-preview-title-id='column.home'
|
||||||
<NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' data-preview-icon='bell' ><Icon id='search' fixedWidth /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
|
data-preview-icon='home'
|
||||||
<NavLink className='tabs-bar__link' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><Icon id='bars' fixedWidth /></NavLink>,
|
><Icon
|
||||||
|
id='home'
|
||||||
|
fixedWidth
|
||||||
|
/><FormattedMessage
|
||||||
|
id='tabs_bar.home'
|
||||||
|
defaultMessage='Home'
|
||||||
|
/></NavLink >,
|
||||||
|
<NavLink
|
||||||
|
className='tabs-bar__link'
|
||||||
|
to='/notifications'
|
||||||
|
data-preview-title-id='column.notifications'
|
||||||
|
data-preview-icon='bell'
|
||||||
|
><NotificationsCounterIcon /><FormattedMessage
|
||||||
|
id='tabs_bar.notifications'
|
||||||
|
defaultMessage='Notifications'
|
||||||
|
/></NavLink >,
|
||||||
|
<NavLink
|
||||||
|
className='tabs-bar__link'
|
||||||
|
to='/timelines/public/local'
|
||||||
|
data-preview-title-id='column.community'
|
||||||
|
data-preview-icon='users'
|
||||||
|
><Icon
|
||||||
|
id='users'
|
||||||
|
fixedWidth
|
||||||
|
/><FormattedMessage
|
||||||
|
id='tabs_bar.local_timeline'
|
||||||
|
defaultMessage='Local'
|
||||||
|
/></NavLink >,
|
||||||
|
<NavLink
|
||||||
|
className='tabs-bar__link'
|
||||||
|
exact
|
||||||
|
to='/timelines/public'
|
||||||
|
data-preview-title-id='column.public'
|
||||||
|
data-preview-icon='globe'
|
||||||
|
><Icon
|
||||||
|
id='globe'
|
||||||
|
fixedWidth
|
||||||
|
/><FormattedMessage
|
||||||
|
id='tabs_bar.federated_timeline'
|
||||||
|
defaultMessage='Federated'
|
||||||
|
/></NavLink >,
|
||||||
|
<NavLink
|
||||||
|
className='tabs-bar__link optional'
|
||||||
|
to='/search'
|
||||||
|
data-preview-title-id='tabs_bar.search'
|
||||||
|
data-preview-icon='bell'
|
||||||
|
><Icon
|
||||||
|
id='search'
|
||||||
|
fixedWidth
|
||||||
|
/><FormattedMessage
|
||||||
|
id='tabs_bar.search'
|
||||||
|
defaultMessage='Search'
|
||||||
|
/></NavLink >,
|
||||||
|
<NavLink
|
||||||
|
className='tabs-bar__link'
|
||||||
|
style={{ flexGrow: '0', flexBasis: '30px' }}
|
||||||
|
to='/getting-started'
|
||||||
|
data-preview-title-id='getting_started.heading'
|
||||||
|
data-preview-icon='bars'
|
||||||
|
><Icon
|
||||||
|
id='bars'
|
||||||
|
fixedWidth
|
||||||
|
/></NavLink >,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getIndex (path) {
|
export function getIndex(path) {
|
||||||
return links.findIndex(link => link.props.to === path);
|
return links.findIndex(link => link.props.to === path);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLink (index) {
|
export function getLink(index) {
|
||||||
return links[index].props.to;
|
return links[index].props.to;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,13 +91,13 @@ export default @injectIntl
|
|||||||
class TabsBar extends React.PureComponent {
|
class TabsBar extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
intl: PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
history: PropTypes.object.isRequired,
|
history: PropTypes.object.isRequired,
|
||||||
}
|
};
|
||||||
|
|
||||||
setRef = ref => {
|
setRef = ref => {
|
||||||
this.node = ref;
|
this.node = ref;
|
||||||
}
|
};
|
||||||
|
|
||||||
handleClick = (e) => {
|
handleClick = (e) => {
|
||||||
// Only apply optimization for touch devices, which we assume are slower
|
// Only apply optimization for touch devices, which we assume are slower
|
||||||
@ -50,7 +112,6 @@ class TabsBar extends React.PureComponent {
|
|||||||
const nextTab = tabs.find(tab => tab.contains(e.target));
|
const nextTab = tabs.find(tab => tab.contains(e.target));
|
||||||
const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
|
const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
|
||||||
|
|
||||||
|
|
||||||
if (currentTab !== nextTab) {
|
if (currentTab !== nextTab) {
|
||||||
if (currentTab) {
|
if (currentTab) {
|
||||||
currentTab.classList.remove('active');
|
currentTab.classList.remove('active');
|
||||||
@ -67,19 +128,28 @@ class TabsBar extends React.PureComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { intl: { formatMessage } } = this.props;
|
const { intl: { formatMessage } } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='tabs-bar__wrapper'>
|
<div className='tabs-bar__wrapper'>
|
||||||
<nav className='tabs-bar' ref={this.setRef}>
|
|
||||||
{links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
|
<nav
|
||||||
</nav>
|
className='tabs-bar'
|
||||||
|
ref={this.setRef}
|
||||||
|
>
|
||||||
|
{links.map(link => React.cloneElement(link, {
|
||||||
|
key : link.props.to,
|
||||||
|
onClick : this.handleClick,
|
||||||
|
'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }),
|
||||||
|
}))}
|
||||||
|
</nav >
|
||||||
|
|
||||||
<div id='tabs-bar__portal' />
|
<div id='tabs-bar__portal' />
|
||||||
</div>
|
|
||||||
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,112 +10,118 @@ import LoadingBarContainer from './containers/loading_bar_container';
|
|||||||
import ModalContainer from './containers/modal_container';
|
import ModalContainer from './containers/modal_container';
|
||||||
import { isMobile } from '../../is_mobile';
|
import { isMobile } from '../../is_mobile';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { uploadCompose, resetCompose } from '../../actions/compose';
|
import { resetCompose, uploadCompose } from '../../actions/compose';
|
||||||
import { expandHomeTimeline } from '../../actions/timelines';
|
import { expandHomeTimeline } from '../../actions/timelines';
|
||||||
import { expandNotifications } from '../../actions/notifications';
|
import { expandNotifications } from '../../actions/notifications';
|
||||||
import { fetchFilters } from '../../actions/filters';
|
import { fetchFilters } from '../../actions/filters';
|
||||||
import { clearHeight } from '../../actions/height_cache';
|
import { clearHeight } from '../../actions/height_cache';
|
||||||
import { focusApp, unfocusApp } from 'mastodon/actions/app';
|
import { focusApp, unfocusApp } from 'mastodon/actions/app';
|
||||||
import { submitMarkers } from 'mastodon/actions/markers';
|
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 UploadArea from './components/upload_area';
|
||||||
import ColumnsAreaContainer from './containers/columns_area_container';
|
import ColumnsAreaContainer from './containers/columns_area_container';
|
||||||
import DocumentTitle from './components/document_title';
|
import DocumentTitle from './components/document_title';
|
||||||
import {
|
import {
|
||||||
Compose,
|
|
||||||
Status,
|
|
||||||
GettingStarted,
|
|
||||||
KeyboardShortcuts,
|
|
||||||
PublicTimeline,
|
|
||||||
CommunityTimeline,
|
|
||||||
AccountTimeline,
|
|
||||||
AccountGallery,
|
AccountGallery,
|
||||||
HomeTimeline,
|
AccountTimeline,
|
||||||
|
Blocks,
|
||||||
|
BookmarkedStatuses,
|
||||||
|
CommunityTimeline,
|
||||||
|
Compose,
|
||||||
|
Directory,
|
||||||
|
DirectTimeline,
|
||||||
|
DomainBlocks,
|
||||||
|
FavouritedStatuses,
|
||||||
|
Favourites,
|
||||||
Followers,
|
Followers,
|
||||||
Following,
|
Following,
|
||||||
Reblogs,
|
|
||||||
Favourites,
|
|
||||||
DirectTimeline,
|
|
||||||
HashtagTimeline,
|
|
||||||
Notifications,
|
|
||||||
FollowRequests,
|
FollowRequests,
|
||||||
GenericNotFound,
|
GenericNotFound,
|
||||||
FavouritedStatuses,
|
GettingStarted,
|
||||||
BookmarkedStatuses,
|
HashtagTimeline,
|
||||||
ListTimeline,
|
HomeTimeline,
|
||||||
Blocks,
|
KeyboardShortcuts,
|
||||||
DomainBlocks,
|
|
||||||
Mutes,
|
|
||||||
PinnedStatuses,
|
|
||||||
Lists,
|
Lists,
|
||||||
|
ListTimeline,
|
||||||
|
Mutes,
|
||||||
|
Notifications,
|
||||||
|
PinnedStatuses,
|
||||||
|
PublicTimeline,
|
||||||
|
Reblogs,
|
||||||
Search,
|
Search,
|
||||||
Directory,
|
Status,
|
||||||
} from './util/async-components';
|
} 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 previewMediaState } from './components/media_modal';
|
||||||
import { previewState as previewVideoState } from './components/video_modal';
|
import { previewState as previewVideoState } from './components/video_modal';
|
||||||
|
|
||||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||||
// Without this it ends up in ~8 very commonly used bundles.
|
// Without this it ends up in ~8 very commonly used bundles.
|
||||||
import '../../components/status';
|
import '../../components/status';
|
||||||
|
import InstantMessaging from './components/messaging/instantMessaging';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
|
beforeUnload: { id: 'ui.beforeunload', defaultMessage: 'Your draft will be lost if you leave Mastodon.' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
isComposing: state.getIn(['compose', 'is_composing']),
|
isComposing : state.getIn(['compose', 'is_composing']),
|
||||||
hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
|
hasComposingText : state.getIn(['compose', 'text']).trim().length !== 0,
|
||||||
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 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,
|
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,
|
dropdownMenuIsOpen : state.getIn(['dropdown_menu', 'openId']) !== null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const keyMap = {
|
const keyMap = {
|
||||||
help: '?',
|
help : '?',
|
||||||
new: 'n',
|
new : 'n',
|
||||||
search: 's',
|
search : 's',
|
||||||
forceNew: 'option+n',
|
forceNew : 'option+n',
|
||||||
focusColumn: ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
focusColumn : ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
||||||
reply: 'r',
|
reply : 'r',
|
||||||
favourite: 'f',
|
favourite : 'f',
|
||||||
boost: 'b',
|
boost : 'b',
|
||||||
mention: 'm',
|
mention : 'm',
|
||||||
open: ['enter', 'o'],
|
open : ['enter', 'o'],
|
||||||
openProfile: 'p',
|
openProfile : 'p',
|
||||||
moveDown: ['down', 'j'],
|
moveDown : ['down', 'j'],
|
||||||
moveUp: ['up', 'k'],
|
moveUp : ['up', 'k'],
|
||||||
back: 'backspace',
|
back : 'backspace',
|
||||||
goToHome: 'g h',
|
goToHome : 'g h',
|
||||||
goToNotifications: 'g n',
|
goToNotifications: 'g n',
|
||||||
goToLocal: 'g l',
|
goToLocal : 'g l',
|
||||||
goToFederated: 'g t',
|
goToFederated : 'g t',
|
||||||
goToDirect: 'g d',
|
goToDirect : 'g d',
|
||||||
goToStart: 'g s',
|
goToStart : 'g s',
|
||||||
goToFavourites: 'g f',
|
goToFavourites : 'g f',
|
||||||
goToPinned: 'g p',
|
goToPinned : 'g p',
|
||||||
goToProfile: 'g u',
|
goToProfile : 'g u',
|
||||||
goToBlocked: 'g b',
|
goToBlocked : 'g b',
|
||||||
goToMuted: 'g m',
|
goToMuted : 'g m',
|
||||||
goToRequests: 'g r',
|
goToRequests : 'g r',
|
||||||
toggleHidden: 'x',
|
toggleHidden : 'x',
|
||||||
toggleSensitive: 'h',
|
toggleSensitive : 'h',
|
||||||
openMedia: 'e',
|
openMedia : 'e',
|
||||||
};
|
};
|
||||||
|
|
||||||
class SwitchingColumnsArea extends React.PureComponent {
|
class SwitchingColumnsArea extends React.PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children : PropTypes.node,
|
||||||
location: PropTypes.object,
|
location : PropTypes.object,
|
||||||
onLayoutChange: PropTypes.func.isRequired,
|
onLayoutChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
mobile: isMobile(window.innerWidth),
|
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 });
|
window.addEventListener('resize', this.handleResize, { passive: true });
|
||||||
|
|
||||||
if (this.state.mobile || forceSingleColumn) {
|
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)) {
|
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
|
||||||
this.node.handleChildrenContentChange();
|
this.node.handleChildrenContentChange();
|
||||||
}
|
}
|
||||||
@ -138,21 +144,14 @@ class SwitchingColumnsArea extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldUpdateScroll (_, { location }) {
|
shouldUpdateScroll(_, { location }) {
|
||||||
return location.state !== previewMediaState && location.state !== previewVideoState;
|
return location.state !== previewMediaState && location.state !== previewVideoState;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLayoutChange = debounce(() => {
|
|
||||||
// The cached heights are no longer accurate, invalidate
|
|
||||||
this.props.onLayoutChange();
|
|
||||||
}, 500, {
|
|
||||||
trailing: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
handleResize = () => {
|
handleResize = () => {
|
||||||
const mobile = isMobile(window.innerWidth);
|
const mobile = isMobile(window.innerWidth);
|
||||||
|
|
||||||
@ -163,61 +162,219 @@ class SwitchingColumnsArea extends React.PureComponent {
|
|||||||
} else {
|
} else {
|
||||||
this.handleLayoutChange();
|
this.handleLayoutChange();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
setRef = c => {
|
setRef = c => {
|
||||||
if (c) {
|
if (c) {
|
||||||
this.node = c.getWrappedInstance();
|
this.node = c.getWrappedInstance();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { children } = this.props;
|
const { children } = this.props;
|
||||||
const { mobile } = this.state;
|
const { mobile } = this.state;
|
||||||
const singleColumn = forceSingleColumn || mobile;
|
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 (
|
return (
|
||||||
<ColumnsAreaContainer ref={this.setRef} singleColumn={singleColumn}>
|
<ColumnsAreaContainer
|
||||||
<WrappedSwitch>
|
ref={this.setRef}
|
||||||
|
singleColumn={singleColumn}
|
||||||
|
>
|
||||||
|
<WrappedSwitch >
|
||||||
{redirect}
|
{redirect}
|
||||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
<WrappedRoute
|
||||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
path='/tk-example'
|
||||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
component={InstantMessaging}
|
||||||
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
content={children}
|
||||||
<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
|
||||||
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
path='/getting-started'
|
||||||
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
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
|
||||||
<WrappedRoute path='/favourites' component={FavouritedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
path='/notifications'
|
||||||
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
|
component={Notifications}
|
||||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
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
|
||||||
<WrappedRoute path='/directory' component={Directory} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
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
|
||||||
<WrappedRoute path='/statuses/:statusId' exact component={Status} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
path='/statuses/new'
|
||||||
<WrappedRoute path='/statuses/:statusId/reblogs' component={Reblogs} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
component={Compose}
|
||||||
<WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
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
|
||||||
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll, withReplies: true }} />
|
path='/accounts/:accountId'
|
||||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
exact
|
||||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
component={AccountTimeline}
|
||||||
<WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
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
|
||||||
<WrappedRoute path='/blocks' component={Blocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
path='/follow_requests'
|
||||||
<WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
component={FollowRequests}
|
||||||
<WrappedRoute path='/mutes' component={Mutes} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
content={children}
|
||||||
<WrappedRoute path='/lists' component={Lists} content={children} componentParams={{ shouldUpdateScroll: this.shouldUpdateScroll }} />
|
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} />
|
<WrappedRoute
|
||||||
</WrappedSwitch>
|
component={GenericNotFound}
|
||||||
</ColumnsAreaContainer>
|
content={children}
|
||||||
|
/>
|
||||||
|
</WrappedSwitch >
|
||||||
|
</ColumnsAreaContainer >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,15 +390,15 @@ class UI extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch : PropTypes.func.isRequired,
|
||||||
children: PropTypes.node,
|
children : PropTypes.node,
|
||||||
isComposing: PropTypes.bool,
|
isComposing : PropTypes.bool,
|
||||||
hasComposingText: PropTypes.bool,
|
hasComposingText : PropTypes.bool,
|
||||||
hasMediaAttachments: PropTypes.bool,
|
hasMediaAttachments: PropTypes.bool,
|
||||||
canUploadMore: PropTypes.bool,
|
canUploadMore : PropTypes.bool,
|
||||||
location: PropTypes.object,
|
location : PropTypes.object,
|
||||||
intl: PropTypes.object.isRequired,
|
intl : PropTypes.object.isRequired,
|
||||||
dropdownMenuIsOpen: PropTypes.bool,
|
dropdownMenuIsOpen : PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -259,20 +416,20 @@ class UI extends React.PureComponent {
|
|||||||
// but we set user-friendly message for other browsers, e.g. Edge.
|
// but we set user-friendly message for other browsers, e.g. Edge.
|
||||||
e.returnValue = intl.formatMessage(messages.beforeUnload);
|
e.returnValue = intl.formatMessage(messages.beforeUnload);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleWindowFocus = () => {
|
handleWindowFocus = () => {
|
||||||
this.props.dispatch(focusApp());
|
this.props.dispatch(focusApp());
|
||||||
}
|
};
|
||||||
|
|
||||||
handleWindowBlur = () => {
|
handleWindowBlur = () => {
|
||||||
this.props.dispatch(unfocusApp());
|
this.props.dispatch(unfocusApp());
|
||||||
}
|
};
|
||||||
|
|
||||||
handleLayoutChange = () => {
|
handleLayoutChange = () => {
|
||||||
// The cached heights are no longer accurate, invalidate
|
// The cached heights are no longer accurate, invalidate
|
||||||
this.props.dispatch(clearHeight());
|
this.props.dispatch(clearHeight());
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDragEnter = (e) => {
|
handleDragEnter = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -288,7 +445,7 @@ class UI extends React.PureComponent {
|
|||||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore) {
|
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore) {
|
||||||
this.setState({ draggingOver: true });
|
this.setState({ draggingOver: true });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDragOver = (e) => {
|
handleDragOver = (e) => {
|
||||||
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
||||||
@ -303,7 +460,7 @@ class UI extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDrop = (e) => {
|
handleDrop = (e) => {
|
||||||
if (this.dataTransferIsText(e.dataTransfer)) return;
|
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) {
|
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore) {
|
||||||
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleDragLeave = (e) => {
|
handleDragLeave = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -329,15 +486,15 @@ class UI extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ draggingOver: false });
|
this.setState({ draggingOver: false });
|
||||||
}
|
};
|
||||||
|
|
||||||
dataTransferIsText = (dataTransfer) => {
|
dataTransferIsText = (dataTransfer) => {
|
||||||
return (dataTransfer && Array.from(dataTransfer.types).filter((type) => type === 'text/plain').length === 1);
|
return (dataTransfer && Array.from(dataTransfer.types).filter((type) => type === 'text/plain').length === 1);
|
||||||
}
|
};
|
||||||
|
|
||||||
closeUploadModal = () => {
|
closeUploadModal = () => {
|
||||||
this.setState({ draggingOver: false });
|
this.setState({ draggingOver: false });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleServiceWorkerPostMessage = ({ data }) => {
|
handleServiceWorkerPostMessage = ({ data }) => {
|
||||||
if (data.type === 'navigate') {
|
if (data.type === 'navigate') {
|
||||||
@ -345,9 +502,9 @@ class UI extends React.PureComponent {
|
|||||||
} else {
|
} else {
|
||||||
console.warn('Unknown message type:', data.type);
|
console.warn('Unknown message type:', data.type);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount() {
|
||||||
window.addEventListener('focus', this.handleWindowFocus, false);
|
window.addEventListener('focus', this.handleWindowFocus, false);
|
||||||
window.addEventListener('blur', this.handleWindowBlur, false);
|
window.addEventListener('blur', this.handleWindowBlur, false);
|
||||||
window.addEventListener('beforeunload', this.handleBeforeUnload, false);
|
window.addEventListener('beforeunload', this.handleBeforeUnload, false);
|
||||||
@ -372,13 +529,13 @@ class UI extends React.PureComponent {
|
|||||||
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
|
setTimeout(() => this.props.dispatch(fetchFilters()), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
this.hotkeys.__mousetrap__.stopCallback = (e, element) => {
|
||||||
return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
|
return ['TEXTAREA', 'SELECT', 'INPUT'].includes(element.tagName);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('focus', this.handleWindowFocus);
|
window.removeEventListener('focus', this.handleWindowFocus);
|
||||||
window.removeEventListener('blur', this.handleWindowBlur);
|
window.removeEventListener('blur', this.handleWindowBlur);
|
||||||
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
||||||
@ -392,7 +549,7 @@ class UI extends React.PureComponent {
|
|||||||
|
|
||||||
setRef = c => {
|
setRef = c => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyNew = e => {
|
handleHotkeyNew = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -402,7 +559,7 @@ class UI extends React.PureComponent {
|
|||||||
if (element) {
|
if (element) {
|
||||||
element.focus();
|
element.focus();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeySearch = e => {
|
handleHotkeySearch = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -412,12 +569,12 @@ class UI extends React.PureComponent {
|
|||||||
if (element) {
|
if (element) {
|
||||||
element.focus();
|
element.focus();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyForceNew = e => {
|
handleHotkeyForceNew = e => {
|
||||||
this.handleHotkeyNew(e);
|
this.handleHotkeyNew(e);
|
||||||
this.props.dispatch(resetCompose());
|
this.props.dispatch(resetCompose());
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyFocusColumn = e => {
|
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
|
||||||
@ -435,7 +592,7 @@ class UI extends React.PureComponent {
|
|||||||
status.focus();
|
status.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyBack = () => {
|
handleHotkeyBack = () => {
|
||||||
if (window.history && window.history.length === 1) {
|
if (window.history && window.history.length === 1) {
|
||||||
@ -443,11 +600,11 @@ class UI extends React.PureComponent {
|
|||||||
} else {
|
} else {
|
||||||
this.context.router.history.goBack();
|
this.context.router.history.goBack();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
setHotkeysRef = c => {
|
setHotkeysRef = c => {
|
||||||
this.hotkeys = c;
|
this.hotkeys = c;
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyToggleHelp = () => {
|
handleHotkeyToggleHelp = () => {
|
||||||
if (this.props.location.pathname === '/keyboard-shortcuts') {
|
if (this.props.location.pathname === '/keyboard-shortcuts') {
|
||||||
@ -455,95 +612,111 @@ class UI extends React.PureComponent {
|
|||||||
} else {
|
} else {
|
||||||
this.context.router.history.push('/keyboard-shortcuts');
|
this.context.router.history.push('/keyboard-shortcuts');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToHome = () => {
|
handleHotkeyGoToHome = () => {
|
||||||
this.context.router.history.push('/timelines/home');
|
this.context.router.history.push('/timelines/home');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToNotifications = () => {
|
handleHotkeyGoToNotifications = () => {
|
||||||
this.context.router.history.push('/notifications');
|
this.context.router.history.push('/notifications');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToLocal = () => {
|
handleHotkeyGoToLocal = () => {
|
||||||
this.context.router.history.push('/timelines/public/local');
|
this.context.router.history.push('/timelines/public/local');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToFederated = () => {
|
handleHotkeyGoToFederated = () => {
|
||||||
this.context.router.history.push('/timelines/public');
|
this.context.router.history.push('/timelines/public');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToDirect = () => {
|
handleHotkeyGoToDirect = () => {
|
||||||
this.context.router.history.push('/timelines/direct');
|
this.context.router.history.push('/timelines/direct');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToStart = () => {
|
handleHotkeyGoToStart = () => {
|
||||||
this.context.router.history.push('/getting-started');
|
this.context.router.history.push('/getting-started');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToFavourites = () => {
|
handleHotkeyGoToFavourites = () => {
|
||||||
this.context.router.history.push('/favourites');
|
this.context.router.history.push('/favourites');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToPinned = () => {
|
handleHotkeyGoToPinned = () => {
|
||||||
this.context.router.history.push('/pinned');
|
this.context.router.history.push('/pinned');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToProfile = () => {
|
handleHotkeyGoToProfile = () => {
|
||||||
this.context.router.history.push(`/accounts/${me}`);
|
this.context.router.history.push(`/accounts/${me}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToBlocked = () => {
|
handleHotkeyGoToBlocked = () => {
|
||||||
this.context.router.history.push('/blocks');
|
this.context.router.history.push('/blocks');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToMuted = () => {
|
handleHotkeyGoToMuted = () => {
|
||||||
this.context.router.history.push('/mutes');
|
this.context.router.history.push('/mutes');
|
||||||
}
|
};
|
||||||
|
|
||||||
handleHotkeyGoToRequests = () => {
|
handleHotkeyGoToRequests = () => {
|
||||||
this.context.router.history.push('/follow_requests');
|
this.context.router.history.push('/follow_requests');
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { draggingOver } = this.state;
|
const { draggingOver } = this.state;
|
||||||
const { children, isComposing, location, dropdownMenuIsOpen } = this.props;
|
const { children, isComposing, location, dropdownMenuIsOpen } = this.props;
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
help: this.handleHotkeyToggleHelp,
|
help : this.handleHotkeyToggleHelp,
|
||||||
new: this.handleHotkeyNew,
|
new : this.handleHotkeyNew,
|
||||||
search: this.handleHotkeySearch,
|
search : this.handleHotkeySearch,
|
||||||
forceNew: this.handleHotkeyForceNew,
|
forceNew : this.handleHotkeyForceNew,
|
||||||
focusColumn: this.handleHotkeyFocusColumn,
|
focusColumn : this.handleHotkeyFocusColumn,
|
||||||
back: this.handleHotkeyBack,
|
back : this.handleHotkeyBack,
|
||||||
goToHome: this.handleHotkeyGoToHome,
|
goToHome : this.handleHotkeyGoToHome,
|
||||||
goToNotifications: this.handleHotkeyGoToNotifications,
|
goToNotifications: this.handleHotkeyGoToNotifications,
|
||||||
goToLocal: this.handleHotkeyGoToLocal,
|
goToLocal : this.handleHotkeyGoToLocal,
|
||||||
goToFederated: this.handleHotkeyGoToFederated,
|
goToFederated : this.handleHotkeyGoToFederated,
|
||||||
goToDirect: this.handleHotkeyGoToDirect,
|
goToDirect : this.handleHotkeyGoToDirect,
|
||||||
goToStart: this.handleHotkeyGoToStart,
|
goToStart : this.handleHotkeyGoToStart,
|
||||||
goToFavourites: this.handleHotkeyGoToFavourites,
|
goToFavourites : this.handleHotkeyGoToFavourites,
|
||||||
goToPinned: this.handleHotkeyGoToPinned,
|
goToPinned : this.handleHotkeyGoToPinned,
|
||||||
goToProfile: this.handleHotkeyGoToProfile,
|
goToProfile : this.handleHotkeyGoToProfile,
|
||||||
goToBlocked: this.handleHotkeyGoToBlocked,
|
goToBlocked : this.handleHotkeyGoToBlocked,
|
||||||
goToMuted: this.handleHotkeyGoToMuted,
|
goToMuted : this.handleHotkeyGoToMuted,
|
||||||
goToRequests: this.handleHotkeyGoToRequests,
|
goToRequests : this.handleHotkeyGoToRequests,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
<HotKeys
|
||||||
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
|
keyMap={keyMap}
|
||||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange}>
|
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}
|
{children}
|
||||||
</SwitchingColumnsArea>
|
</SwitchingColumnsArea >
|
||||||
|
|
||||||
<NotificationsContainer />
|
<NotificationsContainer />
|
||||||
<LoadingBarContainer className='loading-bar' />
|
<LoadingBarContainer className='loading-bar' />
|
||||||
<ModalContainer />
|
<ModalContainer />
|
||||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
<UploadArea
|
||||||
|
active={draggingOver}
|
||||||
|
onClose={this.closeUploadModal}
|
||||||
|
/>
|
||||||
<DocumentTitle />
|
<DocumentTitle />
|
||||||
</div>
|
</div >
|
||||||
</HotKeys>
|
</HotKeys >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
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 ColumnLoading from '../components/column_loading';
|
||||||
import BundleColumnError from '../components/bundle_column_error';
|
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
|
// Small wrapper to pass multiColumn to the route components
|
||||||
export class WrappedSwitch extends React.PureComponent {
|
export class WrappedSwitch extends React.PureComponent {
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { multiColumn, children } = this.props;
|
const { multiColumn, children } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch >
|
||||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
|
{React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
|
||||||
</Switch>
|
</Switch >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export class WrappedSwitch extends React.PureComponent {
|
|||||||
|
|
||||||
WrappedSwitch.propTypes = {
|
WrappedSwitch.propTypes = {
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
children: PropTypes.node,
|
children : PropTypes.node,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Small Wrapper to extract the params from the route and pass
|
// Small Wrapper to extract the params from the route and pass
|
||||||
@ -32,9 +32,9 @@ WrappedSwitch.propTypes = {
|
|||||||
export class WrappedRoute extends React.Component {
|
export class WrappedRoute extends React.Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
component: PropTypes.func.isRequired,
|
component : PropTypes.func.isRequired,
|
||||||
content: PropTypes.node,
|
content : PropTypes.node,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn : PropTypes.bool,
|
||||||
componentParams: PropTypes.object,
|
componentParams: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,21 +46,28 @@ export class WrappedRoute extends React.Component {
|
|||||||
const { component, content, multiColumn, componentParams } = this.props;
|
const { component, content, multiColumn, componentParams } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}>
|
<BundleContainer
|
||||||
{Component => <Component params={match.params} multiColumn={multiColumn} {...componentParams}>{content}</Component>}
|
fetchComponent={component}
|
||||||
</BundleContainer>
|
loading={this.renderLoading}
|
||||||
|
error={this.renderError}
|
||||||
|
>
|
||||||
|
{Component => (<Component
|
||||||
|
params={match.params}
|
||||||
|
multiColumn={multiColumn} {...componentParams}
|
||||||
|
>{content}</Component >)}
|
||||||
|
</BundleContainer >
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
renderLoading = () => {
|
renderLoading = () => {
|
||||||
return <ColumnLoading />;
|
return <ColumnLoading />;
|
||||||
}
|
};
|
||||||
|
|
||||||
renderError = (props) => {
|
renderError = (props) => {
|
||||||
return <BundleColumnError {...props} />;
|
return <BundleColumnError {...props} />;
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { component: Component, content, ...rest } = this.props;
|
const { component: Component, content, ...rest } = this.props;
|
||||||
|
|
||||||
return <Route {...rest} render={this.renderComponent} />;
|
return <Route {...rest} render={this.renderComponent} />;
|
||||||
|
@ -15,9 +15,9 @@ class ReducedMotion extends React.Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
defaultStyle: PropTypes.object,
|
defaultStyle: PropTypes.object,
|
||||||
style: PropTypes.object,
|
style : PropTypes.object,
|
||||||
children: PropTypes.func,
|
children : PropTypes.func,
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
@ -33,9 +33,12 @@ class ReducedMotion extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Motion style={style} defaultStyle={defaultStyle}>
|
<Motion
|
||||||
|
style={style}
|
||||||
|
defaultStyle={defaultStyle}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</Motion>
|
</Motion >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,5 +25,5 @@ export const usePendingItems = getMeta('use_pending_items');
|
|||||||
export const showTrends = getMeta('trends');
|
export const showTrends = getMeta('trends');
|
||||||
export const title = getMeta('title');
|
export const title = getMeta('title');
|
||||||
export const cropImages = getMeta('crop_images');
|
export const cropImages = getMeta('crop_images');
|
||||||
|
console.log('initialState', initialState);
|
||||||
export default initialState;
|
export default initialState;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"account.block_domain": "Tout masquer venant de {domain}",
|
"account.block_domain": "Tout masquer venant de {domain}",
|
||||||
"account.blocked": "Bloqué·e",
|
"account.blocked": "Bloqué·e",
|
||||||
"account.cancel_follow_request": "Annuler la demande de suivi",
|
"account.cancel_follow_request": "Annuler la demande de suivi",
|
||||||
"account.direct": "Envoyer un message direct à @{name}",
|
"account.direct": "Envoyer un message privé à @{name}",
|
||||||
"account.domain_blocked": "Domaine caché",
|
"account.domain_blocked": "Domaine caché",
|
||||||
"account.edit_profile": "Modifier le profil",
|
"account.edit_profile": "Modifier le profil",
|
||||||
"account.endorse": "Recommander sur le profil",
|
"account.endorse": "Recommander sur le profil",
|
||||||
@ -52,6 +52,7 @@
|
|||||||
"bundle_modal_error.retry": "Réessayer",
|
"bundle_modal_error.retry": "Réessayer",
|
||||||
"column.bookmarks": "Marque pages",
|
"column.bookmarks": "Marque pages",
|
||||||
"column.blocks": "Comptes bloqués",
|
"column.blocks": "Comptes bloqués",
|
||||||
|
"column.bookmarks": "Bookmarks",
|
||||||
"column.community": "Fil public local",
|
"column.community": "Fil public local",
|
||||||
"column.direct": "Messages privés",
|
"column.direct": "Messages privés",
|
||||||
"column.directory": "Parcourir les profils",
|
"column.directory": "Parcourir les profils",
|
||||||
@ -139,8 +140,9 @@
|
|||||||
"empty_column.account_timeline": "Aucun pouet ici !",
|
"empty_column.account_timeline": "Aucun pouet ici !",
|
||||||
"empty_column.account_unavailable": "Profil non disponible",
|
"empty_column.account_unavailable": "Profil non disponible",
|
||||||
"empty_column.blocks": "Vous n’avez bloqué aucun·e utilisateur·rice pour le moment.",
|
"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.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.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.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.",
|
"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.documentation": "Documentation",
|
||||||
"getting_started.heading": "Pour commencer",
|
"getting_started.heading": "Pour commencer",
|
||||||
"getting_started.invite": "Inviter des gens",
|
"getting_started.invite": "Inviter des gens",
|
||||||
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer ou faire des rapports de bogues via {github} sur GitHub.",
|
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer ou faire des rapports de bogues via {forge} sur GitHub.",
|
||||||
"getting_started.security": "Sécurité",
|
"getting_started.security": "Sécurité",
|
||||||
"getting_started.terms": "Conditions d’utilisation",
|
"getting_started.terms": "Conditions d’utilisation",
|
||||||
"hashtag.column_header.tag_mode.all": "et {additional}",
|
"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.column": "pour focaliser un statut dans l’une des colonnes",
|
||||||
"keyboard_shortcuts.compose": "pour focaliser la zone de rédaction",
|
"keyboard_shortcuts.compose": "pour focaliser la zone de rédaction",
|
||||||
"keyboard_shortcuts.description": "Description",
|
"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.down": "pour descendre dans la liste",
|
||||||
"keyboard_shortcuts.enter": "pour ouvrir le statut",
|
"keyboard_shortcuts.enter": "pour ouvrir le statut",
|
||||||
"keyboard_shortcuts.favourite": "pour ajouter aux favoris",
|
"keyboard_shortcuts.favourite": "pour ajouter aux favoris",
|
||||||
@ -253,9 +255,10 @@
|
|||||||
"mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
|
"mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
|
||||||
"navigation_bar.apps": "Applications mobiles",
|
"navigation_bar.apps": "Applications mobiles",
|
||||||
"navigation_bar.blocks": "Comptes bloqués",
|
"navigation_bar.blocks": "Comptes bloqués",
|
||||||
|
"navigation_bar.bookmarks": "Bookmarks",
|
||||||
"navigation_bar.community_timeline": "Fil public local",
|
"navigation_bar.community_timeline": "Fil public local",
|
||||||
"navigation_bar.compose": "Rédiger un nouveau pouet",
|
"navigation_bar.compose": "Rédiger un nouveau pouet",
|
||||||
"navigation_bar.direct": "Messages directs",
|
"navigation_bar.direct": "Messages privés",
|
||||||
"navigation_bar.discover": "Découvrir",
|
"navigation_bar.discover": "Découvrir",
|
||||||
"navigation_bar.domain_blocks": "Domaines cachés",
|
"navigation_bar.domain_blocks": "Domaines cachés",
|
||||||
"navigation_bar.edit_profile": "Modifier le profil",
|
"navigation_bar.edit_profile": "Modifier le profil",
|
||||||
@ -352,7 +355,7 @@
|
|||||||
"status.copy": "Copier le lien vers le pouet",
|
"status.copy": "Copier le lien vers le pouet",
|
||||||
"status.delete": "Effacer",
|
"status.delete": "Effacer",
|
||||||
"status.detailed_status": "Vue détaillée de la conversation",
|
"status.detailed_status": "Vue détaillée de la conversation",
|
||||||
"status.direct": "Envoyer un message direct à @{name}",
|
"status.direct": "Envoyer un message privé à @{name}",
|
||||||
"status.embed": "Intégrer",
|
"status.embed": "Intégrer",
|
||||||
"status.favourite": "Ajouter aux favoris",
|
"status.favourite": "Ajouter aux favoris",
|
||||||
"status.filtered": "Filtré",
|
"status.filtered": "Filtré",
|
||||||
@ -371,6 +374,7 @@
|
|||||||
"status.reblogged_by": "{name} a partagé :",
|
"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.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.redraft": "Effacer et ré-écrire",
|
||||||
|
"status.remove_bookmark": "Enlever le marque-page",
|
||||||
"status.reply": "Répondre",
|
"status.reply": "Répondre",
|
||||||
"status.replyAll": "Répondre au fil",
|
"status.replyAll": "Répondre au fil",
|
||||||
"status.report": "Signaler @{name}",
|
"status.report": "Signaler @{name}",
|
||||||
@ -386,9 +390,9 @@
|
|||||||
"status.unpin": "Retirer du profil",
|
"status.unpin": "Retirer du profil",
|
||||||
"suggestions.dismiss": "Rejeter la suggestion",
|
"suggestions.dismiss": "Rejeter la suggestion",
|
||||||
"suggestions.header": "Vous pourriez être intéressé·e par…",
|
"suggestions.header": "Vous pourriez être intéressé·e par…",
|
||||||
"tabs_bar.federated_timeline": "Fil public global",
|
"tabs_bar.federated_timeline": "Global",
|
||||||
"tabs_bar.home": "Accueil",
|
"tabs_bar.home": "Accueil",
|
||||||
"tabs_bar.local_timeline": "Fil public local",
|
"tabs_bar.local_timeline": "Local",
|
||||||
"tabs_bar.notifications": "Notifications",
|
"tabs_bar.notifications": "Notifications",
|
||||||
"tabs_bar.search": "Chercher",
|
"tabs_bar.search": "Chercher",
|
||||||
"time_remaining.days": "{number, plural, one {# day} other {# days}} restants",
|
"time_remaining.days": "{number, plural, one {# day} other {# days}} restants",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ACCOUNT_IMPORT, ACCOUNTS_IMPORT } from '../actions/importer';
|
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();
|
const initialState = ImmutableMap();
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ const normalizeAccounts = (state, accounts) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function accounts(state = initialState, action) {
|
export default function accounts(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch (action.type) {
|
||||||
case ACCOUNT_IMPORT:
|
case ACCOUNT_IMPORT:
|
||||||
return normalizeAccount(state, action.account);
|
return normalizeAccount(state, action.account);
|
||||||
case ACCOUNTS_IMPORT:
|
case ACCOUNTS_IMPORT:
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
REBLOG_REQUEST,
|
|
||||||
REBLOG_FAIL,
|
|
||||||
FAVOURITE_REQUEST,
|
|
||||||
FAVOURITE_FAIL,
|
|
||||||
UNFAVOURITE_SUCCESS,
|
|
||||||
BOOKMARK_REQUEST,
|
|
||||||
BOOKMARK_FAIL,
|
BOOKMARK_FAIL,
|
||||||
|
BOOKMARK_REQUEST,
|
||||||
|
FAVOURITE_FAIL,
|
||||||
|
FAVOURITE_REQUEST,
|
||||||
|
REBLOG_FAIL,
|
||||||
|
REBLOG_REQUEST,
|
||||||
|
UNFAVOURITE_SUCCESS,
|
||||||
} from '../actions/interactions';
|
} from '../actions/interactions';
|
||||||
import {
|
import { STATUS_HIDE, STATUS_MUTE_SUCCESS, STATUS_REVEAL, STATUS_UNMUTE_SUCCESS } from '../actions/statuses';
|
||||||
STATUS_MUTE_SUCCESS,
|
|
||||||
STATUS_UNMUTE_SUCCESS,
|
|
||||||
STATUS_REVEAL,
|
|
||||||
STATUS_HIDE,
|
|
||||||
} from '../actions/statuses';
|
|
||||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||||
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
||||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
import { fromJS, Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||||
|
|
||||||
@ -33,7 +28,7 @@ const deleteStatus = (state, id, references) => {
|
|||||||
const initialState = ImmutableMap();
|
const initialState = ImmutableMap();
|
||||||
|
|
||||||
export default function statuses(state = initialState, action) {
|
export default function statuses(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch (action.type) {
|
||||||
case STATUS_IMPORT:
|
case STATUS_IMPORT:
|
||||||
return importStatus(state, action.status);
|
return importStatus(state, action.status);
|
||||||
case STATUSES_IMPORT:
|
case STATUSES_IMPORT:
|
||||||
|
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));
|
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(() => {
|
ready(() => {
|
||||||
const input = document.getElementById('domain_block_severity');
|
const domainBlockSeverityInput = document.getElementById('domain_block_severity');
|
||||||
if (input) onDomainBlockSeverityChange(input);
|
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/variables';
|
||||||
@import 'bliss/mixins';
|
@import 'bliss/mixins';
|
||||||
@import 'bliss/variables';
|
@import 'bliss/variables';
|
||||||
|
@ -72,3 +72,13 @@
|
|||||||
color: $inverted-text-color;
|
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 {
|
.container {
|
||||||
box-sizing: border-box;
|
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 {
|
.header-wrapper {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
||||||
@ -649,7 +645,7 @@ $small-breakpoint: 960px;
|
|||||||
.landing {
|
.landing {
|
||||||
margin-bottom: 100px;
|
margin-bottom: 100px;
|
||||||
|
|
||||||
@media screen and (max-width: 738px) {
|
@media screen and (max-width: $xs-top-breakpoint) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,9 +711,9 @@ $small-breakpoint: 960px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.account__avatar {
|
.account__avatar {
|
||||||
width: 44px;
|
width: $avatar-side;
|
||||||
height: 44px;
|
height: $avatar-side;
|
||||||
background-size: 44px 44px;
|
background-size: $avatar-side $avatar-side;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,13 +748,13 @@ $small-breakpoint: 960px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__grid {
|
&__grid {
|
||||||
max-width: 960px;
|
max-width: $small-breakpoint;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
|
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
|
||||||
grid-gap: 30px;
|
grid-gap: 30px;
|
||||||
|
|
||||||
@media screen and (max-width: 738px) {
|
@media screen and (max-width: $xs-top-breakpoint) {
|
||||||
grid-template-columns: minmax(0, 100%);
|
grid-template-columns: minmax(0, 100%);
|
||||||
grid-gap: 10px;
|
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%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr,
|
||||||
|
.select select,
|
||||||
|
.textarea,
|
||||||
|
.input {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.batch-form-box {
|
.batch-form-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -160,3 +160,35 @@ button {
|
|||||||
height: 100%;
|
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,
|
.autosuggest-textarea__textarea,
|
||||||
.spoiler-input__input {
|
.spoiler-input__input {
|
||||||
display: block;
|
display: block;
|
||||||
@ -2193,27 +2197,16 @@ a.account__display-name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
&__message {
|
background-color: $classic-base-color !important;
|
||||||
margin-left: 48px + 15px * 2;
|
padding: 1em 0;
|
||||||
padding-top: 15px;
|
color: $ui-highlight-color;
|
||||||
}
|
border-left: $ui-highlight-color 5px solid;
|
||||||
|
|
||||||
&__favourite-icon-wrapper {
|
|
||||||
left: -32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.account {
|
.account {
|
||||||
|
padding: 0;
|
||||||
padding-top: 8px;
|
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 {
|
.columns-area__panels__pane--compositional {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 600px + (285px * 1) + (10px * 1)) {
|
@media screen and (min-width: 600px + (285px * 1) + (10px * 1)) {
|
||||||
@ -2315,6 +2309,7 @@ a.account__display-name {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: calc(100% - 10px);
|
height: calc(100% - 10px);
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
.navigation-bar {
|
.navigation-bar {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
@ -2659,6 +2654,28 @@ a.account__display-name {
|
|||||||
padding: 15px;
|
padding: 15px;
|
||||||
text-decoration: none;
|
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,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
&: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 {
|
ul {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
@ -150,4 +166,36 @@
|
|||||||
i {
|
i {
|
||||||
margin: 0.5ch;
|
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;
|
$no-columns-breakpoint: 600px;
|
||||||
table{
|
table {
|
||||||
thead{
|
thead {
|
||||||
|
|
||||||
th{
|
th {
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
background: $ui-highlight-color;
|
background: $ui-highlight-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
td, th{
|
|
||||||
|
td, th {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
a{
|
|
||||||
|
a {
|
||||||
@extend .text-btn
|
@extend .text-btn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.table-responsive{
|
|
||||||
|
.table-responsive {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.table-striped{
|
|
||||||
|
.table-striped {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
|
|
||||||
tr{
|
tr {
|
||||||
|
|
||||||
&:odd{
|
&:odd {
|
||||||
background: $ui-base-lighter-color;
|
background: $ui-base-lighter-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.group-form{
|
|
||||||
|
.group-form {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: $font-monospace, monospace;
|
font-family: $font-monospace, monospace;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@ -114,6 +120,7 @@ code {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
|
background: $ui-highlight-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -957,3 +964,8 @@ code {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compose-form__publish-button-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
@ -1,84 +1 @@
|
|||||||
$messagingBoxWidth: 15em;
|
@import "../mastodon/messaging/main";
|
||||||
$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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// Commonly used web colors
|
// Commonly used web colors
|
||||||
$black: #000000; // Black
|
$black: #111; // Black
|
||||||
$white: #ffffff; // White
|
$white: #fff; // White
|
||||||
$success-green: #79bd9a !default; // Padua
|
$success-green: #6bbd77 !default; // Padua
|
||||||
$error-red: #df405a !default; // Cerise
|
$error-red: #d4839b !default; // Cerise
|
||||||
$warning-red: #ff5050 !default; // Sunset Orange
|
$warning-red: #528dc8 !default; // Sunset Orange
|
||||||
$gold-star: #ca8f04 !default; // Dark Goldenrod
|
$gold-star: #98c6ff !default; // Dark Goldenrod
|
||||||
|
|
||||||
// Values from the classic Mastodon UI
|
// Values from the classic Mastodon UI
|
||||||
$classic-base-color: #282c37; // Midnight Express
|
$classic-base-color: #282c37; // Midnight Express
|
||||||
@ -48,7 +48,15 @@ $media-modal-media-max-width: 100%;
|
|||||||
$media-modal-media-max-height: 80%;
|
$media-modal-media-max-height: 80%;
|
||||||
|
|
||||||
$no-gap-breakpoint: 415px;
|
$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-sans-serif: 'mastodon-font-sans-serif' !default;
|
||||||
$font-display: 'mastodon-font-display' !default;
|
$font-display: 'mastodon-font-display' !default;
|
||||||
$font-monospace: 'mastodon-font-monospace' !default;
|
$font-monospace: 'mastodon-font-monospace' !default;
|
||||||
|
@ -95,9 +95,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.account__avatar {
|
.account__avatar {
|
||||||
width: 44px;
|
width: $avatar-side;
|
||||||
height: 44px;
|
height: $avatar-side;
|
||||||
background-size: 44px 44px;
|
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;
|
$inverted-text-color: $black !default;
|
||||||
$lighter-text-color: darken($ui-base-color, 6%) !default;
|
$lighter-text-color: darken($ui-base-color, 6%) !default;
|
||||||
$light-text-color: darken($ui-primary-color, 40%) !default;
|
$light-text-color: darken($ui-primary-color, 40%) !default;
|
||||||
|
$avatar-side: 55px;
|
||||||
|
@ -1,30 +1,26 @@
|
|||||||
@import 'large_center';
|
//@import 'large_center';
|
||||||
// custom sheet made on https://www.cipherbliss.com
|
//// custom sheet made on https://www.cipherbliss.com
|
||||||
// Commonly used web colors
|
//// Commonly used web colors
|
||||||
$black: #fff; // Black
|
//$black: #fff; // Black
|
||||||
$white: #000; // White
|
//$white: #000; // White
|
||||||
$success-green: #6bbd77 !default; // Padua
|
//$success-green: #6bbd77 !default; // Padua
|
||||||
$error-red: #d4839b !default; // Cerise
|
//$error-red: #d4839b !default; // Cerise
|
||||||
$warning-red: #528dc8 !default; // Sunset Orange
|
//$warning-red: #528dc8 !default; // Sunset Orange
|
||||||
$gold-star: #00ec84 !default; // Dark Goldenrod
|
//$gold-star: #00ec84 !default; // Dark Goldenrod
|
||||||
|
//
|
||||||
// User Interface Colors
|
//// User Interface Colors
|
||||||
$ui-base-color: #313644; // Midnight Express
|
//$ui-base-color: #313644; // Midnight Express
|
||||||
$ui-base-lighter-color: #bdd2d6;
|
//$ui-base-lighter-color: #bdd2d6;
|
||||||
$ui-primary-color: #a1ccff; // Echo Blue
|
//$ui-primary-color: #a1ccff; // Echo Blue
|
||||||
$ui-secondary-color: #7fc0ff; // Pattens Blue
|
//$ui-secondary-color: #7fc0ff; // Pattens Blue
|
||||||
$ui-highlight-color: #00a7d1; // Summer Sky
|
//$ui-highlight-color: #00a7d1; // Summer Sky
|
||||||
|
//$avatar-side: 55px;
|
||||||
// Variables for components
|
//// Variables for components
|
||||||
$media-modal-media-max-width: 100%;
|
//$media-modal-media-max-width: 100%;
|
||||||
// put margins on top and bottom of image to avoid the screen covered by image.
|
//// put margins on top and bottom of image to avoid the screen covered by image.
|
||||||
$media-modal-media-max-height: 80%;
|
//$media-modal-media-max-height: 80%;
|
||||||
// fix
|
//// fix
|
||||||
|
//
|
||||||
.media-gallery {
|
//// then we import the rest of the world
|
||||||
margin-left: 0;
|
//@import 'application';
|
||||||
}
|
//@import 'bliss/messaging';
|
||||||
|
|
||||||
// then we import the rest of the world
|
|
||||||
@import 'application';
|
|
||||||
@import 'bliss/messaging';
|
|
||||||
|
@ -751,9 +751,9 @@ $small-breakpoint: 960px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.account__avatar {
|
.account__avatar {
|
||||||
width: 44px;
|
width: $avatar-side;
|
||||||
height: 44px;
|
height: $avatar-side;
|
||||||
background-size: 44px 44px;
|
background-size: $avatar-side $avatar-side;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,18 +181,39 @@ $content-width: 840px;
|
|||||||
padding-top: 30px;
|
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 {
|
h2 {
|
||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
font-weight: 400;
|
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) {
|
@media screen and (max-width: $no-columns-breakpoint) {
|
||||||
border-bottom: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,3 +229,7 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.padded {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
@ -949,8 +949,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes fade {
|
@keyframes fade {
|
||||||
0% { opacity: 0; }
|
0% {
|
||||||
100% { opacity: 1; }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -2175,7 +2179,7 @@ a.account__display-name {
|
|||||||
.scrollable {
|
.scrollable {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
||||||
@supports(display: grid) {
|
@supports (display: grid) {
|
||||||
contain: content;
|
contain: content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2333,6 +2337,9 @@ a.account__display-name {
|
|||||||
.columns-area__panels__pane--navigational {
|
.columns-area__panels__pane--navigational {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.getting-started__footer {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 600px + (285px * 2) + (10px * 2)) {
|
@media screen and (min-width: 600px + (285px * 2) + (10px * 2)) {
|
||||||
@ -2512,13 +2519,12 @@ a.account__display-name {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
will-change: transform; // improves perf in mobile Chrome
|
|
||||||
|
|
||||||
&.optionally-scrollable {
|
&.optionally-scrollable {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@supports(display: grid) { // hack to fix Chrome <57
|
@supports (display: grid) { // hack to fix Chrome <57
|
||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2535,7 +2541,7 @@ a.account__display-name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scrollable.fullscreen {
|
.scrollable.fullscreen {
|
||||||
@supports(display: grid) { // hack to fix Chrome <57
|
@supports (display: grid) { // hack to fix Chrome <57
|
||||||
contain: none;
|
contain: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3465,9 +3471,15 @@ a.status-card.compact:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loader-label {
|
@keyframes loader-label {
|
||||||
0% { opacity: 0.25; }
|
0% {
|
||||||
30% { opacity: 1; }
|
opacity: 0.25;
|
||||||
100% { opacity: 0.25; }
|
}
|
||||||
|
30% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-error-cover {
|
.video-error-cover {
|
||||||
@ -3756,7 +3768,7 @@ a.status-card.compact:hover {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
@supports(display: grid) { // hack to fix Chrome <57
|
@supports (display: grid) { // hack to fix Chrome <57
|
||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5161,6 +5173,7 @@ a.status-card.compact:hover {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End Media Gallery */
|
/* End Media Gallery */
|
||||||
|
|
||||||
.detailed,
|
.detailed,
|
||||||
@ -5832,9 +5845,15 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes flicker {
|
@keyframes flicker {
|
||||||
0% { opacity: 1; }
|
0% {
|
||||||
30% { opacity: 0.75; }
|
opacity: 1;
|
||||||
100% { opacity: 1; }
|
}
|
||||||
|
30% {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 630px) and (max-height: 400px) {
|
@media screen and (max-width: 630px) and (max-height: 400px) {
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
svg {
|
> svg {
|
||||||
fill: $primary-text-color;
|
fill: $primary-text-color;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
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;
|
||||||
|
}
|
@ -76,3 +76,5 @@ $no-gap-breakpoint: 415px;
|
|||||||
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
||||||
$font-display: 'mastodon-font-display' !default;
|
$font-display: 'mastodon-font-display' !default;
|
||||||
$font-monospace: 'mastodon-font-monospace' !default;
|
$font-monospace: 'mastodon-font-monospace' !default;
|
||||||
|
|
||||||
|
$avatar-side: 55px;
|
||||||
|
@ -372,8 +372,8 @@
|
|||||||
|
|
||||||
.account__avatar {
|
.account__avatar {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: 36px;
|
width: $avatar-side;
|
||||||
height: 36px;
|
height: $avatar-side;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
|
@ -5,7 +5,7 @@ class ActivityPub::Activity
|
|||||||
include Redisable
|
include Redisable
|
||||||
|
|
||||||
SUPPORTED_TYPES = %w(Note Question).freeze
|
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)
|
def initialize(json, account, **options)
|
||||||
@json = json
|
@json = json
|
||||||
|
@ -157,7 +157,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||||||
return if tag['name'].blank?
|
return if tag['name'].blank?
|
||||||
|
|
||||||
Tag.find_or_create_by_names(tag['name']) do |hashtag|
|
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
|
end
|
||||||
rescue ActiveRecord::RecordInvalid
|
rescue ActiveRecord::RecordInvalid
|
||||||
nil
|
nil
|
||||||
@ -167,7 +167,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||||||
return if tag['href'].blank?
|
return if tag['href'].blank?
|
||||||
|
|
||||||
account = account_from_uri(tag['href'])
|
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?
|
return if account.nil?
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
# user_id :bigint(8)
|
# user_id :bigint(8)
|
||||||
# dump_file_name :string
|
# dump_file_name :string
|
||||||
# dump_content_type :string
|
# dump_content_type :string
|
||||||
# dump_file_size :integer
|
# dump_file_size :bigint
|
||||||
# dump_updated_at :datetime
|
# dump_updated_at :datetime
|
||||||
# processed :boolean default(FALSE), not null
|
# processed :boolean default(FALSE), not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
|
@ -16,6 +16,7 @@ class Form::AdminSettings
|
|||||||
open_deletion
|
open_deletion
|
||||||
timeline_preview
|
timeline_preview
|
||||||
show_staff_badge
|
show_staff_badge
|
||||||
|
enable_bootstrap_timeline_accounts
|
||||||
bootstrap_timeline_accounts
|
bootstrap_timeline_accounts
|
||||||
theme
|
theme
|
||||||
min_invite_role
|
min_invite_role
|
||||||
@ -40,6 +41,7 @@ class Form::AdminSettings
|
|||||||
open_deletion
|
open_deletion
|
||||||
timeline_preview
|
timeline_preview
|
||||||
show_staff_badge
|
show_staff_badge
|
||||||
|
enable_bootstrap_timeline_accounts
|
||||||
activity_api_enabled
|
activity_api_enabled
|
||||||
peers_api_enabled
|
peers_api_enabled
|
||||||
show_known_fediverse_at_about_page
|
show_known_fediverse_at_about_page
|
||||||
|
@ -40,7 +40,7 @@ class Form::CustomEmojiBatch
|
|||||||
if category_id.present?
|
if category_id.present?
|
||||||
CustomEmojiCategory.find(category_id)
|
CustomEmojiCategory.find(category_id)
|
||||||
elsif category_name.present?
|
elsif category_name.present?
|
||||||
CustomEmojiCategory.create!(name: category_name)
|
CustomEmojiCategory.find_or_create_by!(name: category_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ class Tag < ApplicationRecord
|
|||||||
class << self
|
class << self
|
||||||
def find_or_create_by_names(name_or_names)
|
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|
|
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?
|
yield tag if block_given?
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
require 'rubygems/package'
|
require 'rubygems/package'
|
||||||
|
|
||||||
class BackupService < BaseService
|
class BackupService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
attr_reader :account, :backup, :collection
|
attr_reader :account, :backup, :collection
|
||||||
|
|
||||||
def call(backup)
|
def call(backup)
|
||||||
@ -20,7 +22,7 @@ class BackupService < BaseService
|
|||||||
|
|
||||||
account.statuses.with_includes.reorder(nil).find_in_batches do |statuses|
|
account.statuses.with_includes.reorder(nil).find_in_batches do |statuses|
|
||||||
statuses.each do |status|
|
statuses.each do |status|
|
||||||
item = serialize(status, ActivityPub::ActivitySerializer)
|
item = serialize_payload(status, ActivityPub::ActivitySerializer, signer: @account)
|
||||||
item.delete(:'@context')
|
item.delete(:'@context')
|
||||||
|
|
||||||
unless item[:type] == 'Announce' || item[:object][:attachment].blank?
|
unless item[:type] == 'Announce' || item[:object][:attachment].blank?
|
||||||
|
@ -5,7 +5,7 @@ class BootstrapTimelineService < BaseService
|
|||||||
@source_account = source_account
|
@source_account = source_account
|
||||||
|
|
||||||
autofollow_inviter!
|
autofollow_inviter!
|
||||||
autofollow_bootstrap_timeline_accounts!
|
autofollow_bootstrap_timeline_accounts! if Setting.enable_bootstrap_timeline_accounts
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -45,7 +45,7 @@ class FetchLinkCardService < BaseService
|
|||||||
def html
|
def html
|
||||||
return @html if defined?(@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'
|
if res.code == 200 && res.mime_type == 'text/html'
|
||||||
@html = res.body_with_limit
|
@html = res.body_with_limit
|
||||||
@html_charset = res.charset
|
@html_charset = res.charset
|
||||||
|
@ -93,7 +93,7 @@ class FetchOEmbedService
|
|||||||
def html
|
def html
|
||||||
return @html if defined?(@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
|
res.code != 200 || res.mime_type != 'text/html' ? nil : res.body_with_limit
|
||||||
end
|
end
|
||||||
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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class FetchRemoteStatusService < BaseService
|
class FetchRemoteStatusService < BaseService
|
||||||
def call(url, prefetched_body = nil, protocol = :ostatus)
|
def call(url, prefetched_body = nil)
|
||||||
if 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
|
else
|
||||||
resource_url = url
|
resource_url = url
|
||||||
resource_options = { prefetched_body: prefetched_body }
|
resource_options = { prefetched_body: prefetched_body }
|
||||||
end
|
end
|
||||||
|
|
||||||
case protocol
|
ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options) unless resource_url.nil?
|
||||||
when :activitypub
|
|
||||||
ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -33,7 +33,7 @@ class FetchResourceService < BaseService
|
|||||||
body = response.body_with_limit
|
body = response.body_with_limit
|
||||||
json = body_to_json(body)
|
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
|
elsif !terminal
|
||||||
link_header = response['Link'] && parse_link_header(response)
|
link_header = response['Link'] && parse_link_header(response)
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ class ResolveURLService < BaseService
|
|||||||
|
|
||||||
def process_url
|
def process_url
|
||||||
if equals_or_includes_any?(type, ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES)
|
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)
|
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?
|
authorize_with @on_behalf_of, status, :show? unless status.nil?
|
||||||
status
|
status
|
||||||
elsif fetched_resource.nil? && @on_behalf_of.present?
|
elsif fetched_resource.nil? && @on_behalf_of.present?
|
||||||
@ -45,12 +45,8 @@ class ResolveURLService < BaseService
|
|||||||
fetched_resource.second[:prefetched_body]
|
fetched_resource.second[:prefetched_body]
|
||||||
end
|
end
|
||||||
|
|
||||||
def protocol
|
|
||||||
fetched_resource.third
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
def type
|
||||||
return json_data['type'] if protocol == :activitypub
|
json_data['type']
|
||||||
end
|
end
|
||||||
|
|
||||||
def json_data
|
def json_data
|
||||||
|
@ -4,21 +4,12 @@
|
|||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
= t('admin.reports.report', id: @report.id)
|
= t('admin.reports.report', id: @report.id)
|
||||||
|
|
||||||
%div{ style: 'overflow: hidden; margin-bottom: 20px' }
|
- content_for :page_heading_actions do
|
||||||
- if @report.unresolved?
|
- 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
|
- else
|
||||||
= link_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), method: :post, class: 'button'
|
= link_to t('admin.reports.mark_as_unresolved'), reopen_admin_report_path(@report), method: :post, class: 'button'
|
||||||
|
|
||||||
%hr.spacer
|
|
||||||
|
|
||||||
.table-wrapper
|
.table-wrapper
|
||||||
%table.table.inline-table
|
%table.table.inline-table
|
||||||
%tbody
|
%tbody
|
||||||
@ -77,6 +68,17 @@
|
|||||||
|
|
||||||
%hr.spacer
|
%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
|
||||||
.speech-bubble__bubble= simple_format(@report.comment.presence || t('admin.reports.comment.none'))
|
.speech-bubble__bubble= simple_format(@report.comment.presence || t('admin.reports.comment.none'))
|
||||||
.speech-bubble__owner
|
.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
|
- content_for :page_title do
|
||||||
= t('admin.settings.title')
|
= t('admin.settings.title')
|
||||||
|
|
||||||
@ -38,7 +41,9 @@
|
|||||||
%hr.spacer/
|
%hr.spacer/
|
||||||
|
|
||||||
.fields-group
|
.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/
|
%hr.spacer/
|
||||||
|
|
||||||
|
@ -21,8 +21,13 @@
|
|||||||
|
|
||||||
.content-wrapper
|
.content-wrapper
|
||||||
.content
|
.content
|
||||||
|
.content-heading
|
||||||
%h2= yield :page_title
|
%h2= yield :page_title
|
||||||
|
|
||||||
|
- if :page_heading_actions
|
||||||
|
.content-heading-actions
|
||||||
|
= yield :page_heading_actions
|
||||||
|
|
||||||
= render 'application/flashes'
|
= render 'application/flashes'
|
||||||
|
|
||||||
= yield
|
= yield
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
%td= number_to_human_size @export.total_storage
|
%td= number_to_human_size @export.total_storage
|
||||||
%td
|
%td
|
||||||
%tr
|
%tr
|
||||||
%th= t('accounts.posts', count: @export.total_statuses)
|
%th= t('accounts.posts_tab_heading')
|
||||||
%td= number_with_delimiter @export.total_statuses
|
%td= number_with_delimiter @export.total_statuses
|
||||||
%td
|
%td
|
||||||
%tr
|
%tr
|
||||||
%th= t('exports.follows')
|
%th= t('admin.accounts.follows')
|
||||||
%td= number_with_delimiter @export.total_follows
|
%td= number_with_delimiter @export.total_follows
|
||||||
%td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv)
|
%td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv)
|
||||||
%tr
|
%tr
|
||||||
@ -21,7 +21,7 @@
|
|||||||
%td= number_with_delimiter @export.total_lists
|
%td= number_with_delimiter @export.total_lists
|
||||||
%td= table_link_to 'download', t('exports.csv'), settings_exports_lists_path(format: :csv)
|
%td= table_link_to 'download', t('exports.csv'), settings_exports_lists_path(format: :csv)
|
||||||
%tr
|
%tr
|
||||||
%th= t('accounts.followers', count: @export.total_followers)
|
%th= t('admin.accounts.followers')
|
||||||
%td= number_with_delimiter @export.total_followers
|
%td= number_with_delimiter @export.total_followers
|
||||||
%td
|
%td
|
||||||
%tr
|
%tr
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
%hr.spacer/
|
%hr.spacer/
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
|
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked'), recommended: true
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
|
= f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
|
||||||
@ -32,18 +32,20 @@
|
|||||||
|
|
||||||
%hr.spacer/
|
%hr.spacer/
|
||||||
|
|
||||||
.fields-row
|
.custom-label-container
|
||||||
.fields-row__column.fields-group.fields-row__column-6
|
.fields-group.custom_labels_container
|
||||||
.input.with_block_label
|
.with_block_label
|
||||||
%label= t('simple_form.labels.defaults.fields')
|
%label= t('simple_form.labels.defaults.fields')
|
||||||
%span.hint= t('simple_form.hints.defaults.fields')
|
%span.hint= t('simple_form.hints.defaults.fields')
|
||||||
|
|
||||||
= f.simple_fields_for :fields do |fields_f|
|
= f.simple_fields_for :fields do |fields_f|
|
||||||
.row
|
.columns
|
||||||
|
.column.tag-side
|
||||||
= fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name'), input_html: { maxlength: 255 }
|
= fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name'), input_html: { maxlength: 255 }
|
||||||
|
.column.value-side
|
||||||
= fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value'), input_html: { maxlength: 255 }
|
= fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value'), input_html: { maxlength: 255 }
|
||||||
|
|
||||||
.fields-row__column.fields-group.fields-row__column-6
|
.fields-group.verification-container
|
||||||
%h6= t('verification.verification')
|
%h6= t('verification.verification')
|
||||||
%p.hint= t('verification.explanation_html')
|
%p.hint= t('verification.explanation_html')
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user