kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge remote-tracking branch 'origin/develop' into typescript
commit
79b782600b
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
- dashboard-filled.svg - Modified from Tabler icons, MIT
|
- dashboard-filled.svg - Modified from Tabler icons, MIT
|
||||||
- fediverse.svg - Modified from Wikipedia, CC0
|
- fediverse.svg - Modified from Wikipedia, CC0
|
||||||
- gavel.svg - Created by ramsha61 for this project, MIT
|
|
||||||
- home-squared.svg - Modified from Tabler icons, MIT
|
- home-squared.svg - Modified from Tabler icons, MIT
|
||||||
- pen-plus.svg - Modified from Tabler icons, MIT
|
- pen-plus.svg - Modified from Tabler icons, MIT
|
||||||
|
|
||||||
Tabler: https://tabler-icons.io/
|
Tabler: https://tabler-icons.io/
|
||||||
Feather: https://feathericons.com/
|
Feather: https://feathericons.com/
|
||||||
Fediverse logo: https://en.wikipedia.org/wiki/Fediverse#/media/File:Fediverse_logo_proposal.svg
|
Fediverse logo: https://en.wikipedia.org/wiki/Fediverse#/media/File:Fediverse_logo_proposal.svg
|
||||||
ramsha61: https://www.fiverr.com/ramsha61
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M10 9.07l-.1.1a.87.87 0 0 1-.59.24.82.82 0 0 1-.59-.24l-.83-.82a.86.86 0 0 1 0-1.19l4-4a.82.82 0 0 1 .59-.25.83.83 0 0 1 .59.25l.82.82a.83.83 0 0 1 .25.59.82.82 0 0 1-.25.59l-.1.11h0a14.27 14.27 0 0 0 4.6 4.61l.11-.11a.87.87 0 0 1 .59-.24.82.82 0 0 1 .59.24l.83.83a.85.85 0 0 1 0 1.18l-4 4a.85.85 0 0 1-1.18 0l-.8-.78a.81.81 0 0 1 0-1.16h0l.1-.11m-1.49-1.37l-8 8.4-.14.16a1 1 0 0 1-.48.16h-.11a.88.88 0 0 1-.63-.26l-.9-.9a.92.92 0 0 1-.1-1.17 1 1 0 0 1 .16-.2l8.39-8"/>
|
|
||||||
<path d="M13.91 5.17l-.1.11-.37.37-3.71 3.71A14.69 14.69 0 0 1 14.34 14l4.18-4.19"/>
|
|
||||||
<path d="M21.37,20.75a.31.31,0,0,1,0,.23.26.26,0,0,1-.2.1H12.74a.28.28,0,0,1-.21-.1.31.31,0,0,1,0-.23L13,19.23a.85.85,0,0,1,.81-.6h6.37a.83.83,0,0,1,.8.6l.45,1.52Z"/>
|
|
||||||
</svg>
|
|
Przed Szerokość: | Wysokość: | Rozmiar: 951 B |
|
@ -0,0 +1,127 @@
|
||||||
|
{
|
||||||
|
"acct": "alex",
|
||||||
|
"avatar": "https://media.gleasonator.com/6d64aecb17348b23aaff78db4687b9476cb0da1c07cc6a819c2e6ec7144c18b1.png",
|
||||||
|
"avatar_static": "https://media.gleasonator.com/6d64aecb17348b23aaff78db4687b9476cb0da1c07cc6a819c2e6ec7144c18b1.png",
|
||||||
|
"bot": false,
|
||||||
|
"created_at": "2020-01-08T01:25:43.000Z",
|
||||||
|
"display_name": "Alex Gleason",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "Website",
|
||||||
|
"value": "<a href=\"https://alexgleason.me\" rel=\"ugc\">https://alexgleason.me</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Soapbox",
|
||||||
|
"value": "<a href=\"https://soapbox.pub\" rel=\"ugc\">https://soapbox.pub</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Email",
|
||||||
|
"value": "alex@alexgleason.me"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gender identity",
|
||||||
|
"value": "Soyboy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Donate (PayPal)",
|
||||||
|
"value": "<a href=\"https://paypal.me/gleasonator\" rel=\"ugc\">https://paypal.me/gleasonator</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$BTC",
|
||||||
|
"value": "bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$ETH",
|
||||||
|
"value": "0xAc9aB5Fc04Dc1cB1789Af75b523Bd23C70B2D717"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$DOGE",
|
||||||
|
"value": "D5zVZs6jrRakaPVGiErkQiHt9sayzm6V5D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$XMR",
|
||||||
|
"value": "45JDCLrjJ4bgVUSbbs2yjy9m5Mf4VLPW8fG7jw9sq5u69rXZZopQogZNeyYkMBnXpkaip4p4QwaaJNhdTotPa9g44DBCzdK"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"followers_count": 2378,
|
||||||
|
"following_count": 1571,
|
||||||
|
"fqn": "alex@gleasonator.com",
|
||||||
|
"header": "https://media.gleasonator.com/accounts/headers/000/000/001/original/9d0e4dbf1c9dbc8f.png",
|
||||||
|
"header_static": "https://media.gleasonator.com/accounts/headers/000/000/001/original/9d0e4dbf1c9dbc8f.png",
|
||||||
|
"id": "9v5bmRalQvjOy0ECcC",
|
||||||
|
"last_status_at": "2022-02-20T04:14:49",
|
||||||
|
"locked": false,
|
||||||
|
"note": "I create Fediverse software that empowers people online.<br/><br/>I'm vegan btw<br/><br/>Note: If you have a question for me, please tag me publicly. This gives the opportunity for others to chime in, and bystanders to learn.",
|
||||||
|
"pleroma": {
|
||||||
|
"accepts_chat_messages": true,
|
||||||
|
"also_known_as": [
|
||||||
|
"https://mitra.social/users/alex"
|
||||||
|
],
|
||||||
|
"ap_id": "https://gleasonator.com/users/alex",
|
||||||
|
"background_image": null,
|
||||||
|
"birthday": "1993-07-03",
|
||||||
|
"favicon": "https://gleasonator.com/favicon.png",
|
||||||
|
"hide_favorites": true,
|
||||||
|
"hide_followers": false,
|
||||||
|
"hide_followers_count": false,
|
||||||
|
"hide_follows": false,
|
||||||
|
"hide_follows_count": false,
|
||||||
|
"is_admin": true,
|
||||||
|
"is_confirmed": true,
|
||||||
|
"is_moderator": false,
|
||||||
|
"is_suggested": true,
|
||||||
|
"relationship": {},
|
||||||
|
"skip_thread_containment": false,
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "Website",
|
||||||
|
"value": "https://alexgleason.me"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Soapbox",
|
||||||
|
"value": "https://soapbox.pub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Email",
|
||||||
|
"value": "alex@alexgleason.me"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gender identity",
|
||||||
|
"value": "Soyboy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Donate (PayPal)",
|
||||||
|
"value": "https://paypal.me/gleasonator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$BTC",
|
||||||
|
"value": "bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$ETH",
|
||||||
|
"value": "0xAc9aB5Fc04Dc1cB1789Af75b523Bd23C70B2D717"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$DOGE",
|
||||||
|
"value": "D5zVZs6jrRakaPVGiErkQiHt9sayzm6V5D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$XMR",
|
||||||
|
"value": "45JDCLrjJ4bgVUSbbs2yjy9m5Mf4VLPW8fG7jw9sq5u69rXZZopQogZNeyYkMBnXpkaip4p4QwaaJNhdTotPa9g44DBCzdK"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"note": "I create Fediverse software that empowers people online.\r\n\r\nI'm vegan btw\r\n\r\nNote: If you have a question for me, please tag me publicly. This gives the opportunity for others to chime in, and bystanders to learn.",
|
||||||
|
"pleroma": {
|
||||||
|
"actor_type": "Person",
|
||||||
|
"discoverable": false
|
||||||
|
},
|
||||||
|
"sensitive": false
|
||||||
|
},
|
||||||
|
"statuses_count": 23477,
|
||||||
|
"url": "https://gleasonator.com/users/alex",
|
||||||
|
"username": "alex"
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
{
|
||||||
|
"uri": "fedibird.com",
|
||||||
|
"title": "Fedibird",
|
||||||
|
"short_description": "多くの独自機能を備えた、連合志向の汎用Mastodonサーバです。Fediverseの活動拠点としてご利用ください。",
|
||||||
|
"description": "多くの独自機能を備えた、連合志向の汎用Mastodonサーバです。Fediverseの活動拠点としてご利用ください。",
|
||||||
|
"email": "support@fedibird.com",
|
||||||
|
"version": "3.4.1",
|
||||||
|
"urls": {
|
||||||
|
"streaming_api": "wss://fedibird.com"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"user_count": 1964,
|
||||||
|
"status_count": 4590304,
|
||||||
|
"domain_count": 9024
|
||||||
|
},
|
||||||
|
"thumbnail": "https://s3.fedibird.com/site_uploads/files/000/000/001/original/fedibird_hero_image.png",
|
||||||
|
"languages": [
|
||||||
|
"ja"
|
||||||
|
],
|
||||||
|
"registrations": true,
|
||||||
|
"approval_required": false,
|
||||||
|
"invites_enabled": true,
|
||||||
|
"configuration": {
|
||||||
|
"statuses": {
|
||||||
|
"max_characters": 500,
|
||||||
|
"max_media_attachments": 4,
|
||||||
|
"characters_reserved_per_url": 23,
|
||||||
|
"min_expiration": 60,
|
||||||
|
"max_expiration": 37152000,
|
||||||
|
"supported_expires_actions": [
|
||||||
|
"delete",
|
||||||
|
"mark"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"media_attachments": {
|
||||||
|
"supported_mime_types": [
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/gif",
|
||||||
|
"image/webp",
|
||||||
|
"image/heif",
|
||||||
|
"image/heic",
|
||||||
|
"video/webm",
|
||||||
|
"video/mp4",
|
||||||
|
"video/quicktime",
|
||||||
|
"video/ogg",
|
||||||
|
"audio/wave",
|
||||||
|
"audio/wav",
|
||||||
|
"audio/x-wav",
|
||||||
|
"audio/x-pn-wave",
|
||||||
|
"audio/ogg",
|
||||||
|
"audio/mpeg",
|
||||||
|
"audio/mp3",
|
||||||
|
"audio/webm",
|
||||||
|
"audio/flac",
|
||||||
|
"audio/aac",
|
||||||
|
"audio/m4a",
|
||||||
|
"audio/x-m4a",
|
||||||
|
"audio/mp4",
|
||||||
|
"audio/3gpp",
|
||||||
|
"video/x-ms-asf"
|
||||||
|
],
|
||||||
|
"image_size_limit": 10485760,
|
||||||
|
"image_matrix_limit": 16777216,
|
||||||
|
"video_size_limit": 41943040,
|
||||||
|
"video_frame_rate_limit": 60,
|
||||||
|
"video_matrix_limit": 2304000
|
||||||
|
},
|
||||||
|
"polls": {
|
||||||
|
"max_options": 4,
|
||||||
|
"max_characters_per_option": 50,
|
||||||
|
"min_expiration": 300,
|
||||||
|
"max_expiration": 2629746
|
||||||
|
},
|
||||||
|
"emoji_reactions": {
|
||||||
|
"max_reactions": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"feature_quote": true,
|
||||||
|
"fedibird_capabilities": [
|
||||||
|
"favourite_hashtag",
|
||||||
|
"favourite_domain",
|
||||||
|
"favourite_list",
|
||||||
|
"status_expire",
|
||||||
|
"follow_no_delivery",
|
||||||
|
"follow_hashtag",
|
||||||
|
"subscribe_account",
|
||||||
|
"subscribe_domain",
|
||||||
|
"subscribe_keyword",
|
||||||
|
"timeline_home_visibility",
|
||||||
|
"timeline_no_local",
|
||||||
|
"timeline_domain",
|
||||||
|
"timeline_group",
|
||||||
|
"timeline_group_directory",
|
||||||
|
"visibility_mutual",
|
||||||
|
"visibility_limited",
|
||||||
|
"emoji_reaction",
|
||||||
|
"misskey_birthday",
|
||||||
|
"misskey_location"
|
||||||
|
],
|
||||||
|
"contact_account": {
|
||||||
|
"id": "1",
|
||||||
|
"username": "noellabo",
|
||||||
|
"acct": "noellabo",
|
||||||
|
"display_name": "のえる",
|
||||||
|
"locked": false,
|
||||||
|
"bot": false,
|
||||||
|
"cat": false,
|
||||||
|
"discoverable": true,
|
||||||
|
"group": false,
|
||||||
|
"created_at": "2019-08-15T00:00:00.000Z",
|
||||||
|
"note": "<p>主に、Fediverseへの関心に基づいた投稿を行うアカウントです。DTP・印刷に関する話をしたり、同人の話をしたり、カレーをブーストしたりします。</p><p>Mastodonサーバ『Fedibird』の管理者アカウントでもあります。ご連絡は当アカウントへ、サーバインフォメーションについては <a href=\"https://fedibird.com/about/more\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">fedibird.com/about/more</span><span class=\"invisible\"></span></a> と <span class=\"h-card\"><a href=\"https://fedibird.com/@info\" class=\"u-url mention\">@<span>info</span></a></span> を参照してください。</p>",
|
||||||
|
"url": "https://fedibird.com/@noellabo",
|
||||||
|
"avatar": "https://s3.fedibird.com/accounts/avatars/000/000/001/original/6ef3b7f18f726755.png",
|
||||||
|
"avatar_static": "https://s3.fedibird.com/accounts/avatars/000/000/001/original/6ef3b7f18f726755.png",
|
||||||
|
"header": "https://s3.fedibird.com/accounts/headers/000/000/001/original/6a5a51722c094835.jpg",
|
||||||
|
"header_static": "https://s3.fedibird.com/accounts/headers/000/000/001/original/6a5a51722c094835.jpg",
|
||||||
|
"followers_count": 1560,
|
||||||
|
"following_count": 758,
|
||||||
|
"subscribing_count": 121,
|
||||||
|
"statuses_count": 61325,
|
||||||
|
"last_status_at": "2022-02-24",
|
||||||
|
"emojis": [
|
||||||
|
{
|
||||||
|
"shortcode": "liberapay",
|
||||||
|
"url": "https://s3.fedibird.com/custom_emojis/images/000/025/634/original/5b8620742973f844.png",
|
||||||
|
"static_url": "https://s3.fedibird.com/custom_emojis/images/000/025/634/static/5b8620742973f844.png",
|
||||||
|
"visible_in_picker": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shortcode": "mastodon",
|
||||||
|
"url": "https://s3.fedibird.com/custom_emojis/images/000/008/396/original/1317b6f8efcf8318.png",
|
||||||
|
"static_url": "https://s3.fedibird.com/custom_emojis/images/000/008/396/static/1317b6f8efcf8318.png",
|
||||||
|
"visible_in_picker": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": ":liberapay: Liberapay",
|
||||||
|
"value": "<a href=\"https://liberapay.com/noellabo\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">liberapay.com/noellabo</span><span class=\"invisible\"></span></a>",
|
||||||
|
"verified_at": "2020-10-22T03:04:43.206+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ":mastodon: DTP-Mstdn.jp",
|
||||||
|
"value": "<a class=\"account-url-link\" data-account-acct=\"noellabo@dtp-mstdn.jp\" data-account-actor-type=\"Person\" data-account-id=\"55\" href=\"https://dtp-mstdn.jp/@noellabo\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">dtp-mstdn.jp/@noellabo</span><span class=\"invisible\"></span></a>",
|
||||||
|
"verified_at": "2020-05-23T00:14:02.232+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "別宅",
|
||||||
|
"value": "<a class=\"account-url-link\" data-account-acct=\"noellabo@gorone.xyz\" data-account-actor-type=\"Person\" data-account-id=\"14504\" href=\"https://gorone.xyz/@noellabo\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">gorone.xyz/@noellabo</span><span class=\"invisible\"></span></a>",
|
||||||
|
"verified_at": "2021-08-11T07:48:53.479+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bluesky community",
|
||||||
|
"value": "<a class=\"account-url-link\" data-account-acct=\"noellabo@mastodon.blueskycommunity.net\" data-account-actor-type=\"Person\" data-account-id=\"107267866207603606\" href=\"https://mastodon.blueskycommunity.net/@noellabo\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">mastodon.blueskycommunity.net/</span><span class=\"invisible\">@noellabo</span></a>",
|
||||||
|
"verified_at": "2021-11-13T04:28:30.593+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"other_settings": {
|
||||||
|
"birthday": null,
|
||||||
|
"location": "埼玉県",
|
||||||
|
"cat_ears_color": "#d5c5c0",
|
||||||
|
"noindex": false,
|
||||||
|
"hide_network": false,
|
||||||
|
"hide_statuses_count": false,
|
||||||
|
"hide_following_count": false,
|
||||||
|
"hide_followers_count": false,
|
||||||
|
"enable_reaction": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"text": "日本の法律と社会規範に従った行動を心がけてください"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3",
|
||||||
|
"text": "不快や脅威に対してはブロック・ミュート・フィルターで距離をとってください"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"text": "投稿する際は、適切な公開範囲・CW・閲覧注意を使用してください"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"uri": "https://ica.mkljczk.pl",
|
||||||
|
"title": "Friendica Social Network",
|
||||||
|
"short_description": "",
|
||||||
|
"description": "",
|
||||||
|
"email": "me@mkljczk.pl",
|
||||||
|
"version": "2022.05-dev",
|
||||||
|
"urls": null,
|
||||||
|
"stats": {
|
||||||
|
"user_count": 0,
|
||||||
|
"status_count": 0,
|
||||||
|
"domain_count": 0
|
||||||
|
},
|
||||||
|
"thumbnail": "https://ica.mkljczk.plimages/friendica-32.png",
|
||||||
|
"languages": [
|
||||||
|
"pl"
|
||||||
|
],
|
||||||
|
"max_toot_chars": 200000,
|
||||||
|
"registrations": true,
|
||||||
|
"approval_required": false,
|
||||||
|
"invites_enabled": false,
|
||||||
|
"contact_account": {
|
||||||
|
"id": "2",
|
||||||
|
"username": "nofriend",
|
||||||
|
"acct": "nofriend",
|
||||||
|
"display_name": "marcin mikołajczak",
|
||||||
|
"locked": true,
|
||||||
|
"bot": false,
|
||||||
|
"discoverable": true,
|
||||||
|
"group": false,
|
||||||
|
"created_at": "2022-02-19T14:51:00.000Z",
|
||||||
|
"note": "",
|
||||||
|
"url": "https://ica.mkljczk.pl/profile/nofriend",
|
||||||
|
"avatar": "https://ica.mkljczk.pl/photo/contact/300/68a16c11-1262-1103-d40b-806159848009?ts=1645292106",
|
||||||
|
"avatar_static": "https://ica.mkljczk.pl/photo/contact/300/68a16c11-1262-1103-d40b-806159848009?ts=1645292106",
|
||||||
|
"header": "https://ica.mkljczk.pl/photo/header/68a16c11-1262-1103-d40b-806159848009?ts=1645292106",
|
||||||
|
"header_static": "https://ica.mkljczk.pl/photo/header/68a16c11-1262-1103-d40b-806159848009?ts=1645292106",
|
||||||
|
"followers_count": 0,
|
||||||
|
"following_count": 1,
|
||||||
|
"statuses_count": 0,
|
||||||
|
"last_status_at": "2022-02-20",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": []
|
||||||
|
},
|
||||||
|
"rules": []
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
"id": "106",
|
||||||
|
"created_at": "2022-02-19T18:19:40.000Z",
|
||||||
|
"in_reply_to_id": null,
|
||||||
|
"in_reply_to_account_id": null,
|
||||||
|
"sensitive": false,
|
||||||
|
"spoiler_text": "",
|
||||||
|
"visibility": "public",
|
||||||
|
"language": "fa",
|
||||||
|
"uri": "https://ica.mkljczk.pl/objects/68a16c11-4262-1134-bc4e-0db298374337",
|
||||||
|
"url": "https://ica.mkljczk.pl/display/68a16c11-4262-1134-bc4e-0db298374337",
|
||||||
|
"replies_count": 0,
|
||||||
|
"reblogs_count": 0,
|
||||||
|
"favourites_count": 0,
|
||||||
|
"favourited": false,
|
||||||
|
"reblogged": false,
|
||||||
|
"muted": false,
|
||||||
|
"bookmarked": true,
|
||||||
|
"content": "Hello to Friendica from fe.soapbox.pub!",
|
||||||
|
"reblog": null,
|
||||||
|
"application": {
|
||||||
|
"name": "Soapbox FE"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"id": "95",
|
||||||
|
"username": "alex",
|
||||||
|
"acct": "alex",
|
||||||
|
"display_name": "Alex Gleason",
|
||||||
|
"locked": true,
|
||||||
|
"bot": false,
|
||||||
|
"discoverable": false,
|
||||||
|
"group": false,
|
||||||
|
"created_at": "2022-02-19T18:17:43.000Z",
|
||||||
|
"note": "",
|
||||||
|
"url": "https://ica.mkljczk.pl/profile/alex",
|
||||||
|
"avatar": "https://ica.mkljczk.pl/photo/contact/300/68a16c11-1862-1134-4779-f98088458845?ts=1645294804",
|
||||||
|
"avatar_static": "https://ica.mkljczk.pl/photo/contact/300/68a16c11-1862-1134-4779-f98088458845?ts=1645294804",
|
||||||
|
"header": "https://ica.mkljczk.pl/photo/header/68a16c11-1862-1134-4779-f98088458845?ts=1645294804",
|
||||||
|
"header_static": "https://ica.mkljczk.pl/photo/header/68a16c11-1862-1134-4779-f98088458845?ts=1645294804",
|
||||||
|
"followers_count": 0,
|
||||||
|
"following_count": 0,
|
||||||
|
"statuses_count": 2,
|
||||||
|
"last_status_at": "2022-02-19",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": []
|
||||||
|
},
|
||||||
|
"media_attachments": [],
|
||||||
|
"mentions": [],
|
||||||
|
"tags": [],
|
||||||
|
"emojis": [],
|
||||||
|
"card": null,
|
||||||
|
"poll": null
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"id": "00YSECR4P7E64BD5MBA639PRVT",
|
||||||
|
"username": "alex",
|
||||||
|
"acct": "alex",
|
||||||
|
"display_name": "Alex Gleason",
|
||||||
|
"locked": false,
|
||||||
|
"bot": false,
|
||||||
|
"created_at": "2022-02-23T22:43:55Z",
|
||||||
|
"note": "<p>My GoToSocial profile</p>",
|
||||||
|
"url": "http://localhost/@alex",
|
||||||
|
"avatar": "",
|
||||||
|
"avatar_static": "",
|
||||||
|
"header": "",
|
||||||
|
"header_static": "",
|
||||||
|
"followers_count": 0,
|
||||||
|
"following_count": 0,
|
||||||
|
"statuses_count": 1,
|
||||||
|
"last_status_at": "2022-02-23T22:54:14Z",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": [],
|
||||||
|
"source": {
|
||||||
|
"privacy": "unlisted",
|
||||||
|
"language": "en",
|
||||||
|
"note": "<p>My GoToSocial profile</p>",
|
||||||
|
"fields": []
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"uri": "http://localhost",
|
||||||
|
"title": "localhost",
|
||||||
|
"description": "",
|
||||||
|
"short_description": "",
|
||||||
|
"email": "",
|
||||||
|
"version": "0.2.0 31935ee",
|
||||||
|
"registrations": true,
|
||||||
|
"approval_required": true,
|
||||||
|
"invites_enabled": false,
|
||||||
|
"urls": {
|
||||||
|
"streaming_api": "wss://localhost"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"domain_count": 0,
|
||||||
|
"status_count": 1,
|
||||||
|
"user_count": 1
|
||||||
|
},
|
||||||
|
"thumbnail": "",
|
||||||
|
"contact_account": {
|
||||||
|
"id": "",
|
||||||
|
"username": "",
|
||||||
|
"acct": "",
|
||||||
|
"display_name": "",
|
||||||
|
"locked": false,
|
||||||
|
"bot": false,
|
||||||
|
"created_at": "",
|
||||||
|
"note": "",
|
||||||
|
"url": "",
|
||||||
|
"avatar": "",
|
||||||
|
"avatar_static": "",
|
||||||
|
"header": "",
|
||||||
|
"header_static": "",
|
||||||
|
"followers_count": 0,
|
||||||
|
"following_count": 0,
|
||||||
|
"statuses_count": 0,
|
||||||
|
"last_status_at": "",
|
||||||
|
"emojis": null,
|
||||||
|
"fields": null
|
||||||
|
},
|
||||||
|
"max_toot_chars": 5000
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"id": "01FWMCNM07GGDV8HF40NZ9YTGR",
|
||||||
|
"created_at": "2022-02-23T22:54:14Z",
|
||||||
|
"sensitive": false,
|
||||||
|
"spoiler_text": "",
|
||||||
|
"visibility": "public",
|
||||||
|
"language": "en",
|
||||||
|
"uri": "http://localhost/users/alex/statuses/01FWMCNM07GGDV8HF40NZ9YTGR",
|
||||||
|
"url": "http://localhost/@alex/statuses/01FWMCNM07GGDV8HF40NZ9YTGR",
|
||||||
|
"replies_count": 0,
|
||||||
|
"reblogs_count": 0,
|
||||||
|
"favourites_count": 0,
|
||||||
|
"favourited": false,
|
||||||
|
"reblogged": false,
|
||||||
|
"muted": false,
|
||||||
|
"bookmarked": false,
|
||||||
|
"content": "<p>Hello GoToSocial!</p>",
|
||||||
|
"application": {
|
||||||
|
"name": "Soapbox FE",
|
||||||
|
"website": "https://soapbox.pub/"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"id": "00YSECR4P7E64BD5MBA639PRVT",
|
||||||
|
"username": "alex",
|
||||||
|
"acct": "alex",
|
||||||
|
"display_name": "alex",
|
||||||
|
"locked": false,
|
||||||
|
"bot": false,
|
||||||
|
"created_at": "2022-02-23T22:43:55Z",
|
||||||
|
"note": "",
|
||||||
|
"url": "http://localhost/@alex",
|
||||||
|
"avatar": "",
|
||||||
|
"avatar_static": "",
|
||||||
|
"header": "",
|
||||||
|
"header_static": "",
|
||||||
|
"followers_count": 0,
|
||||||
|
"following_count": 0,
|
||||||
|
"statuses_count": 1,
|
||||||
|
"last_status_at": "2022-02-23T22:54:14Z",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": []
|
||||||
|
},
|
||||||
|
"media_attachments": [],
|
||||||
|
"mentions": [],
|
||||||
|
"tags": [],
|
||||||
|
"emojis": [],
|
||||||
|
"card": null,
|
||||||
|
"poll": null,
|
||||||
|
"text": "Hello GoToSocial!"
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"id": "107828148293766288",
|
||||||
|
"created_at": "2022-02-20T03:16:09.812Z",
|
||||||
|
"in_reply_to_id": "107828147870368566",
|
||||||
|
"in_reply_to_account_id": "106801667066418367",
|
||||||
|
"sensitive": false,
|
||||||
|
"spoiler_text": "",
|
||||||
|
"visibility": "public",
|
||||||
|
"language": "en",
|
||||||
|
"uri": "https://mastodon.social/users/benis911/statuses/107828148293766288",
|
||||||
|
"url": "https://mastodon.social/@benis911/107828148293766288",
|
||||||
|
"replies_count": 0,
|
||||||
|
"reblogs_count": 0,
|
||||||
|
"favourites_count": 0,
|
||||||
|
"edited_at": null,
|
||||||
|
"content": "<p>test reply to self</p>",
|
||||||
|
"reblog": null,
|
||||||
|
"application": {
|
||||||
|
"name": "Soapbox FE",
|
||||||
|
"website": "https://soapbox.pub/"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"id": "106801667066418367",
|
||||||
|
"username": "benis911",
|
||||||
|
"acct": "benis911",
|
||||||
|
"display_name": "",
|
||||||
|
"locked": false,
|
||||||
|
"bot": false,
|
||||||
|
"discoverable": null,
|
||||||
|
"group": false,
|
||||||
|
"created_at": "2021-08-22T00:00:00.000Z",
|
||||||
|
"note": "<p></p>",
|
||||||
|
"url": "https://mastodon.social/@benis911",
|
||||||
|
"avatar": "https://mastodon.social/avatars/original/missing.png",
|
||||||
|
"avatar_static": "https://mastodon.social/avatars/original/missing.png",
|
||||||
|
"header": "https://mastodon.social/headers/original/missing.png",
|
||||||
|
"header_static": "https://mastodon.social/headers/original/missing.png",
|
||||||
|
"followers_count": 0,
|
||||||
|
"following_count": 0,
|
||||||
|
"statuses_count": 3,
|
||||||
|
"last_status_at": "2022-02-20",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": []
|
||||||
|
},
|
||||||
|
"media_attachments": [],
|
||||||
|
"mentions": [],
|
||||||
|
"tags": [],
|
||||||
|
"emojis": [],
|
||||||
|
"card": null,
|
||||||
|
"poll": null
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"uri": "mitra.social",
|
||||||
|
"title": "Mitra",
|
||||||
|
"short_description": "Federated social network with smart contracts",
|
||||||
|
"description": "This is an instance of [Mitra](https://codeberg.org/silverpill/mitra), federated social network built on [ActivityPub](https://activitypub.rocks/) protocol.\nRegistration is invitation-only.\nAdmin:\n - [@silverpill@mitra.social](https://mitra.social/profile/dd4ebc18-269d-4c7b-a310-03d29c6ab551)\n - Matrix: @silverpill:poa.st\n",
|
||||||
|
"version": "3.0.0 (compatible; Mitra 0.4.0)",
|
||||||
|
"registrations": false,
|
||||||
|
"login_message": "Sign this message to log in to https://mitra.social. Do not sign this message on other sites!",
|
||||||
|
"post_character_limit": 5000,
|
||||||
|
"blockchain_explorer_url": null,
|
||||||
|
"blockchain_contract_address": null,
|
||||||
|
"ipfs_gateway_url": "https://ipfs.mitra.social"
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"acct": "benis911",
|
||||||
|
"avatar": "https://gleasonator.com/images/avi.png",
|
||||||
|
"avatar_static": "https://gleasonator.com/images/avi.png",
|
||||||
|
"bot": false,
|
||||||
|
"created_at": "2021-03-26T20:42:11.000Z",
|
||||||
|
"display_name": "benis911",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": [],
|
||||||
|
"followers_count": 0,
|
||||||
|
"following_count": 0,
|
||||||
|
"fqn": "benis911@gleasonator.com",
|
||||||
|
"header": "https://media.gleasonator.com/fc595bbbcf5aabefecd1c2adfe5b7f5457db59847992881668653a0338ba25bd.jpg",
|
||||||
|
"header_static": "https://media.gleasonator.com/fc595bbbcf5aabefecd1c2adfe5b7f5457db59847992881668653a0338ba25bd.jpg",
|
||||||
|
"id": "A5c5LK7EJTFR0u26Pg",
|
||||||
|
"last_status_at": "2022-02-23T17:31:08",
|
||||||
|
"locked": true,
|
||||||
|
"note": "hello world 2",
|
||||||
|
"pleroma": {
|
||||||
|
"accepts_chat_messages": true,
|
||||||
|
"also_known_as": [
|
||||||
|
"https://gleasonator.com/users/alex",
|
||||||
|
"https://poa.st/users/alex"
|
||||||
|
],
|
||||||
|
"ap_id": "https://gleasonator.com/users/benis911",
|
||||||
|
"background_image": null,
|
||||||
|
"birthday": "2000-01-25",
|
||||||
|
"favicon": "https://gleasonator.com/favicon.png",
|
||||||
|
"hide_favorites": true,
|
||||||
|
"hide_followers": true,
|
||||||
|
"hide_followers_count": true,
|
||||||
|
"hide_follows": true,
|
||||||
|
"hide_follows_count": true,
|
||||||
|
"is_admin": false,
|
||||||
|
"is_confirmed": true,
|
||||||
|
"is_moderator": false,
|
||||||
|
"is_suggested": false,
|
||||||
|
"relationship": {},
|
||||||
|
"skip_thread_containment": false,
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"fields": [],
|
||||||
|
"note": "hello world 2",
|
||||||
|
"pleroma": {
|
||||||
|
"actor_type": "Person",
|
||||||
|
"discoverable": false
|
||||||
|
},
|
||||||
|
"sensitive": false
|
||||||
|
},
|
||||||
|
"statuses_count": 152,
|
||||||
|
"url": "https://gleasonator.com/users/benis911",
|
||||||
|
"username": "benis911"
|
||||||
|
},
|
||||||
|
"application": {
|
||||||
|
"name": "Soapbox FE",
|
||||||
|
"website": "https://soapbox.pub/"
|
||||||
|
},
|
||||||
|
"bookmarked": false,
|
||||||
|
"card": null,
|
||||||
|
"content": "Hello :ablobcathyper: :ageblobcat: 😂 world 😋 test :blobcatphoto:",
|
||||||
|
"created_at": "2022-02-23T17:31:07.000Z",
|
||||||
|
"emojis": [
|
||||||
|
{
|
||||||
|
"shortcode": "ablobcathyper",
|
||||||
|
"static_url": "https://gleasonator.com/emoji/blobcat/ablobcathyper.png",
|
||||||
|
"url": "https://gleasonator.com/emoji/blobcat/ablobcathyper.png",
|
||||||
|
"visible_in_picker": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shortcode": "ageblobcat",
|
||||||
|
"static_url": "https://gleasonator.com/emoji/blobcat/ageblobcat.png",
|
||||||
|
"url": "https://gleasonator.com/emoji/blobcat/ageblobcat.png",
|
||||||
|
"visible_in_picker": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"shortcode": "blobcatphoto",
|
||||||
|
"static_url": "https://gleasonator.com/emoji/blobcat/blobcatphoto.png",
|
||||||
|
"url": "https://gleasonator.com/emoji/blobcat/blobcatphoto.png",
|
||||||
|
"visible_in_picker": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"favourited": false,
|
||||||
|
"favourites_count": 0,
|
||||||
|
"id": "AGm7uC9DaAIGUa4KYK",
|
||||||
|
"in_reply_to_account_id": null,
|
||||||
|
"in_reply_to_id": null,
|
||||||
|
"language": null,
|
||||||
|
"media_attachments": [],
|
||||||
|
"mentions": [],
|
||||||
|
"muted": false,
|
||||||
|
"pinned": false,
|
||||||
|
"pleroma": {
|
||||||
|
"content": {
|
||||||
|
"text/plain": "Hello :ablobcathyper: :ageblobcat: 😂 world 😋 test :blobcatphoto:"
|
||||||
|
},
|
||||||
|
"conversation_id": "AGm7uC3BwZTOBtFW9w",
|
||||||
|
"direct_conversation_id": null,
|
||||||
|
"emoji_reactions": [],
|
||||||
|
"expires_at": null,
|
||||||
|
"in_reply_to_account_acct": null,
|
||||||
|
"local": true,
|
||||||
|
"parent_visible": false,
|
||||||
|
"pinned_at": null,
|
||||||
|
"quote": null,
|
||||||
|
"quote_url": null,
|
||||||
|
"quote_visible": false,
|
||||||
|
"spoiler_text": {
|
||||||
|
"text/plain": ""
|
||||||
|
},
|
||||||
|
"thread_muted": false
|
||||||
|
},
|
||||||
|
"poll": null,
|
||||||
|
"reblog": null,
|
||||||
|
"reblogged": false,
|
||||||
|
"reblogs_count": 0,
|
||||||
|
"replies_count": 0,
|
||||||
|
"sensitive": false,
|
||||||
|
"spoiler_text": "",
|
||||||
|
"tags": [],
|
||||||
|
"text": null,
|
||||||
|
"uri": "https://gleasonator.com/objects/2dc79219-aed6-40c0-8818-0c2d26ed3436",
|
||||||
|
"url": "https://gleasonator.com/notice/AGm7uC9DaAIGUa4KYK",
|
||||||
|
"visibility": "public"
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"id": "107831528995252317",
|
||||||
|
"created_at": "2022-02-20T17:35:55.224Z",
|
||||||
|
"in_reply_to_id": null,
|
||||||
|
"in_reply_to_account_id": null,
|
||||||
|
"sensitive": true,
|
||||||
|
"spoiler_text": "testing",
|
||||||
|
"visibility": "public",
|
||||||
|
"language": "en",
|
||||||
|
"uri": "https://fedibird.com/users/alex/statuses/107831528995252317",
|
||||||
|
"url": "https://fedibird.com/@alex/107831528995252317",
|
||||||
|
"replies_count": 0,
|
||||||
|
"reblogs_count": 0,
|
||||||
|
"favourites_count": 0,
|
||||||
|
"emoji_reactions_count": 0,
|
||||||
|
"emoji_reactions": [],
|
||||||
|
"content": "<p>hello world</p>",
|
||||||
|
"reblog": null,
|
||||||
|
"application": {
|
||||||
|
"name": "Web",
|
||||||
|
"website": null
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"id": "66768",
|
||||||
|
"username": "alex",
|
||||||
|
"acct": "alex",
|
||||||
|
"display_name": "",
|
||||||
|
"locked": false,
|
||||||
|
"bot": false,
|
||||||
|
"cat": false,
|
||||||
|
"discoverable": null,
|
||||||
|
"group": false,
|
||||||
|
"created_at": "2020-01-27T00:00:00.000Z",
|
||||||
|
"note": "<p></p>",
|
||||||
|
"url": "https://fedibird.com/@alex",
|
||||||
|
"avatar": "https://fedibird.com/avatars/original/missing.png",
|
||||||
|
"avatar_static": "https://fedibird.com/avatars/original/missing.png",
|
||||||
|
"header": "https://fedibird.com/headers/original/missing.png",
|
||||||
|
"header_static": "https://fedibird.com/headers/original/missing.png",
|
||||||
|
"followers_count": 1,
|
||||||
|
"following_count": 1,
|
||||||
|
"subscribing_count": 0,
|
||||||
|
"statuses_count": 5,
|
||||||
|
"last_status_at": "2022-02-20",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": [],
|
||||||
|
"other_settings": {
|
||||||
|
"noindex": false,
|
||||||
|
"hide_network": false,
|
||||||
|
"hide_statuses_count": false,
|
||||||
|
"hide_following_count": false,
|
||||||
|
"hide_followers_count": false,
|
||||||
|
"enable_reaction": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"media_attachments": [],
|
||||||
|
"mentions": [],
|
||||||
|
"tags": [],
|
||||||
|
"emojis": [],
|
||||||
|
"card": null,
|
||||||
|
"poll": null,
|
||||||
|
"quote": null
|
||||||
|
}
|
|
@ -0,0 +1,201 @@
|
||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"acct": "alex",
|
||||||
|
"avatar": "https://media.gleasonator.com/6d64aecb17348b23aaff78db4687b9476cb0da1c07cc6a819c2e6ec7144c18b1.png",
|
||||||
|
"avatar_static": "https://media.gleasonator.com/6d64aecb17348b23aaff78db4687b9476cb0da1c07cc6a819c2e6ec7144c18b1.png",
|
||||||
|
"bot": false,
|
||||||
|
"created_at": "2020-01-08T01:25:43.000Z",
|
||||||
|
"display_name": "Alex Gleason",
|
||||||
|
"emojis": [],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "Website",
|
||||||
|
"value": "<a href=\"https://alexgleason.me\" rel=\"ugc\">https://alexgleason.me</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Soapbox",
|
||||||
|
"value": "<a href=\"https://soapbox.pub\" rel=\"ugc\">https://soapbox.pub</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Email",
|
||||||
|
"value": "alex@alexgleason.me"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gender identity",
|
||||||
|
"value": "Soyboy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Donate (PayPal)",
|
||||||
|
"value": "<a href=\"https://paypal.me/gleasonator\" rel=\"ugc\">https://paypal.me/gleasonator</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$BTC",
|
||||||
|
"value": "bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$ETH",
|
||||||
|
"value": "0xAc9aB5Fc04Dc1cB1789Af75b523Bd23C70B2D717"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$DOGE",
|
||||||
|
"value": "D5zVZs6jrRakaPVGiErkQiHt9sayzm6V5D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$XMR",
|
||||||
|
"value": "45JDCLrjJ4bgVUSbbs2yjy9m5Mf4VLPW8fG7jw9sq5u69rXZZopQogZNeyYkMBnXpkaip4p4QwaaJNhdTotPa9g44DBCzdK"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"followers_count": 2390,
|
||||||
|
"following_count": 1574,
|
||||||
|
"fqn": "alex@gleasonator.com",
|
||||||
|
"header": "https://media.gleasonator.com/accounts/headers/000/000/001/original/9d0e4dbf1c9dbc8f.png",
|
||||||
|
"header_static": "https://media.gleasonator.com/accounts/headers/000/000/001/original/9d0e4dbf1c9dbc8f.png",
|
||||||
|
"id": "9v5bmRalQvjOy0ECcC",
|
||||||
|
"last_status_at": "2022-02-23T17:54:41",
|
||||||
|
"locked": false,
|
||||||
|
"note": "I create Fediverse software that empowers people online.<br/><br/>I'm vegan btw<br/><br/>Note: If you have a question for me, please tag me publicly. This gives the opportunity for others to chime in, and bystanders to learn.",
|
||||||
|
"pleroma": {
|
||||||
|
"accepts_chat_messages": true,
|
||||||
|
"also_known_as": [
|
||||||
|
"https://mitra.social/users/alex"
|
||||||
|
],
|
||||||
|
"ap_id": "https://gleasonator.com/users/alex",
|
||||||
|
"background_image": null,
|
||||||
|
"birthday": "1993-07-03",
|
||||||
|
"favicon": "https://gleasonator.com/favicon.png",
|
||||||
|
"hide_favorites": true,
|
||||||
|
"hide_followers": false,
|
||||||
|
"hide_followers_count": false,
|
||||||
|
"hide_follows": false,
|
||||||
|
"hide_follows_count": false,
|
||||||
|
"is_admin": true,
|
||||||
|
"is_confirmed": true,
|
||||||
|
"is_moderator": false,
|
||||||
|
"is_suggested": true,
|
||||||
|
"relationship": {},
|
||||||
|
"skip_thread_containment": false,
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "Website",
|
||||||
|
"value": "https://alexgleason.me"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Soapbox",
|
||||||
|
"value": "https://soapbox.pub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Email",
|
||||||
|
"value": "alex@alexgleason.me"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gender identity",
|
||||||
|
"value": "Soyboy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Donate (PayPal)",
|
||||||
|
"value": "https://paypal.me/gleasonator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$BTC",
|
||||||
|
"value": "bc1q9cx35adpm73aq2fw40ye6ts8hfxqzjr5unwg0n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$ETH",
|
||||||
|
"value": "0xAc9aB5Fc04Dc1cB1789Af75b523Bd23C70B2D717"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$DOGE",
|
||||||
|
"value": "D5zVZs6jrRakaPVGiErkQiHt9sayzm6V5D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "$XMR",
|
||||||
|
"value": "45JDCLrjJ4bgVUSbbs2yjy9m5Mf4VLPW8fG7jw9sq5u69rXZZopQogZNeyYkMBnXpkaip4p4QwaaJNhdTotPa9g44DBCzdK"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"note": "I create Fediverse software that empowers people online.\r\n\r\nI'm vegan btw\r\n\r\nNote: If you have a question for me, please tag me publicly. This gives the opportunity for others to chime in, and bystanders to learn.",
|
||||||
|
"pleroma": {
|
||||||
|
"actor_type": "Person",
|
||||||
|
"discoverable": false
|
||||||
|
},
|
||||||
|
"sensitive": false
|
||||||
|
},
|
||||||
|
"statuses_count": 23502,
|
||||||
|
"url": "https://gleasonator.com/users/alex",
|
||||||
|
"username": "alex"
|
||||||
|
},
|
||||||
|
"application": null,
|
||||||
|
"bookmarked": false,
|
||||||
|
"card": null,
|
||||||
|
"content": "<p>What is tolerance?</p>",
|
||||||
|
"created_at": "2020-03-23T19:33:06.000Z",
|
||||||
|
"emojis": [],
|
||||||
|
"favourited": false,
|
||||||
|
"favourites_count": 47,
|
||||||
|
"id": "103874034847713213",
|
||||||
|
"in_reply_to_account_id": null,
|
||||||
|
"in_reply_to_id": null,
|
||||||
|
"language": null,
|
||||||
|
"media_attachments": [],
|
||||||
|
"mentions": [],
|
||||||
|
"muted": false,
|
||||||
|
"pinned": true,
|
||||||
|
"pleroma": {
|
||||||
|
"content": {
|
||||||
|
"text/plain": "What is tolerance?"
|
||||||
|
},
|
||||||
|
"conversation_id": "3023268",
|
||||||
|
"direct_conversation_id": null,
|
||||||
|
"emoji_reactions": [
|
||||||
|
{
|
||||||
|
"count": 3,
|
||||||
|
"me": false,
|
||||||
|
"name": "❤️"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expires_at": null,
|
||||||
|
"in_reply_to_account_acct": null,
|
||||||
|
"local": true,
|
||||||
|
"parent_visible": false,
|
||||||
|
"pinned_at": "2021-11-23T01:38:44.000Z",
|
||||||
|
"quote": null,
|
||||||
|
"quote_url": null,
|
||||||
|
"quote_visible": false,
|
||||||
|
"spoiler_text": {
|
||||||
|
"text/plain": ""
|
||||||
|
},
|
||||||
|
"thread_muted": false
|
||||||
|
},
|
||||||
|
"poll": {
|
||||||
|
"emojis": [],
|
||||||
|
"expired": true,
|
||||||
|
"expires_at": "2020-03-24T19:33:06.000Z",
|
||||||
|
"id": "4930",
|
||||||
|
"multiple": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"title": "Banning, censoring, and deplatforming anyone you disagree with",
|
||||||
|
"votes_count": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Promoting free speech, even for people and ideas you dislike",
|
||||||
|
"votes_count": 36
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"voters_count": 2,
|
||||||
|
"votes_count": 38
|
||||||
|
},
|
||||||
|
"reblog": null,
|
||||||
|
"reblogged": false,
|
||||||
|
"reblogs_count": 26,
|
||||||
|
"replies_count": 14,
|
||||||
|
"sensitive": false,
|
||||||
|
"spoiler_text": "",
|
||||||
|
"tags": [],
|
||||||
|
"text": null,
|
||||||
|
"uri": "https://gleasonator.com/users/alex/statuses/103874034847713213",
|
||||||
|
"url": "https://gleasonator.com/notice/103874034847713213",
|
||||||
|
"visibility": "public"
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
import { openModal, closeModal } from './modals';
|
||||||
|
|
||||||
|
export const ACCOUNT_NOTE_SUBMIT_REQUEST = 'ACCOUNT_NOTE_SUBMIT_REQUEST';
|
||||||
|
export const ACCOUNT_NOTE_SUBMIT_SUCCESS = 'ACCOUNT_NOTE_SUBMIT_SUCCESS';
|
||||||
|
export const ACCOUNT_NOTE_SUBMIT_FAIL = 'ACCOUNT_NOTE_SUBMIT_FAIL';
|
||||||
|
|
||||||
|
export const ACCOUNT_NOTE_INIT_MODAL = 'ACCOUNT_NOTE_INIT_MODAL';
|
||||||
|
|
||||||
|
export const ACCOUNT_NOTE_CHANGE_COMMENT = 'ACCOUNT_NOTE_CHANGE_COMMENT';
|
||||||
|
|
||||||
|
export function submitAccountNote() {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
dispatch(submitAccountNoteRequest());
|
||||||
|
|
||||||
|
const id = getState().getIn(['account_notes', 'edit', 'account_id']);
|
||||||
|
|
||||||
|
api(getState).post(`/api/v1/accounts/${id}/note`, {
|
||||||
|
comment: getState().getIn(['account_notes', 'edit', 'comment']),
|
||||||
|
}).then(response => {
|
||||||
|
dispatch(closeModal());
|
||||||
|
dispatch(submitAccountNoteSuccess(response.data));
|
||||||
|
}).catch(error => dispatch(submitAccountNoteFail(error)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitAccountNoteRequest() {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_NOTE_SUBMIT_REQUEST,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitAccountNoteSuccess(relationship) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_NOTE_SUBMIT_SUCCESS,
|
||||||
|
relationship,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitAccountNoteFail(error) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_NOTE_SUBMIT_FAIL,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initAccountNoteModal(account) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const comment = getState().getIn(['relationships', account.get('id'), 'note']);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACCOUNT_NOTE_INIT_MODAL,
|
||||||
|
account,
|
||||||
|
comment,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(openModal('ACCOUNT_NOTE'));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeAccountNoteComment(comment) {
|
||||||
|
return {
|
||||||
|
type: ACCOUNT_NOTE_CHANGE_COMMENT,
|
||||||
|
comment,
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ import { getSettings } from '../settings';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
normalizeAccount,
|
normalizeAccount,
|
||||||
normalizeStatus,
|
|
||||||
normalizePoll,
|
normalizePoll,
|
||||||
} from './normalizer';
|
} from './normalizer';
|
||||||
|
|
||||||
|
@ -22,11 +21,17 @@ export function importAccounts(accounts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importStatus(status, idempotencyKey) {
|
export function importStatus(status, idempotencyKey) {
|
||||||
return { type: STATUS_IMPORT, status, idempotencyKey };
|
return (dispatch, getState) => {
|
||||||
|
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
|
||||||
|
return dispatch({ type: STATUS_IMPORT, status, idempotencyKey, expandSpoilers });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importStatuses(statuses) {
|
export function importStatuses(statuses) {
|
||||||
return { type: STATUSES_IMPORT, statuses };
|
return (dispatch, getState) => {
|
||||||
|
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
|
||||||
|
return dispatch({ type: STATUSES_IMPORT, statuses, expandSpoilers });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importPolls(polls) {
|
export function importPolls(polls) {
|
||||||
|
@ -60,11 +65,6 @@ export function importFetchedStatus(status, idempotencyKey) {
|
||||||
// Skip broken statuses
|
// Skip broken statuses
|
||||||
if (isBroken(status)) return;
|
if (isBroken(status)) return;
|
||||||
|
|
||||||
const normalOldStatus = getState().getIn(['statuses', status.id]);
|
|
||||||
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
|
|
||||||
|
|
||||||
const normalizedStatus = normalizeStatus(status, normalOldStatus, expandSpoilers);
|
|
||||||
|
|
||||||
if (status.reblog?.id) {
|
if (status.reblog?.id) {
|
||||||
dispatch(importFetchedStatus(status.reblog));
|
dispatch(importFetchedStatus(status.reblog));
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ export function importFetchedStatus(status, idempotencyKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(importFetchedAccount(status.account));
|
dispatch(importFetchedAccount(status.account));
|
||||||
dispatch(importStatus(normalizedStatus, idempotencyKey));
|
dispatch(importStatus(status, idempotencyKey));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +113,7 @@ export function importFetchedStatuses(statuses) {
|
||||||
// Skip broken statuses
|
// Skip broken statuses
|
||||||
if (isBroken(status)) return;
|
if (isBroken(status)) return;
|
||||||
|
|
||||||
const normalOldStatus = getState().getIn(['statuses', status.id]);
|
normalStatuses.push(status);
|
||||||
const expandSpoilers = getSettings(getState()).get('expandSpoilers');
|
|
||||||
|
|
||||||
normalStatuses.push(normalizeStatus(status, normalOldStatus, expandSpoilers));
|
|
||||||
accounts.push(status.account);
|
accounts.push(status.account);
|
||||||
|
|
||||||
if (status.reblog?.id) {
|
if (status.reblog?.id) {
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import escapeTextContentForBrowser from 'escape-html';
|
import escapeTextContentForBrowser from 'escape-html';
|
||||||
|
|
||||||
import { stripCompatibilityFeatures } from 'soapbox/utils/html';
|
|
||||||
|
|
||||||
import emojify from '../../features/emoji/emoji';
|
import emojify from '../../features/emoji/emoji';
|
||||||
import { unescapeHTML } from '../../utils/html';
|
import { unescapeHTML } from '../../utils/html';
|
||||||
|
|
||||||
const domParser = new DOMParser();
|
|
||||||
|
|
||||||
const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
|
const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
|
||||||
obj[`:${emoji.shortcode}:`] = emoji;
|
obj[`:${emoji.shortcode}:`] = emoji;
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -45,61 +41,6 @@ export function normalizeAccount(account) {
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeStatus(status, normalOldStatus, expandSpoilers) {
|
|
||||||
const normalStatus = { ...status };
|
|
||||||
|
|
||||||
// Some backends can return null, or omit these required fields
|
|
||||||
if (!normalStatus.emojis) normalStatus.emojis = [];
|
|
||||||
if (!normalStatus.spoiler_text) normalStatus.spoiler_text = '';
|
|
||||||
|
|
||||||
// Copy the pleroma object too, so we can modify our copy
|
|
||||||
if (status.pleroma) {
|
|
||||||
normalStatus.pleroma = { ...status.pleroma };
|
|
||||||
}
|
|
||||||
|
|
||||||
normalStatus.account = status.account.id;
|
|
||||||
|
|
||||||
if (status.reblog?.id) {
|
|
||||||
normalStatus.reblog = status.reblog.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.poll?.id) {
|
|
||||||
normalStatus.poll = status.poll.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.pleroma?.quote?.id) {
|
|
||||||
// Normalize quote to the top-level, so delete the original for performance
|
|
||||||
normalStatus.quote = status.pleroma.quote.id;
|
|
||||||
delete normalStatus.pleroma.quote;
|
|
||||||
} else if (status.quote?.id) {
|
|
||||||
// Fedibird compatibility, because why not
|
|
||||||
normalStatus.quote = status.quote.id;
|
|
||||||
} else if (status.quote_id) {
|
|
||||||
// Fedibird: fall back to quote_id
|
|
||||||
normalStatus.quote = status.quote_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only calculate these values when status first encountered
|
|
||||||
// Otherwise keep the ones already in the reducer
|
|
||||||
if (normalOldStatus) {
|
|
||||||
normalStatus.search_index = normalOldStatus.get('search_index');
|
|
||||||
normalStatus.contentHtml = normalOldStatus.get('contentHtml');
|
|
||||||
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
|
|
||||||
normalStatus.hidden = normalOldStatus.get('hidden');
|
|
||||||
} else {
|
|
||||||
const spoilerText = normalStatus.spoiler_text || '';
|
|
||||||
const searchContent = ([spoilerText, status.content].concat((status.poll?.options) ? status.poll.options.map(option => option.title) : [])).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
|
||||||
const emojiMap = makeEmojiMap(normalStatus);
|
|
||||||
|
|
||||||
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
|
|
||||||
normalStatus.contentHtml = stripCompatibilityFeatures(emojify(normalStatus.content, emojiMap));
|
|
||||||
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
|
||||||
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizePoll(poll) {
|
export function normalizePoll(poll) {
|
||||||
const normalPoll = { ...poll };
|
const normalPoll = { ...poll };
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ export const makeDefaultConfig = features => {
|
||||||
logo: '',
|
logo: '',
|
||||||
banner: '',
|
banner: '',
|
||||||
brandColor: '', // Empty
|
brandColor: '', // Empty
|
||||||
|
accentColor: '',
|
||||||
customCss: ImmutableList(),
|
customCss: ImmutableList(),
|
||||||
promoPanel: ImmutableMap({
|
promoPanel: ImmutableMap({
|
||||||
items: ImmutableList(),
|
items: ImmutableList(),
|
||||||
|
@ -59,6 +60,8 @@ export const makeDefaultConfig = features => {
|
||||||
}),
|
}),
|
||||||
aboutPages: ImmutableMap(),
|
aboutPages: ImmutableMap(),
|
||||||
authenticatedProfile: true,
|
authenticatedProfile: true,
|
||||||
|
singleUserMode: false,
|
||||||
|
singleUserModeProfile: '',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import Icon from 'soapbox/components/icon';
|
import Icon from 'soapbox/components/icon';
|
||||||
import { captureException } from 'soapbox/monitoring';
|
import { captureException } from 'soapbox/monitoring';
|
||||||
|
import sourceCode from 'soapbox/utils/code';
|
||||||
|
|
||||||
export default class ErrorBoundary extends React.PureComponent {
|
export default class ErrorBoundary extends React.PureComponent {
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||||
<Icon src={require('@tabler/icons/icons/mood-sad.svg')} className='sad-face' />
|
<Icon src={require('@tabler/icons/icons/mood-sad.svg')} className='sad-face' />
|
||||||
<FormattedMessage id='alert.unexpected.message' defaultMessage='An unexpected error occurred.' />
|
<FormattedMessage id='alert.unexpected.message' defaultMessage='An unexpected error occurred.' />
|
||||||
<div className='return-home'>
|
<div className='return-home'>
|
||||||
<a a href='/'>
|
<a href='/'>
|
||||||
<Icon src={require('@tabler/icons/icons/arrow-back.svg')} />
|
<Icon src={require('@tabler/icons/icons/arrow-back.svg')} />
|
||||||
<FormattedMessage id='alert.unexpected.return_home' defaultMessage='Return Home' />
|
<FormattedMessage id='alert.unexpected.return_home' defaultMessage='Return Home' />
|
||||||
</a>
|
</a>
|
||||||
|
@ -87,6 +88,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||||
{browser && <p className='error-boundary__browser'>
|
{browser && <p className='error-boundary__browser'>
|
||||||
{browser.getBrowserName()} {browser.getBrowserVersion()}
|
{browser.getBrowserName()} {browser.getBrowserVersion()}
|
||||||
</p>}
|
</p>}
|
||||||
|
<p className='error-boundary__version'>{sourceCode.displayName} {sourceCode.version}</p>
|
||||||
<p className='help-text'>
|
<p className='help-text'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='alert.unexpected.help_text'
|
id='alert.unexpected.help_text'
|
||||||
|
|
|
@ -440,7 +440,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
||||||
href: `/pleroma/admin/#/users/${status.getIn(['account', 'id'])}/`,
|
href: `/pleroma/admin/#/users/${status.getIn(['account', 'id'])}/`,
|
||||||
icon: require('icons/gavel.svg'),
|
icon: require('@tabler/icons/icons/gavel.svg'),
|
||||||
});
|
});
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.admin_status),
|
text: intl.formatMessage(messages.admin_status),
|
||||||
|
|
|
@ -43,27 +43,8 @@ class StatusReplyMentions extends ImmutablePureComponent {
|
||||||
const to = status.get('mentions', []);
|
const to = status.get('mentions', []);
|
||||||
|
|
||||||
// The post is a reply, but it has no mentions.
|
// The post is a reply, but it has no mentions.
|
||||||
|
// Rare, but it can happen.
|
||||||
if (to.size === 0) {
|
if (to.size === 0) {
|
||||||
// The author is replying to themself.
|
|
||||||
if (status.get('in_reply_to_account_id') === status.getIn(['account', 'id'])) {
|
|
||||||
return (
|
|
||||||
<div className='reply-mentions'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='reply_mentions.reply'
|
|
||||||
defaultMessage='Replying to {accounts}{more}'
|
|
||||||
values={{
|
|
||||||
accounts: (<>
|
|
||||||
<HoverRefWrapper accountId={status.getIn(['account', 'id'])} inline>
|
|
||||||
<Link to={`/@${status.getIn(['account', 'acct'])}`} className='reply-mentions__account'>@{status.getIn(['account', 'username'])}</Link>
|
|
||||||
</HoverRefWrapper>
|
|
||||||
</>),
|
|
||||||
more: false,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// The reply-to is unknown. Rare, but it can happen.
|
|
||||||
return (
|
return (
|
||||||
<div className='reply-mentions'>
|
<div className='reply-mentions'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
@ -73,8 +54,6 @@ class StatusReplyMentions extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The typical case with a reply-to and a list of mentions.
|
// The typical case with a reply-to and a list of mentions.
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -6,7 +6,7 @@ import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
import { Provider, connect } from 'react-redux';
|
import { Provider, connect } from 'react-redux';
|
||||||
import { Switch, BrowserRouter, Route } from 'react-router-dom';
|
import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom';
|
||||||
import { ScrollContext } from 'react-router-scroll-4';
|
import { ScrollContext } from 'react-router-scroll-4';
|
||||||
|
|
||||||
// import Introduction from '../features/introduction';
|
// import Introduction from '../features/introduction';
|
||||||
|
@ -64,6 +64,9 @@ const mapStateToProps = (state) => {
|
||||||
|
|
||||||
// In demo mode, force the default brand color
|
// In demo mode, force the default brand color
|
||||||
const brandColor = settings.get('demo') ? '#0482d8' : soapboxConfig.get('brandColor');
|
const brandColor = settings.get('demo') ? '#0482d8' : soapboxConfig.get('brandColor');
|
||||||
|
const accentColor = settings.get('demo') ? null : soapboxConfig.get('accentColor');
|
||||||
|
|
||||||
|
const singleUserMode = soapboxConfig.get('singleUserMode') && soapboxConfig.get('singleUserModeProfile');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
showIntroduction,
|
showIntroduction,
|
||||||
|
@ -75,11 +78,12 @@ const mapStateToProps = (state) => {
|
||||||
dyslexicFont: settings.get('dyslexicFont'),
|
dyslexicFont: settings.get('dyslexicFont'),
|
||||||
demetricator: settings.get('demetricator'),
|
demetricator: settings.get('demetricator'),
|
||||||
locale: validLocale(locale) ? locale : 'en',
|
locale: validLocale(locale) ? locale : 'en',
|
||||||
themeCss: generateThemeCss(brandColor),
|
themeCss: generateThemeCss(brandColor, accentColor),
|
||||||
brandColor: soapboxConfig.get('brandColor'),
|
brandColor: soapboxConfig.get('brandColor'),
|
||||||
themeMode: settings.get('themeMode'),
|
themeMode: settings.get('themeMode'),
|
||||||
halloween: settings.get('halloween'),
|
halloween: settings.get('halloween'),
|
||||||
customCss: settings.get('demo') ? null : soapboxConfig.get('customCss'),
|
customCss: settings.get('demo') ? null : soapboxConfig.get('customCss'),
|
||||||
|
singleUserMode,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,6 +106,7 @@ class SoapboxMount extends React.PureComponent {
|
||||||
customCss: ImmutablePropTypes.list,
|
customCss: ImmutablePropTypes.list,
|
||||||
halloween: PropTypes.bool,
|
halloween: PropTypes.bool,
|
||||||
dispatch: PropTypes.func,
|
dispatch: PropTypes.func,
|
||||||
|
singleUserMode: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -134,7 +139,7 @@ class SoapboxMount extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { me, instanceLoaded, themeCss, locale, customCss } = this.props;
|
const { me, instanceLoaded, themeCss, locale, customCss, singleUserMode } = this.props;
|
||||||
if (me === null) return null;
|
if (me === null) return null;
|
||||||
if (!instanceLoaded) return null;
|
if (!instanceLoaded) return null;
|
||||||
if (this.state.localeLoading) return null;
|
if (this.state.localeLoading) return null;
|
||||||
|
@ -170,7 +175,9 @@ class SoapboxMount extends React.PureComponent {
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||||
<Switch>
|
<Switch>
|
||||||
{!me && <Route exact path='/' component={PublicLayout} />}
|
{!me && (singleUserMode
|
||||||
|
? <Redirect exact from='/' to={`/${singleUserMode}`} />
|
||||||
|
: <Route exact path='/' component={PublicLayout} />)}
|
||||||
<Route exact path='/about/:slug?' component={PublicLayout} />
|
<Route exact path='/about/:slug?' component={PublicLayout} />
|
||||||
<Route path='/' component={UI} />
|
<Route path='/' component={UI} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
@ -62,6 +62,8 @@ const messages = defineMessages({
|
||||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||||
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
|
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
|
||||||
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
|
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
|
||||||
|
createNote: { id: 'account.create_note', defaultMessage: 'Create a note' },
|
||||||
|
editNote: { id: 'account.edit_note', defaultMessage: 'Edit note' },
|
||||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||||
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
|
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
|
||||||
deactivateUser: { id: 'admin.users.actions.deactivate_user', defaultMessage: 'Deactivate @{name}' },
|
deactivateUser: { id: 'admin.users.actions.deactivate_user', defaultMessage: 'Deactivate @{name}' },
|
||||||
|
@ -268,6 +270,14 @@ class Header extends ImmutablePureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (features.notes) {
|
||||||
|
menu.push({
|
||||||
|
text: intl.formatMessage(account.getIn(['relationship', 'note']) ? messages.editNote : messages.createNote),
|
||||||
|
action: this.props.onShowNote,
|
||||||
|
icon: require('@tabler/icons/icons/note.svg'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (account.getIn(['relationship', 'following'])) {
|
if (account.getIn(['relationship', 'following'])) {
|
||||||
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
||||||
menu.push({
|
menu.push({
|
||||||
|
@ -400,7 +410,7 @@ class Header extends ImmutablePureComponent {
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.admin_account, { name: account.get('username') }),
|
text: intl.formatMessage(messages.admin_account, { name: account.get('username') }),
|
||||||
href: `/pleroma/admin/#/users/${account.get('id')}/`, newTab: true,
|
href: `/pleroma/admin/#/users/${account.get('id')}/`, newTab: true,
|
||||||
icon: require('icons/gavel.svg'),
|
icon: require('@tabler/icons/icons/gavel.svg'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,10 @@ export default class Header extends ImmutablePureComponent {
|
||||||
this.props.onUnsuggestUser(this.props.account);
|
this.props.onUnsuggestUser(this.props.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleShowNote = () => {
|
||||||
|
this.props.onShowNote(this.props.account);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { account, identity_proofs } = this.props;
|
const { account, identity_proofs } = this.props;
|
||||||
const moved = (account) ? account.get('moved') : false;
|
const moved = (account) ? account.get('moved') : false;
|
||||||
|
@ -165,6 +169,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onDemoteToUser={this.handleDemoteToUser}
|
onDemoteToUser={this.handleDemoteToUser}
|
||||||
onSuggestUser={this.handleSuggestUser}
|
onSuggestUser={this.handleSuggestUser}
|
||||||
onUnsuggestUser={this.handleUnsuggestUser}
|
onUnsuggestUser={this.handleUnsuggestUser}
|
||||||
|
onShowNote={this.handleShowNote}
|
||||||
username={this.props.username}
|
username={this.props.username}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,21 +3,7 @@ import React from 'react';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import { initAccountNoteModal } from 'soapbox/actions/account_notes';
|
||||||
verifyUser,
|
|
||||||
unverifyUser,
|
|
||||||
promoteToAdmin,
|
|
||||||
promoteToModerator,
|
|
||||||
demoteToUser,
|
|
||||||
suggestUsers,
|
|
||||||
unsuggestUsers,
|
|
||||||
} from 'soapbox/actions/admin';
|
|
||||||
import { launchChat } from 'soapbox/actions/chats';
|
|
||||||
import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation';
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
|
||||||
import { isAdmin } from 'soapbox/utils/accounts';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
followAccount,
|
followAccount,
|
||||||
unfollowAccount,
|
unfollowAccount,
|
||||||
|
@ -28,16 +14,31 @@ import {
|
||||||
unpinAccount,
|
unpinAccount,
|
||||||
subscribeAccount,
|
subscribeAccount,
|
||||||
unsubscribeAccount,
|
unsubscribeAccount,
|
||||||
} from '../../../actions/accounts';
|
} from 'soapbox/actions/accounts';
|
||||||
|
import {
|
||||||
|
verifyUser,
|
||||||
|
unverifyUser,
|
||||||
|
promoteToAdmin,
|
||||||
|
promoteToModerator,
|
||||||
|
demoteToUser,
|
||||||
|
suggestUsers,
|
||||||
|
unsuggestUsers,
|
||||||
|
} from 'soapbox/actions/admin';
|
||||||
|
import { launchChat } from 'soapbox/actions/chats';
|
||||||
import {
|
import {
|
||||||
mentionCompose,
|
mentionCompose,
|
||||||
directCompose,
|
directCompose,
|
||||||
} from '../../../actions/compose';
|
} from 'soapbox/actions/compose';
|
||||||
import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
|
import { blockDomain, unblockDomain } from 'soapbox/actions/domain_blocks';
|
||||||
import { openModal } from '../../../actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { initMuteModal } from '../../../actions/mutes';
|
import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation';
|
||||||
import { initReport } from '../../../actions/reports';
|
import { initMuteModal } from 'soapbox/actions/mutes';
|
||||||
import { makeGetAccount } from '../../../selectors';
|
import { initReport } from 'soapbox/actions/reports';
|
||||||
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
|
import { makeGetAccount } from 'soapbox/selectors';
|
||||||
|
import { isAdmin } from 'soapbox/utils/accounts';
|
||||||
|
|
||||||
import Header from '../components/header';
|
import Header from '../components/header';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -246,6 +247,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
.then(() => dispatch(snackbar.success(message)))
|
.then(() => dispatch(snackbar.success(message)))
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShowNote(account) {
|
||||||
|
dispatch(initAccountNoteModal(account));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
|
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
|
||||||
|
|
|
@ -35,7 +35,7 @@ class AdminNav extends React.PureComponent {
|
||||||
<FormattedMessage id='admin_nav.dashboard' defaultMessage='Dashboard' />
|
<FormattedMessage id='admin_nav.dashboard' defaultMessage='Dashboard' />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink className='promo-panel-item' to='/admin/reports'>
|
<NavLink className='promo-panel-item' to='/admin/reports'>
|
||||||
<IconWithCounter src={require('icons/gavel.svg')} count={reportsCount} />
|
<IconWithCounter src={require('@tabler/icons/icons/gavel.svg')} count={reportsCount} />
|
||||||
<FormattedMessage id='admin_nav.reports' defaultMessage='Reports' />
|
<FormattedMessage id='admin_nav.reports' defaultMessage='Reports' />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
{((instance.get('registrations') && instance.get('approval_required')) || approvalCount > 0) && (
|
{((instance.get('registrations') && instance.get('approval_required')) || approvalCount > 0) && (
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { fromJS } from 'immutable';
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
import { normalizeStatus } from 'soapbox/actions/importer/normalizer';
|
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||||
|
import { calculateStatus } from 'soapbox/reducers/statuses';
|
||||||
import { makeGetAccount } from 'soapbox/selectors';
|
import { makeGetAccount } from 'soapbox/selectors';
|
||||||
|
|
||||||
export const buildStatus = (state, scheduledStatus) => {
|
export const buildStatus = (state, scheduledStatus) => {
|
||||||
|
@ -10,37 +11,19 @@ export const buildStatus = (state, scheduledStatus) => {
|
||||||
const params = scheduledStatus.get('params');
|
const params = scheduledStatus.get('params');
|
||||||
const account = getAccount(state, me);
|
const account = getAccount(state, me);
|
||||||
|
|
||||||
const status = normalizeStatus({
|
const status = ImmutableMap({
|
||||||
account,
|
account,
|
||||||
application: null,
|
|
||||||
bookmarked: false,
|
|
||||||
card: null,
|
|
||||||
content: params.get('text', '').replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
content: params.get('text', '').replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
||||||
created_at: params.get('scheduled_at'),
|
created_at: params.get('scheduled_at'),
|
||||||
emojis: [],
|
|
||||||
favourited: false,
|
|
||||||
favourites_count: 0,
|
|
||||||
id: scheduledStatus.get('id'),
|
id: scheduledStatus.get('id'),
|
||||||
in_reply_to_account_id: null,
|
|
||||||
in_reply_to_id: params.get('in_reply_to_id'),
|
in_reply_to_id: params.get('in_reply_to_id'),
|
||||||
language: null,
|
|
||||||
media_attachments: scheduledStatus.get('media_attachments'),
|
media_attachments: scheduledStatus.get('media_attachments'),
|
||||||
mentions: [],
|
|
||||||
muted: false,
|
|
||||||
pinned: false,
|
|
||||||
poll: params.get('poll'),
|
poll: params.get('poll'),
|
||||||
reblog: null,
|
|
||||||
reblogged: false,
|
|
||||||
reblogs_count: 0,
|
|
||||||
replies_count: 0,
|
|
||||||
sensitive: params.get('sensitive'),
|
sensitive: params.get('sensitive'),
|
||||||
spoiler_text: '',
|
|
||||||
tags: [],
|
|
||||||
text: null,
|
|
||||||
uri: `/scheduled_statuses/${scheduledStatus.get('id')}`,
|
uri: `/scheduled_statuses/${scheduledStatus.get('id')}`,
|
||||||
url: `/scheduled_statuses/${scheduledStatus.get('id')}`,
|
url: `/scheduled_statuses/${scheduledStatus.get('id')}`,
|
||||||
visibility: params.get('visibility'),
|
visibility: params.get('visibility'),
|
||||||
});
|
});
|
||||||
|
|
||||||
return fromJS(status).set('account', account);
|
return calculateStatus(normalizeStatus(status));
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ import Avatar from 'soapbox/components/avatar';
|
||||||
import DisplayName from 'soapbox/components/display_name';
|
import DisplayName from 'soapbox/components/display_name';
|
||||||
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
|
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
|
||||||
import StatusContent from 'soapbox/components/status_content';
|
import StatusContent from 'soapbox/components/status_content';
|
||||||
|
import StatusReplyMentions from 'soapbox/components/status_reply_mentions';
|
||||||
import PollPreview from 'soapbox/features/ui/components/poll_preview';
|
import PollPreview from 'soapbox/features/ui/components/poll_preview';
|
||||||
import { getDomain } from 'soapbox/utils/accounts';
|
import { getDomain } from 'soapbox/utils/accounts';
|
||||||
|
|
||||||
|
@ -63,6 +64,8 @@ class ScheduledStatus extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<StatusReplyMentions status={status} />
|
||||||
|
|
||||||
<StatusContent
|
<StatusContent
|
||||||
status={status}
|
status={status}
|
||||||
expanded
|
expanded
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import { defaultSettings } from 'soapbox/actions/settings';
|
import { defaultSettings } from 'soapbox/actions/settings';
|
||||||
import { brandColorToCSS } from 'soapbox/utils/theme';
|
import { themeColorsToCSS } from 'soapbox/utils/theme';
|
||||||
|
|
||||||
export default function SitePreview({ soapbox }) {
|
export default function SitePreview({ soapbox }) {
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ export default function SitePreview({ soapbox }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={bodyClass}>
|
<div className={bodyClass}>
|
||||||
<style>{`.site-preview {${brandColorToCSS(soapbox.get('brandColor'))}}`}</style>
|
<style>{`.site-preview {${themeColorsToCSS(soapbox.get('brandColor'), soapbox.get('accentColor'))}}`}</style>
|
||||||
<div className='app-holder'>
|
<div className='app-holder'>
|
||||||
<div>
|
<div>
|
||||||
<div className='ui'>
|
<div className='ui'>
|
||||||
|
|
|
@ -56,6 +56,10 @@ const messages = defineMessages({
|
||||||
promoPanelIconsLink: { id: 'soapbox_config.hints.promo_panel_icons.link', defaultMessage: 'Soapbox Icons List' },
|
promoPanelIconsLink: { id: 'soapbox_config.hints.promo_panel_icons.link', defaultMessage: 'Soapbox Icons List' },
|
||||||
authenticatedProfileLabel: { id: 'soapbox_config.authenticated_profile_label', defaultMessage: 'Profiles require authentication' },
|
authenticatedProfileLabel: { id: 'soapbox_config.authenticated_profile_label', defaultMessage: 'Profiles require authentication' },
|
||||||
authenticatedProfileHint: { id: 'soapbox_config.authenticated_profile_hint', defaultMessage: 'Users must be logged-in to view replies and media on user profiles.' },
|
authenticatedProfileHint: { id: 'soapbox_config.authenticated_profile_hint', defaultMessage: 'Users must be logged-in to view replies and media on user profiles.' },
|
||||||
|
singleUserModeLabel: { id: 'soapbox_config.single_user_mode_label', defaultMessage: 'Single user mode' },
|
||||||
|
singleUserModeHint: { id: 'soapbox_config.single_user_mode_hint', defaultMessage: 'Front page will redirect to a given user profile.' },
|
||||||
|
singleUserModeProfileLabel: { id: 'soapbox_config.single_user_mode_profile_label', defaultMessage: 'Main user handle' },
|
||||||
|
singleUserModeProfileHint: { id: 'soapbox_config.single_user_mode_profile_hint', defaultMessage: '@handle' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||||
|
@ -235,6 +239,12 @@ class SoapboxConfig extends ImmutablePureComponent {
|
||||||
value={soapbox.get('brandColor')}
|
value={soapbox.get('brandColor')}
|
||||||
onChange={this.handleChange(['brandColor'], (e) => e.hex)}
|
onChange={this.handleChange(['brandColor'], (e) => e.hex)}
|
||||||
/>
|
/>
|
||||||
|
<ColorWithPicker
|
||||||
|
buttonId='accent_color'
|
||||||
|
label={<FormattedMessage id='soapbox_config.fields.accent_color_label' defaultMessage='Accent color' />}
|
||||||
|
value={soapbox.get('accentColor')}
|
||||||
|
onChange={this.handleChange(['accentColor'], (e) => e.hex)}
|
||||||
|
/>
|
||||||
<div className='input with_label toggle'>
|
<div className='input with_label toggle'>
|
||||||
<div className='label_input'>
|
<div className='label_input'>
|
||||||
<label><FormattedMessage id='soapbox_config.fields.theme_label' defaultMessage='Default theme' /></label>
|
<label><FormattedMessage id='soapbox_config.fields.theme_label' defaultMessage='Default theme' /></label>
|
||||||
|
@ -291,6 +301,22 @@ class SoapboxConfig extends ImmutablePureComponent {
|
||||||
checked={soapbox.get('authenticatedProfile') === true}
|
checked={soapbox.get('authenticatedProfile') === true}
|
||||||
onChange={this.handleChange(['authenticatedProfile'], (e) => e.target.checked)}
|
onChange={this.handleChange(['authenticatedProfile'], (e) => e.target.checked)}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
name='singleUserMode'
|
||||||
|
label={intl.formatMessage(messages.singleUserModeLabel)}
|
||||||
|
hint={intl.formatMessage(messages.singleUserModeHint)}
|
||||||
|
checked={soapbox.get('singleUserMode') === true}
|
||||||
|
onChange={this.handleChange(['singleUserMode'], (e) => e.target.checked)}
|
||||||
|
/>
|
||||||
|
{soapbox.get('singleUserMode') && (
|
||||||
|
<TextInput
|
||||||
|
name='singleUserModeProfile'
|
||||||
|
label={intl.formatMessage(messages.singleUserModeProfileLabel)}
|
||||||
|
placeholder={intl.formatMessage(messages.singleUserModeProfileHint)}
|
||||||
|
value={soapbox.get('singleUserModeProfile')}
|
||||||
|
onChange={this.handleChange(['singleUserModeProfile'], (e) => e.target.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</FieldsGroup>
|
</FieldsGroup>
|
||||||
<FieldsGroup>
|
<FieldsGroup>
|
||||||
<div className='input with_block_label popup'>
|
<div className='input with_block_label popup'>
|
||||||
|
|
|
@ -438,7 +438,7 @@ class ActionBar extends React.PureComponent {
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
|
||||||
href: `/pleroma/admin/#/users/${status.getIn(['account', 'id'])}/`,
|
href: `/pleroma/admin/#/users/${status.getIn(['account', 'id'])}/`,
|
||||||
icon: require('icons/gavel.svg'),
|
icon: require('@tabler/icons/icons/gavel.svg'),
|
||||||
});
|
});
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.admin_status),
|
text: intl.formatMessage(messages.admin_status),
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { changeAccountNoteComment, submitAccountNote } from 'soapbox/actions/account_notes';
|
||||||
|
import { closeModal } from 'soapbox/actions/modals';
|
||||||
|
import Button from 'soapbox/components/button';
|
||||||
|
import Icon from 'soapbox/components/icon';
|
||||||
|
import { makeGetAccount } from 'soapbox/selectors';
|
||||||
|
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||||
|
placeholder: { id: 'account_note.placeholder', defaultMessage: 'No comment provided' },
|
||||||
|
save: { id: 'account_note.save', defaultMessage: 'Save' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const makeMapStateToProps = () => {
|
||||||
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
isSubmitting: state.getIn(['account_notes', 'edit', 'isSubmitting']),
|
||||||
|
account: getAccount(state, state.getIn(['account_notes', 'edit', 'account_id'])),
|
||||||
|
comment: state.getIn(['account_notes', 'edit', 'comment']),
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onConfirm() {
|
||||||
|
dispatch(submitAccountNote());
|
||||||
|
},
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
dispatch(closeModal());
|
||||||
|
},
|
||||||
|
|
||||||
|
onCommentChange(comment) {
|
||||||
|
dispatch(changeAccountNoteComment(comment));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default @connect(makeMapStateToProps, mapDispatchToProps)
|
||||||
|
@injectIntl
|
||||||
|
class AccountNoteModal extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
isSubmitting: PropTypes.bool,
|
||||||
|
account: PropTypes.object.isRequired,
|
||||||
|
onClose: PropTypes.func.isRequired,
|
||||||
|
onConfirm: PropTypes.func.isRequired,
|
||||||
|
onCommentChange: PropTypes.func.isRequired,
|
||||||
|
comment: PropTypes.string,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCommentChange = e => {
|
||||||
|
this.props.onCommentChange(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
this.props.onConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown = e => {
|
||||||
|
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||||
|
this.handleSubmit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { account, isSubmitting, comment, onClose, intl } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='modal-root__modal account-note-modal'>
|
||||||
|
<div className='account-note-modal__header'>
|
||||||
|
<Icon src={require('@tabler/icons/icons/note.svg')} />
|
||||||
|
<FormattedMessage id='account_note.target' defaultMessage='Note for @{target}' values={{ target: account.get('acct') }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='account-note-modal__container'>
|
||||||
|
<p><FormattedMessage id='account_note.hint' defaultMessage='You can keep notes about this user for yourself (this will not be shared with them):' /></p>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
className='setting-text light'
|
||||||
|
placeholder={intl.formatMessage(messages.placeholder)}
|
||||||
|
value={comment}
|
||||||
|
onChange={this.handleCommentChange}
|
||||||
|
onKeyDown={this.handleKeyDown}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='account-note-modal__action-bar'>
|
||||||
|
<Button onClick={onClose} className='account-note-modal__cancel-button'>
|
||||||
|
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
|
||||||
|
</Button>
|
||||||
|
<Button text={intl.formatMessage(messages.save)} onClick={this.handleSubmit} disabled={isSubmitting} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import {
|
||||||
ReblogsModal,
|
ReblogsModal,
|
||||||
MentionsModal,
|
MentionsModal,
|
||||||
BirthdaysModal,
|
BirthdaysModal,
|
||||||
|
AccountNoteModal,
|
||||||
} from '../../../features/ui/util/async-components';
|
} from '../../../features/ui/util/async-components';
|
||||||
import BundleContainer from '../containers/bundle_container';
|
import BundleContainer from '../containers/bundle_container';
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ const MODAL_COMPONENTS = {
|
||||||
'REACTIONS': ReactionsModal,
|
'REACTIONS': ReactionsModal,
|
||||||
'MENTIONS': MentionsModal,
|
'MENTIONS': MentionsModal,
|
||||||
'BIRTHDAYS': BirthdaysModal,
|
'BIRTHDAYS': BirthdaysModal,
|
||||||
|
'ACCOUNT_NOTE': AccountNoteModal,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ModalRoot extends React.PureComponent {
|
export default class ModalRoot extends React.PureComponent {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
import { injectIntl } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import Avatar from 'soapbox/components/avatar';
|
||||||
import DisplayName from 'soapbox/components/display_name';
|
import DisplayName from 'soapbox/components/display_name';
|
||||||
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
|
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
|
||||||
import StatusContent from 'soapbox/components/status_content';
|
import StatusContent from 'soapbox/components/status_content';
|
||||||
|
import StatusReplyMentions from 'soapbox/components/status_reply_mentions';
|
||||||
import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder_card';
|
import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder_card';
|
||||||
import PlaceholderMediaGallery from 'soapbox/features/placeholder/components/placeholder_media_gallery';
|
import PlaceholderMediaGallery from 'soapbox/features/placeholder/components/placeholder_media_gallery';
|
||||||
import QuotedStatus from 'soapbox/features/status/containers/quoted_status_container';
|
import QuotedStatus from 'soapbox/features/status/containers/quoted_status_container';
|
||||||
|
@ -50,56 +51,6 @@ class PendingStatus extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderReplyMentions = () => {
|
|
||||||
const { status } = this.props;
|
|
||||||
|
|
||||||
if (!status.get('in_reply_to_id')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const to = status.get('mentions', []);
|
|
||||||
|
|
||||||
if (to.size === 0) {
|
|
||||||
if (status.get('in_reply_to_account_id') === status.getIn(['account', 'id'])) {
|
|
||||||
return (
|
|
||||||
<div className='reply-mentions'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='reply_mentions.reply'
|
|
||||||
defaultMessage='Replying to {accounts}{more}'
|
|
||||||
values={{
|
|
||||||
accounts: <span className='reply-mentions__account'>@{status.getIn(['account', 'username'])}</span>,
|
|
||||||
more: false,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div className='reply-mentions'>
|
|
||||||
<FormattedMessage id='reply_mentions.reply_empty' defaultMessage='Replying to post' />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='reply-mentions'>
|
|
||||||
<FormattedMessage
|
|
||||||
id='reply_mentions.reply'
|
|
||||||
defaultMessage='Replying to {accounts}{more}'
|
|
||||||
values={{
|
|
||||||
accounts: to.slice(0, 2).map(account => (<>
|
|
||||||
<span key={account.username} className='reply-mentions__account'>@{account.username}</span>
|
|
||||||
{' '}
|
|
||||||
</>)),
|
|
||||||
more: to.size > 2 && <FormattedMessage id='reply_mentions.more' defaultMessage='and {count} more' values={{ count: to.size - 2 }} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { status, className } = this.props;
|
const { status, className } = this.props;
|
||||||
if (!status) return null;
|
if (!status) return null;
|
||||||
|
@ -137,7 +88,7 @@ class PendingStatus extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.renderReplyMentions()}
|
<StatusReplyMentions status={status} />
|
||||||
|
|
||||||
<StatusContent
|
<StatusContent
|
||||||
status={status}
|
status={status}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { initAccountNoteModal } from 'soapbox/actions/account_notes';
|
||||||
import Badge from 'soapbox/components/badge';
|
import Badge from 'soapbox/components/badge';
|
||||||
import Icon from 'soapbox/components/icon';
|
import Icon from 'soapbox/components/icon';
|
||||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||||
|
@ -48,6 +49,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
displayFqn: PropTypes.bool,
|
displayFqn: PropTypes.bool,
|
||||||
|
onShowNote: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
getStaffBadge = () => {
|
getStaffBadge = () => {
|
||||||
|
@ -115,6 +117,13 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleShowNote = e => {
|
||||||
|
const { account, onShowNote } = this.props;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
onShowNote(account);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { account, displayFqn, intl, identity_proofs, username } = this.props;
|
const { account, displayFqn, intl, identity_proofs, username } = this.props;
|
||||||
|
|
||||||
|
@ -187,6 +196,13 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
||||||
|
|
||||||
{this.getBirthday()}
|
{this.getBirthday()}
|
||||||
|
|
||||||
|
{!!account.getIn(['relationship', 'note']) && (
|
||||||
|
<a href='#' className='profile-info-panel-content__note' onClick={this.handleShowNote}>
|
||||||
|
<Icon src={require('@tabler/icons/icons/note.svg')} />
|
||||||
|
<FormattedMessage id='account.show_note' defaultMessage='Show note' />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
<ProfileStats
|
<ProfileStats
|
||||||
className='profile-info-panel-content__stats'
|
className='profile-info-panel-content__stats'
|
||||||
account={account}
|
account={account}
|
||||||
|
@ -250,8 +266,14 @@ const mapStateToProps = (state, { account }) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
onShowNote(account) {
|
||||||
|
dispatch(initAccountNoteModal(account));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default injectIntl(
|
export default injectIntl(
|
||||||
connect(mapStateToProps, null, null, {
|
connect(mapStateToProps, mapDispatchToProps, null, {
|
||||||
forwardRef: true,
|
forwardRef: true,
|
||||||
},
|
},
|
||||||
)(ProfileInfoPanel));
|
)(ProfileInfoPanel));
|
||||||
|
|
|
@ -3,17 +3,21 @@ import React from 'react';
|
||||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
|
const soapboxConfig = getSoapboxConfig(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
siteTitle: state.getIn(['instance', 'title']),
|
siteTitle: state.getIn(['instance', 'title']),
|
||||||
me: state.get('me'),
|
me: state.get('me'),
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const SignUpPanel = ({ siteTitle, me }) => {
|
const SignUpPanel = ({ siteTitle, me, singleUserMode }) => {
|
||||||
if (me) return null;
|
if (me || singleUserMode) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='wtf-panel'>
|
<div className='wtf-panel'>
|
||||||
|
@ -39,6 +43,7 @@ const SignUpPanel = ({ siteTitle, me }) => {
|
||||||
SignUpPanel.propTypes = {
|
SignUpPanel.propTypes = {
|
||||||
siteTitle: PropTypes.string,
|
siteTitle: PropTypes.string,
|
||||||
me: SoapboxPropTypes.me,
|
me: SoapboxPropTypes.me,
|
||||||
|
singleUserMode: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectIntl(connect(mapStateToProps)(SignUpPanel));
|
export default injectIntl(connect(mapStateToProps)(SignUpPanel));
|
||||||
|
|
|
@ -38,6 +38,7 @@ class TabsBar extends React.PureComponent {
|
||||||
dashboardCount: PropTypes.number,
|
dashboardCount: PropTypes.number,
|
||||||
notificationCount: PropTypes.number,
|
notificationCount: PropTypes.number,
|
||||||
chatsCount: PropTypes.number,
|
chatsCount: PropTypes.number,
|
||||||
|
singleUserMode: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -67,7 +68,7 @@ class TabsBar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, account, logo, onOpenCompose, onOpenSidebar, features, dashboardCount, notificationCount, chatsCount } = this.props;
|
const { intl, account, logo, onOpenCompose, onOpenSidebar, features, dashboardCount, notificationCount, chatsCount, singleUserMode } = this.props;
|
||||||
const { collapsed } = this.state;
|
const { collapsed } = this.state;
|
||||||
const showLinks = this.shouldShowLinks();
|
const showLinks = this.shouldShowLinks();
|
||||||
|
|
||||||
|
@ -151,9 +152,11 @@ class TabsBar extends React.PureComponent {
|
||||||
<Link className='tabs-bar__button button' to='/auth/sign_in'>
|
<Link className='tabs-bar__button button' to='/auth/sign_in'>
|
||||||
<FormattedMessage id='account.login' defaultMessage='Log In' />
|
<FormattedMessage id='account.login' defaultMessage='Log In' />
|
||||||
</Link>
|
</Link>
|
||||||
|
{!singleUserMode && (
|
||||||
<Link className='tabs-bar__button button button-alternative-2' to='/'>
|
<Link className='tabs-bar__button button button-alternative-2' to='/'>
|
||||||
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
||||||
</Link>
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -170,6 +173,7 @@ const mapStateToProps = state => {
|
||||||
const approvalCount = state.getIn(['admin', 'awaitingApproval']).count();
|
const approvalCount = state.getIn(['admin', 'awaitingApproval']).count();
|
||||||
const instance = state.get('instance');
|
const instance = state.get('instance');
|
||||||
const settings = getSettings(state);
|
const settings = getSettings(state);
|
||||||
|
const soapboxConfig = getSoapboxConfig(state);
|
||||||
|
|
||||||
// In demo mode, use the Soapbox logo
|
// In demo mode, use the Soapbox logo
|
||||||
const logo = settings.get('demo') ? require('images/soapbox-logo.svg') : getSoapboxConfig(state).get('logo');
|
const logo = settings.get('demo') ? require('images/soapbox-logo.svg') : getSoapboxConfig(state).get('logo');
|
||||||
|
@ -181,6 +185,7 @@ const mapStateToProps = state => {
|
||||||
notificationCount: state.getIn(['notifications', 'unread']),
|
notificationCount: state.getIn(['notifications', 'unread']),
|
||||||
chatsCount: state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0),
|
chatsCount: state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0),
|
||||||
dashboardCount: reportsCount + approvalCount,
|
dashboardCount: reportsCount + approvalCount,
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { remoteInteraction } from 'soapbox/actions/interactions';
|
import { remoteInteraction } from 'soapbox/actions/interactions';
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
|
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
import IconButton from 'soapbox/components/icon_button';
|
import IconButton from 'soapbox/components/icon_button';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
|
@ -19,12 +20,14 @@ const messages = defineMessages({
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const instance = state.get('instance');
|
const instance = state.get('instance');
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
const soapboxConfig = getSoapboxConfig(state);
|
||||||
|
|
||||||
if (props.action !== 'FOLLOW') {
|
if (props.action !== 'FOLLOW') {
|
||||||
return {
|
return {
|
||||||
features,
|
features,
|
||||||
siteTitle: state.getIn(['instance', 'title']),
|
siteTitle: state.getIn(['instance', 'title']),
|
||||||
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +38,7 @@ const mapStateToProps = (state, props) => {
|
||||||
siteTitle: state.getIn(['instance', 'title']),
|
siteTitle: state.getIn(['instance', 'title']),
|
||||||
userName,
|
userName,
|
||||||
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,6 +57,7 @@ class UnauthorizedModal extends ImmutablePureComponent {
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onRemoteInteraction: PropTypes.func.isRequired,
|
onRemoteInteraction: PropTypes.func.isRequired,
|
||||||
userName: PropTypes.string,
|
userName: PropTypes.string,
|
||||||
|
singleUserMode: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -86,7 +91,7 @@ class UnauthorizedModal extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRemoteInteractions() {
|
renderRemoteInteractions() {
|
||||||
const { intl, siteTitle, userName, action } = this.props;
|
const { intl, siteTitle, userName, action, singleUserMode } = this.props;
|
||||||
const { account } = this.state;
|
const { account } = this.state;
|
||||||
|
|
||||||
let header;
|
let header;
|
||||||
|
@ -134,10 +139,14 @@ class UnauthorizedModal extends ImmutablePureComponent {
|
||||||
<FormattedMessage id='remote_interaction.divider' defaultMessage='or' />
|
<FormattedMessage id='remote_interaction.divider' defaultMessage='or' />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{!singleUserMode && (
|
||||||
|
<>
|
||||||
<h3 className='compose-modal__header__title'><FormattedMessage id='unauthorized_modal.title' defaultMessage='Sign up for {site_title}' values={{ site_title: siteTitle }} /></h3>
|
<h3 className='compose-modal__header__title'><FormattedMessage id='unauthorized_modal.title' defaultMessage='Sign up for {site_title}' values={{ site_title: siteTitle }} /></h3>
|
||||||
<Link to='/' className='unauthorized-modal-content__button button' onClick={this.onClickClose}>
|
<Link to='/' className='unauthorized-modal-content__button button' onClick={this.onClickClose}>
|
||||||
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
||||||
</Link>
|
</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Link to='/auth/sign_in' className='unauthorized-modal-content__button button button-secondary' onClick={this.onClickClose}>
|
<Link to='/auth/sign_in' className='unauthorized-modal-content__button button button-secondary' onClick={this.onClickClose}>
|
||||||
<FormattedMessage id='account.login' defaultMessage='Log in' />
|
<FormattedMessage id='account.login' defaultMessage='Log in' />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -218,6 +218,10 @@ export function BirthdaysModal() {
|
||||||
return import(/* webpackChunkName: "features/ui" */'../components/birthdays_modal');
|
return import(/* webpackChunkName: "features/ui" */'../components/birthdays_modal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AccountNoteModal() {
|
||||||
|
return import(/* webpackChunkName: "features/ui" */'../components/account_note_modal');
|
||||||
|
}
|
||||||
|
|
||||||
export function ListEditor() {
|
export function ListEditor() {
|
||||||
return import(/* webpackChunkName: "features/list_editor" */'../../list_editor');
|
return import(/* webpackChunkName: "features/list_editor" */'../../list_editor');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +1,47 @@
|
||||||
import { fromJS } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
||||||
|
|
||||||
import { normalizeStatus } from 'soapbox/actions/importer/normalizer';
|
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||||
import { makeGetAccount, makeGetStatus } from 'soapbox/selectors';
|
import { calculateStatus } from 'soapbox/reducers/statuses';
|
||||||
|
import { makeGetAccount } from 'soapbox/selectors';
|
||||||
|
|
||||||
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
|
const buildMentions = pendingStatus => {
|
||||||
|
if (pendingStatus.get('in_reply_to_id')) {
|
||||||
|
return ImmutableList(pendingStatus.get('to') || []).map(acct => ImmutableMap({ acct }));
|
||||||
|
} else {
|
||||||
|
return ImmutableList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildPoll = pendingStatus => {
|
||||||
|
if (pendingStatus.hasIn(['poll', 'options'])) {
|
||||||
|
return pendingStatus.get('poll').update('options', options => {
|
||||||
|
return options.map(title => ImmutableMap({ title }));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const buildStatus = (state, pendingStatus, idempotencyKey) => {
|
export const buildStatus = (state, pendingStatus, idempotencyKey) => {
|
||||||
const getAccount = makeGetAccount();
|
|
||||||
const getStatus = makeGetStatus();
|
|
||||||
|
|
||||||
const me = state.get('me');
|
const me = state.get('me');
|
||||||
const account = getAccount(state, me);
|
const account = getAccount(state, me);
|
||||||
|
const inReplyToId = pendingStatus.get('in_reply_to_id');
|
||||||
|
|
||||||
let mentions;
|
const status = ImmutableMap({
|
||||||
if (pendingStatus.get('in_reply_to_id')) {
|
|
||||||
const inReplyTo = getStatus(state, { id: pendingStatus.get('in_reply_to_id') });
|
|
||||||
|
|
||||||
if (inReplyTo.getIn(['account', 'id']) === me) {
|
|
||||||
mentions = ImmutableOrderedSet([account.get('acct')]).union(pendingStatus.get('to', []));
|
|
||||||
} else {
|
|
||||||
mentions = pendingStatus.get('to', []);
|
|
||||||
}
|
|
||||||
|
|
||||||
mentions = mentions.map(mention => ({
|
|
||||||
username: mention.split('@')[0],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
const status = normalizeStatus({
|
|
||||||
account,
|
account,
|
||||||
application: null,
|
|
||||||
bookmarked: false,
|
|
||||||
card: null,
|
|
||||||
content: pendingStatus.get('status', '').replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
content: pendingStatus.get('status', '').replace(new RegExp('\n', 'g'), '<br>'), /* eslint-disable-line no-control-regex */
|
||||||
created_at: new Date(),
|
|
||||||
emojis: [],
|
|
||||||
favourited: false,
|
|
||||||
favourites_count: 0,
|
|
||||||
id: `末pending-${idempotencyKey}`,
|
id: `末pending-${idempotencyKey}`,
|
||||||
in_reply_to_account_id: null,
|
in_reply_to_account_id: state.getIn(['statuses', inReplyToId, 'account'], null),
|
||||||
in_reply_to_id: pendingStatus.get('in_reply_to_id'),
|
in_reply_to_id: inReplyToId,
|
||||||
language: null,
|
media_attachments: pendingStatus.get('media_ids', ImmutableList()).map(id => ImmutableMap({ id })),
|
||||||
media_attachments: pendingStatus.get('media_ids').map(id => ({ id })),
|
mentions: buildMentions(pendingStatus),
|
||||||
mentions,
|
poll: buildPoll(pendingStatus),
|
||||||
muted: false,
|
|
||||||
pinned: false,
|
|
||||||
poll: pendingStatus.get('poll', null),
|
|
||||||
quote: pendingStatus.get('quote_id', null),
|
quote: pendingStatus.get('quote_id', null),
|
||||||
reblog: null,
|
|
||||||
reblogged: false,
|
|
||||||
reblogs_count: 0,
|
|
||||||
replies_count: 0,
|
|
||||||
sensitive: pendingStatus.get('sensitive', false),
|
sensitive: pendingStatus.get('sensitive', false),
|
||||||
spoiler_text: '',
|
|
||||||
tags: [],
|
|
||||||
text: null,
|
|
||||||
uri: '',
|
|
||||||
url: '',
|
|
||||||
visibility: pendingStatus.get('visibility', 'public'),
|
visibility: pendingStatus.get('visibility', 'public'),
|
||||||
});
|
});
|
||||||
|
|
||||||
return fromJS(status).set('account', account);
|
return calculateStatus(normalizeStatus(status));
|
||||||
};
|
};
|
||||||
|
|
|
@ -856,6 +856,7 @@
|
||||||
"soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "Liczba elementów do wyświetlenia w widżecie krypto na stronie głównej",
|
"soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "Liczba elementów do wyświetlenia w widżecie krypto na stronie głównej",
|
||||||
"soapbox_config.custom_css.meta_fields.url_placeholder": "Adres URL",
|
"soapbox_config.custom_css.meta_fields.url_placeholder": "Adres URL",
|
||||||
"soapbox_config.display_fqn_label": "Wyświetlaj domenę (np. @użytkownik@domena) dla lokalnych kont.",
|
"soapbox_config.display_fqn_label": "Wyświetlaj domenę (np. @użytkownik@domena) dla lokalnych kont.",
|
||||||
|
"soapbox_config.fields.accent_color_label": "Kolor akcentu",
|
||||||
"soapbox_config.fields.brand_color_label": "Kolor marki",
|
"soapbox_config.fields.brand_color_label": "Kolor marki",
|
||||||
"soapbox_config.fields.crypto_address.add": "Dodaj nowy adres krypto",
|
"soapbox_config.fields.crypto_address.add": "Dodaj nowy adres krypto",
|
||||||
"soapbox_config.fields.crypto_addresses_label": "Adresy kryptowalut",
|
"soapbox_config.fields.crypto_addresses_label": "Adresy kryptowalut",
|
||||||
|
|
|
@ -844,6 +844,7 @@
|
||||||
"soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "Number of items to display in the crypto homepage widget",
|
"soapbox_config.crypto_donate_panel_limit.meta_fields.limit_placeholder": "Number of items to display in the crypto homepage widget",
|
||||||
"soapbox_config.custom_css.meta_fields.url_placeholder": "URL",
|
"soapbox_config.custom_css.meta_fields.url_placeholder": "URL",
|
||||||
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
|
"soapbox_config.display_fqn_label": "Display domain (eg @user@domain) for local accounts.",
|
||||||
|
"soapbox_config.fields.accent_color_label": "Акцентний колір",
|
||||||
"soapbox_config.fields.brand_color_label": "Brand color",
|
"soapbox_config.fields.brand_color_label": "Brand color",
|
||||||
"soapbox_config.fields.crypto_address.add": "Add new crypto address",
|
"soapbox_config.fields.crypto_address.add": "Add new crypto address",
|
||||||
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
|
"soapbox_config.fields.crypto_addresses_label": "Cryptocurrency addresses",
|
||||||
|
|
|
@ -98,4 +98,50 @@ describe('normalizeInstance()', () => {
|
||||||
const result = normalizeInstance(instance);
|
const result = normalizeInstance(instance);
|
||||||
expect(result.toJS()).toMatchObject(expected);
|
expect(result.toJS()).toMatchObject(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('normalizes Fedibird instance', () => {
|
||||||
|
const instance = fromJS(require('soapbox/__fixtures__/fedibird-instance.json'));
|
||||||
|
const result = normalizeInstance(instance);
|
||||||
|
|
||||||
|
// Sets description_limit
|
||||||
|
expect(result.get('description_limit')).toEqual(1500);
|
||||||
|
|
||||||
|
// But otherwise, it's the same
|
||||||
|
expect(result.delete('description_limit')).toEqual(instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Mitra instance', () => {
|
||||||
|
const instance = fromJS(require('soapbox/__fixtures__/mitra-instance.json'));
|
||||||
|
const result = normalizeInstance(instance);
|
||||||
|
|
||||||
|
// Adds configuration and description_limit
|
||||||
|
expect(result.get('configuration') instanceof ImmutableMap).toBe(true);
|
||||||
|
expect(result.get('description_limit')).toBe(1500);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes GoToSocial instance', () => {
|
||||||
|
const instance = fromJS(require('soapbox/__fixtures__/gotosocial-instance.json'));
|
||||||
|
const result = normalizeInstance(instance);
|
||||||
|
|
||||||
|
// Normalizes max_toot_chars
|
||||||
|
expect(result.getIn(['configuration', 'statuses', 'max_characters'])).toEqual(5000);
|
||||||
|
expect(result.has('max_toot_chars')).toBe(false);
|
||||||
|
|
||||||
|
// Adds configuration and description_limit
|
||||||
|
expect(result.get('configuration') instanceof ImmutableMap).toBe(true);
|
||||||
|
expect(result.get('description_limit')).toBe(1500);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Friendica instance', () => {
|
||||||
|
const instance = fromJS(require('soapbox/__fixtures__/friendica-instance.json'));
|
||||||
|
const result = normalizeInstance(instance);
|
||||||
|
|
||||||
|
// Normalizes max_toot_chars
|
||||||
|
expect(result.getIn(['configuration', 'statuses', 'max_characters'])).toEqual(200000);
|
||||||
|
expect(result.has('max_toot_chars')).toBe(false);
|
||||||
|
|
||||||
|
// Adds configuration and description_limit
|
||||||
|
expect(result.get('configuration') instanceof ImmutableMap).toBe(true);
|
||||||
|
expect(result.get('description_limit')).toBe(1500);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { fromJS } from 'immutable';
|
||||||
|
|
||||||
|
import { normalizeStatus } from '../status';
|
||||||
|
|
||||||
|
describe('normalizeStatus', () => {
|
||||||
|
it('adds base fields', () => {
|
||||||
|
const status = fromJS({});
|
||||||
|
const result = normalizeStatus(status);
|
||||||
|
|
||||||
|
expect(result.get('emojis')).toEqual(fromJS([]));
|
||||||
|
expect(result.get('favourites_count')).toBe(0);
|
||||||
|
expect(result.get('mentions')).toEqual(fromJS([]));
|
||||||
|
expect(result.get('reblog')).toBe(null);
|
||||||
|
expect(result.get('uri')).toBe('');
|
||||||
|
expect(result.get('visibility')).toBe('public');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fixes the order of mentions', () => {
|
||||||
|
const status = fromJS(require('soapbox/__fixtures__/status-unordered-mentions.json'));
|
||||||
|
|
||||||
|
const expected = ['NEETzsche', 'alex', 'Lumeinshin', 'sneeden'];
|
||||||
|
|
||||||
|
const result = normalizeStatus(status)
|
||||||
|
.get('mentions')
|
||||||
|
.map(mention => mention.get('username'))
|
||||||
|
.toJS();
|
||||||
|
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds mention to self in self-reply on Mastodon', () => {
|
||||||
|
const status = fromJS(require('soapbox/__fixtures__/mastodon-reply-to-self.json'));
|
||||||
|
|
||||||
|
const expected = fromJS([{
|
||||||
|
id: '106801667066418367',
|
||||||
|
username: 'benis911',
|
||||||
|
acct: 'benis911',
|
||||||
|
url: 'https://mastodon.social/@benis911',
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const result = normalizeStatus(status).get('mentions');
|
||||||
|
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes mentions with only acct', () => {
|
||||||
|
const status = fromJS({ mentions: [{ acct: 'alex@gleasonator.com' }] });
|
||||||
|
|
||||||
|
const expected = fromJS([{
|
||||||
|
acct: 'alex@gleasonator.com',
|
||||||
|
username: 'alex',
|
||||||
|
url: '',
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const result = normalizeStatus(status).get('mentions');
|
||||||
|
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Mitra attachments', () => {
|
||||||
|
const status = fromJS(require('soapbox/__fixtures__/mitra-status-with-attachments.json'));
|
||||||
|
|
||||||
|
const expected = fromJS([{
|
||||||
|
id: '017eeb0e-e5df-30a4-77a7-a929145cb836',
|
||||||
|
type: 'image',
|
||||||
|
url: 'https://mitra.social/media/8e04e6091bbbac79641b5812508683ce72c38693661c18d16040553f2371e18d.png',
|
||||||
|
preview_url: 'https://mitra.social/media/8e04e6091bbbac79641b5812508683ce72c38693661c18d16040553f2371e18d.png',
|
||||||
|
remote_url: 'https://mitra.social/media/8e04e6091bbbac79641b5812508683ce72c38693661c18d16040553f2371e18d.png',
|
||||||
|
}, {
|
||||||
|
id: '017eeb0e-e5e4-2a48-2889-afdebf368a54',
|
||||||
|
type: 'unknown',
|
||||||
|
url: 'https://mitra.social/media/8f72dc2e98572eb4ba7c3a902bca5f69c448fc4391837e5f8f0d4556280440ac',
|
||||||
|
preview_url: 'https://mitra.social/media/8f72dc2e98572eb4ba7c3a902bca5f69c448fc4391837e5f8f0d4556280440ac',
|
||||||
|
remote_url: 'https://mitra.social/media/8f72dc2e98572eb4ba7c3a902bca5f69c448fc4391837e5f8f0d4556280440ac',
|
||||||
|
}, {
|
||||||
|
id: '017eeb0e-e5e5-79fd-6054-8b6869b1db49',
|
||||||
|
type: 'unknown',
|
||||||
|
url: 'https://mitra.social/media/55a81a090247cc4fc127e5716bcf7964f6e0df9b584f85f4696c0b994747a4d0.oga',
|
||||||
|
preview_url: 'https://mitra.social/media/55a81a090247cc4fc127e5716bcf7964f6e0df9b584f85f4696c0b994747a4d0.oga',
|
||||||
|
remote_url: 'https://mitra.social/media/55a81a090247cc4fc127e5716bcf7964f6e0df9b584f85f4696c0b994747a4d0.oga',
|
||||||
|
}, {
|
||||||
|
id: '017eeb0e-e5e6-c416-a444-21e560c47839',
|
||||||
|
type: 'unknown',
|
||||||
|
url: 'https://mitra.social/media/0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0',
|
||||||
|
preview_url: 'https://mitra.social/media/0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0',
|
||||||
|
remote_url: 'https://mitra.social/media/0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0',
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const result = normalizeStatus(status);
|
||||||
|
|
||||||
|
expect(result.get('media_attachments')).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves Pleroma attachments alone', () => {
|
||||||
|
const status = fromJS(require('soapbox/__fixtures__/pleroma-status-with-attachments.json'));
|
||||||
|
const result = normalizeStatus(status);
|
||||||
|
|
||||||
|
expect(status.get('media_attachments')).toEqual(result.get('media_attachments'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Pleroma quote post', () => {
|
||||||
|
const status = fromJS(require('soapbox/__fixtures__/pleroma-quote-post.json'));
|
||||||
|
const result = normalizeStatus(status);
|
||||||
|
|
||||||
|
expect(result.get('quote')).toEqual(status.getIn(['pleroma', 'quote']));
|
||||||
|
expect(result.getIn(['pleroma', 'quote'])).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes GoToSocial status', () => {
|
||||||
|
const status = fromJS(require('soapbox/__fixtures__/gotosocial-status.json'));
|
||||||
|
const result = normalizeStatus(status);
|
||||||
|
|
||||||
|
// Adds missing fields
|
||||||
|
const missing = {
|
||||||
|
in_reply_to_account_id: null,
|
||||||
|
in_reply_to_id: null,
|
||||||
|
reblog: null,
|
||||||
|
pinned: false,
|
||||||
|
quote: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(result.toJS()).toMatchObject(missing);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes Friendica status', () => {
|
||||||
|
const status = fromJS(require('soapbox/__fixtures__/friendica-status.json'));
|
||||||
|
const result = normalizeStatus(status);
|
||||||
|
|
||||||
|
// Adds missing fields
|
||||||
|
const missing = {
|
||||||
|
pinned: false,
|
||||||
|
quote: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(result.toJS()).toMatchObject(missing);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('normalizes poll and poll options', () => {
|
||||||
|
const status = fromJS({ poll: { options: [{ title: 'Apples' }] } });
|
||||||
|
const result = normalizeStatus(status);
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
options: [{ title: 'Apples', votes_count: 0 }],
|
||||||
|
emojis: [],
|
||||||
|
expired: false,
|
||||||
|
multiple: false,
|
||||||
|
voters_count: 0,
|
||||||
|
votes_count: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(result.get('poll').toJS()).toMatchObject(expected);
|
||||||
|
expect(result.getIn(['poll', 'expires_at']) instanceof Date).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
|
import { accountToMention } from 'soapbox/utils/accounts';
|
||||||
|
|
||||||
|
// Some backends can return null, or omit these required fields
|
||||||
|
const baseStatus = ImmutableMap({
|
||||||
|
application: null,
|
||||||
|
bookmarked: false,
|
||||||
|
card: null,
|
||||||
|
created_at: new Date(),
|
||||||
|
emojis: ImmutableList(),
|
||||||
|
favourited: false,
|
||||||
|
favourites_count: 0,
|
||||||
|
in_reply_to_account_id: null,
|
||||||
|
in_reply_to_id: null,
|
||||||
|
language: null,
|
||||||
|
mentions: ImmutableList(),
|
||||||
|
muted: false,
|
||||||
|
pinned: false,
|
||||||
|
reblog: null,
|
||||||
|
reblogged: false,
|
||||||
|
reblogs_count: 0,
|
||||||
|
replies_count: 0,
|
||||||
|
spoiler_text: '',
|
||||||
|
tags: ImmutableList(),
|
||||||
|
uri: '',
|
||||||
|
url: '',
|
||||||
|
visibility: 'public',
|
||||||
|
});
|
||||||
|
|
||||||
|
const basePollOption = ImmutableMap({ title: '', votes_count: 0 });
|
||||||
|
|
||||||
|
const basePoll = ImmutableMap({
|
||||||
|
emojis: ImmutableList(),
|
||||||
|
expired: false,
|
||||||
|
expires_at: new Date(Date.now() + 1000 * (60 * 5)), // 5 minutes
|
||||||
|
multiple: false,
|
||||||
|
options: ImmutableList(),
|
||||||
|
voters_count: 0,
|
||||||
|
votes_count: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merger function for only overriding undefined values
|
||||||
|
const mergeDefined = (oldVal, newVal) => oldVal === undefined ? newVal : oldVal;
|
||||||
|
|
||||||
|
// Merge base status
|
||||||
|
const mergeBase = status => {
|
||||||
|
return status.mergeDeepWith(mergeDefined, baseStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure attachments have required fields
|
||||||
|
// https://docs.joinmastodon.org/entities/attachment/
|
||||||
|
const normalizeAttachment = attachment => {
|
||||||
|
const url = [
|
||||||
|
attachment.get('url'),
|
||||||
|
attachment.get('preview_url'),
|
||||||
|
attachment.get('remote_url'),
|
||||||
|
].find(url => url) || '';
|
||||||
|
|
||||||
|
const base = ImmutableMap({
|
||||||
|
url,
|
||||||
|
preview_url: url,
|
||||||
|
remote_url: url,
|
||||||
|
});
|
||||||
|
|
||||||
|
return attachment.mergeWith(mergeDefined, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeAttachments = status => {
|
||||||
|
return status.update('media_attachments', ImmutableList(), attachments => {
|
||||||
|
return attachments.map(normalizeAttachment);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize mentions
|
||||||
|
const normalizeMention = mention => {
|
||||||
|
const base = ImmutableMap({
|
||||||
|
acct: '',
|
||||||
|
username: (mention.get('acct') || '').split('@')[0],
|
||||||
|
url: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
return mention.mergeWith(mergeDefined, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeMentions = status => {
|
||||||
|
return status.update('mentions', ImmutableList(), mentions => {
|
||||||
|
return mentions.map(normalizeMention);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize poll option
|
||||||
|
const normalizePollOption = option => {
|
||||||
|
return option.mergeWith(mergeDefined, basePollOption);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize poll
|
||||||
|
const normalizePoll = status => {
|
||||||
|
if (status.hasIn(['poll', 'options'])) {
|
||||||
|
return status.update('poll', ImmutableMap(), poll => {
|
||||||
|
return poll.mergeWith(mergeDefined, basePoll).update('options', options => {
|
||||||
|
return options.map(normalizePollOption);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return status.set('poll', null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Fix order of mentions
|
||||||
|
const fixMentionsOrder = status => {
|
||||||
|
const mentions = status.get('mentions', ImmutableList());
|
||||||
|
const inReplyToAccountId = status.get('in_reply_to_account_id');
|
||||||
|
|
||||||
|
// Sort the replied-to mention to the top
|
||||||
|
const sorted = mentions.sort((a, b) => {
|
||||||
|
if (a.get('id') === inReplyToAccountId) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return status.set('mentions', sorted);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add self to mentions if it's a reply to self
|
||||||
|
const addSelfMention = status => {
|
||||||
|
const accountId = status.getIn(['account', 'id']);
|
||||||
|
|
||||||
|
const isSelfReply = accountId === status.get('in_reply_to_account_id');
|
||||||
|
const hasSelfMention = accountId === status.getIn(['mentions', 0, 'id']);
|
||||||
|
|
||||||
|
if (isSelfReply && !hasSelfMention) {
|
||||||
|
const mention = accountToMention(status.get('account'));
|
||||||
|
return status.update('mentions', ImmutableList(), mentions => (
|
||||||
|
ImmutableList([mention]).concat(mentions)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Move the quote to the top-level
|
||||||
|
const fixQuote = status => {
|
||||||
|
return status.withMutations(status => {
|
||||||
|
status.update('quote', quote => quote || status.getIn(['pleroma', 'quote']) || null);
|
||||||
|
status.deleteIn(['pleroma', 'quote']);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const normalizeStatus = status => {
|
||||||
|
return status.withMutations(status => {
|
||||||
|
mergeBase(status);
|
||||||
|
normalizeAttachments(status);
|
||||||
|
normalizeMentions(status);
|
||||||
|
normalizePoll(status);
|
||||||
|
fixMentionsOrder(status);
|
||||||
|
addSelfMention(status);
|
||||||
|
fixQuote(status);
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,7 +1,6 @@
|
||||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||||
|
|
||||||
import { STATUS_IMPORT } from 'soapbox/actions/importer';
|
import { STATUS_IMPORT } from 'soapbox/actions/importer';
|
||||||
import { normalizeStatus } from 'soapbox/actions/importer/normalizer';
|
|
||||||
import {
|
import {
|
||||||
STATUS_CREATE_REQUEST,
|
STATUS_CREATE_REQUEST,
|
||||||
STATUS_CREATE_FAIL,
|
STATUS_CREATE_FAIL,
|
||||||
|
@ -34,8 +33,8 @@ describe('statuses reducer', () => {
|
||||||
const quotedQuotePost = require('soapbox/__fixtures__/pleroma-quote-of-quote-post.json');
|
const quotedQuotePost = require('soapbox/__fixtures__/pleroma-quote-of-quote-post.json');
|
||||||
|
|
||||||
let state = undefined;
|
let state = undefined;
|
||||||
state = reducer(state, { type: STATUS_IMPORT, status: normalizeStatus(quotePost) });
|
state = reducer(state, { type: STATUS_IMPORT, status: quotePost });
|
||||||
state = reducer(state, { type: STATUS_IMPORT, status: normalizeStatus(quotedQuotePost.pleroma.quote) });
|
state = reducer(state, { type: STATUS_IMPORT, status: quotedQuotePost.pleroma.quote });
|
||||||
|
|
||||||
expect(state.getIn(['AFmFMSpITT9xcOJKcK', 'quote'])).toEqual('AFmFLcd6XYVdjWCrOS');
|
expect(state.getIn(['AFmFMSpITT9xcOJKcK', 'quote'])).toEqual('AFmFLcd6XYVdjWCrOS');
|
||||||
});
|
});
|
||||||
|
@ -43,7 +42,7 @@ describe('statuses reducer', () => {
|
||||||
it('normalizes Mitra attachments', () => {
|
it('normalizes Mitra attachments', () => {
|
||||||
const status = require('soapbox/__fixtures__/mitra-status-with-attachments.json');
|
const status = require('soapbox/__fixtures__/mitra-status-with-attachments.json');
|
||||||
|
|
||||||
const state = reducer(undefined, { type: STATUS_IMPORT, status: normalizeStatus(status) });
|
const state = reducer(undefined, { type: STATUS_IMPORT, status });
|
||||||
|
|
||||||
const expected = fromJS([{
|
const expected = fromJS([{
|
||||||
id: '017eeb0e-e5df-30a4-77a7-a929145cb836',
|
id: '017eeb0e-e5df-30a4-77a7-a929145cb836',
|
||||||
|
@ -76,12 +75,52 @@ describe('statuses reducer', () => {
|
||||||
|
|
||||||
it('leaves Pleroma attachments alone', () => {
|
it('leaves Pleroma attachments alone', () => {
|
||||||
const status = require('soapbox/__fixtures__/pleroma-status-with-attachments.json');
|
const status = require('soapbox/__fixtures__/pleroma-status-with-attachments.json');
|
||||||
const action = { type: STATUS_IMPORT, status: normalizeStatus(status) };
|
const action = { type: STATUS_IMPORT, status };
|
||||||
const state = reducer(undefined, action);
|
const state = reducer(undefined, action);
|
||||||
const expected = fromJS(status.media_attachments);
|
const expected = fromJS(status.media_attachments);
|
||||||
|
|
||||||
expect(state.getIn(['AGNkA21auFR5lnEAHw', 'media_attachments'])).toEqual(expected);
|
expect(state.getIn(['AGNkA21auFR5lnEAHw', 'media_attachments'])).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('hides CWs', () => {
|
||||||
|
const status = require('soapbox/__fixtures__/status-cw.json');
|
||||||
|
const action = { type: STATUS_IMPORT, status };
|
||||||
|
|
||||||
|
const hidden = reducer(undefined, action).getIn(['107831528995252317', 'hidden']);
|
||||||
|
expect(hidden).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('expands CWs when expandSpoilers is enabled', () => {
|
||||||
|
const status = require('soapbox/__fixtures__/status-cw.json');
|
||||||
|
const action = { type: STATUS_IMPORT, status, expandSpoilers: true };
|
||||||
|
|
||||||
|
const hidden = reducer(undefined, action).getIn(['107831528995252317', 'hidden']);
|
||||||
|
expect(hidden).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses custom emojis', () => {
|
||||||
|
const status = require('soapbox/__fixtures__/status-custom-emoji.json');
|
||||||
|
const action = { type: STATUS_IMPORT, status };
|
||||||
|
|
||||||
|
const expected = 'Hello <img draggable="false" class="emojione" alt=":ablobcathyper:" title=":ablobcathyper:" src="https://gleasonator.com/emoji/blobcat/ablobcathyper.png"> <img draggable="false" class="emojione" alt=":ageblobcat:" title=":ageblobcat:" src="https://gleasonator.com/emoji/blobcat/ageblobcat.png"> <img draggable="false" class="emojione" alt="😂" title=":joy:" src="/packs/emoji/1f602.svg"> world <img draggable="false" class="emojione" alt="😋" title=":yum:" src="/packs/emoji/1f60b.svg"> test <img draggable="false" class="emojione" alt=":blobcatphoto:" title=":blobcatphoto:" src="https://gleasonator.com/emoji/blobcat/blobcatphoto.png">';
|
||||||
|
|
||||||
|
const result = reducer(undefined, action).getIn(['AGm7uC9DaAIGUa4KYK', 'contentHtml']);
|
||||||
|
expect(result).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('builds search_index', () => {
|
||||||
|
const status = require('soapbox/__fixtures__/status-with-poll.json');
|
||||||
|
const action = { type: STATUS_IMPORT, status };
|
||||||
|
|
||||||
|
const expected = `What is tolerance?
|
||||||
|
|
||||||
|
Banning, censoring, and deplatforming anyone you disagree with
|
||||||
|
|
||||||
|
Promoting free speech, even for people and ideas you dislike`;
|
||||||
|
|
||||||
|
const result = reducer(undefined, action).getIn(['103874034847713213', 'search_index']);
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('STATUS_CREATE_REQUEST', () => {
|
describe('STATUS_CREATE_REQUEST', () => {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ACCOUNT_NOTE_INIT_MODAL,
|
||||||
|
ACCOUNT_NOTE_CHANGE_COMMENT,
|
||||||
|
ACCOUNT_NOTE_SUBMIT_REQUEST,
|
||||||
|
ACCOUNT_NOTE_SUBMIT_FAIL,
|
||||||
|
ACCOUNT_NOTE_SUBMIT_SUCCESS,
|
||||||
|
} from '../actions/account_notes';
|
||||||
|
|
||||||
|
const initialState = ImmutableMap({
|
||||||
|
edit: ImmutableMap({
|
||||||
|
isSubmitting: false,
|
||||||
|
account: null,
|
||||||
|
comment: null,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function account_notes(state = initialState, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case ACCOUNT_NOTE_INIT_MODAL:
|
||||||
|
return state.withMutations((state) => {
|
||||||
|
state.setIn(['edit', 'isSubmitting'], false);
|
||||||
|
state.setIn(['edit', 'account_id'], action.account.get('id'));
|
||||||
|
state.setIn(['edit', 'comment'], action.comment);
|
||||||
|
});
|
||||||
|
case ACCOUNT_NOTE_CHANGE_COMMENT:
|
||||||
|
return state.setIn(['edit', 'comment'], action.comment);
|
||||||
|
case ACCOUNT_NOTE_SUBMIT_REQUEST:
|
||||||
|
return state.setIn(['edit', 'isSubmitting'], true);
|
||||||
|
case ACCOUNT_NOTE_SUBMIT_FAIL:
|
||||||
|
case ACCOUNT_NOTE_SUBMIT_SUCCESS:
|
||||||
|
return state.setIn(['edit', 'isSubmitting'], false);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import { combineReducers } from 'redux-immutable';
|
||||||
|
|
||||||
import { AUTH_LOGGED_OUT } from 'soapbox/actions/auth';
|
import { AUTH_LOGGED_OUT } from 'soapbox/actions/auth';
|
||||||
|
|
||||||
|
import account_notes from './account_notes';
|
||||||
import accounts from './accounts';
|
import accounts from './accounts';
|
||||||
import accounts_counters from './accounts_counters';
|
import accounts_counters from './accounts_counters';
|
||||||
import accounts_meta from './accounts_meta';
|
import accounts_meta from './accounts_meta';
|
||||||
|
@ -66,6 +67,7 @@ const appReducer = combineReducers({
|
||||||
user_lists,
|
user_lists,
|
||||||
domain_lists,
|
domain_lists,
|
||||||
status_lists,
|
status_lists,
|
||||||
|
account_notes,
|
||||||
accounts,
|
accounts,
|
||||||
accounts_counters,
|
accounts_counters,
|
||||||
statuses,
|
statuses,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { get } from 'lodash';
|
||||||
|
|
||||||
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
|
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
|
||||||
|
|
||||||
|
import { ACCOUNT_NOTE_SUBMIT_SUCCESS } from '../actions/account_notes';
|
||||||
import {
|
import {
|
||||||
ACCOUNT_FOLLOW_SUCCESS,
|
ACCOUNT_FOLLOW_SUCCESS,
|
||||||
ACCOUNT_FOLLOW_REQUEST,
|
ACCOUNT_FOLLOW_REQUEST,
|
||||||
|
@ -106,6 +107,7 @@ export default function relationships(state = initialState, action) {
|
||||||
case ACCOUNT_UNSUBSCRIBE_SUCCESS:
|
case ACCOUNT_UNSUBSCRIBE_SUCCESS:
|
||||||
case ACCOUNT_PIN_SUCCESS:
|
case ACCOUNT_PIN_SUCCESS:
|
||||||
case ACCOUNT_UNPIN_SUCCESS:
|
case ACCOUNT_UNPIN_SUCCESS:
|
||||||
|
case ACCOUNT_NOTE_SUBMIT_SUCCESS:
|
||||||
return normalizeRelationship(state, action.relationship);
|
return normalizeRelationship(state, action.relationship);
|
||||||
case RELATIONSHIPS_FETCH_SUCCESS:
|
case RELATIONSHIPS_FETCH_SUCCESS:
|
||||||
return normalizeRelationships(state, action.relationships);
|
return normalizeRelationships(state, action.relationships);
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
import escapeTextContentForBrowser from 'escape-html';
|
||||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
|
|
||||||
|
import emojify from 'soapbox/features/emoji/emoji';
|
||||||
|
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||||
import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji_reacts';
|
import { simulateEmojiReact, simulateUnEmojiReact } from 'soapbox/utils/emoji_reacts';
|
||||||
|
import { stripCompatibilityFeatures } from 'soapbox/utils/html';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EMOJI_REACT_REQUEST,
|
EMOJI_REACT_REQUEST,
|
||||||
|
@ -26,55 +30,53 @@ import {
|
||||||
} from '../actions/statuses';
|
} from '../actions/statuses';
|
||||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||||
|
|
||||||
// Ensure attachments have required fields
|
const domParser = new DOMParser();
|
||||||
// https://docs.joinmastodon.org/entities/attachment/
|
|
||||||
const normalizeAttachment = attachment => {
|
|
||||||
const url = [
|
|
||||||
attachment.get('url'),
|
|
||||||
attachment.get('preview_url'),
|
|
||||||
attachment.get('remote_url'),
|
|
||||||
].find(url => url) || '';
|
|
||||||
|
|
||||||
const base = ImmutableMap({
|
const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
|
||||||
url,
|
obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
|
||||||
preview_url: url,
|
return obj;
|
||||||
remote_url: url,
|
}, {});
|
||||||
});
|
|
||||||
|
|
||||||
return attachment.mergeWith((o, n) => o || n, base);
|
const minifyStatus = status => {
|
||||||
};
|
return status.mergeWith((o, n) => n || o, {
|
||||||
|
account: status.getIn(['account', 'id']),
|
||||||
const normalizeAttachments = status => {
|
reblog: status.getIn(['reblog', 'id']),
|
||||||
return status.update('media_attachments', ImmutableList(), attachments => {
|
poll: status.getIn(['poll', 'id']),
|
||||||
return attachments.map(normalizeAttachment);
|
quote: status.getIn(['quote', 'id']),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fix order of mentions
|
// Only calculate these values when status first encountered
|
||||||
const fixMentions = status => {
|
// Otherwise keep the ones already in the reducer
|
||||||
const mentions = status.get('mentions');
|
export const calculateStatus = (status, oldStatus, expandSpoilers = false) => {
|
||||||
const inReplyToAccountId = status.get('in_reply_to_account_id');
|
if (oldStatus) {
|
||||||
|
return status.merge({
|
||||||
// Sort the replied-to mention to the top
|
search_index: oldStatus.get('search_index'),
|
||||||
const sorted = mentions.sort((a, b) => {
|
contentHtml: oldStatus.get('contentHtml'),
|
||||||
if (a.get('id') === inReplyToAccountId) {
|
spoilerHtml: oldStatus.get('spoilerHtml'),
|
||||||
return -1;
|
hidden: oldStatus.get('hidden'),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
const spoilerText = status.get('spoiler_text') || '';
|
||||||
}
|
const searchContent = (ImmutableList([spoilerText, status.get('content')]).concat(status.getIn(['poll', 'options'], ImmutableList()).map(option => option.get('title')))).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||||
});
|
const emojiMap = makeEmojiMap(status);
|
||||||
|
|
||||||
return status.set('mentions', sorted);
|
return status.merge({
|
||||||
|
search_index: domParser.parseFromString(searchContent, 'text/html').documentElement.textContent,
|
||||||
|
contentHtml: stripCompatibilityFeatures(emojify(status.get('content'), emojiMap)),
|
||||||
|
spoilerHtml: emojify(escapeTextContentForBrowser(spoilerText), emojiMap),
|
||||||
|
hidden: expandSpoilers ? false : spoilerText.length > 0 || status.get('sensitive'),
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check whether a status is a quote by secondary characteristics
|
||||||
const isQuote = status => {
|
const isQuote = status => {
|
||||||
return Boolean(status.get('quote_id') || status.getIn(['pleroma', 'quote_url']));
|
return Boolean(status.get('quote_id') || status.getIn(['pleroma', 'quote_url']));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Preserve quote if an existing status already has it
|
// Preserve quote if an existing status already has it
|
||||||
const fixQuote = (state, status) => {
|
const fixQuote = (status, oldStatus) => {
|
||||||
const oldStatus = state.get(status.get('id'));
|
|
||||||
|
|
||||||
if (oldStatus && !status.get('quote') && isQuote(status)) {
|
if (oldStatus && !status.get('quote') && isQuote(status)) {
|
||||||
return status
|
return status
|
||||||
.set('quote', oldStatus.get('quote'))
|
.set('quote', oldStatus.get('quote'))
|
||||||
|
@ -84,18 +86,22 @@ const fixQuote = (state, status) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeStatus = (state, status) => {
|
const fixStatus = (state, status, expandSpoilers) => {
|
||||||
|
const oldStatus = state.get(status.get('id'));
|
||||||
|
|
||||||
return status.withMutations(status => {
|
return status.withMutations(status => {
|
||||||
fixMentions(status);
|
normalizeStatus(status);
|
||||||
fixQuote(state, status);
|
fixQuote(status, oldStatus);
|
||||||
normalizeAttachments(status);
|
calculateStatus(status, oldStatus, expandSpoilers);
|
||||||
|
minifyStatus(status);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const importStatus = (state, status) => state.set(status.id, normalizeStatus(state, fromJS(status)));
|
const importStatus = (state, status, expandSpoilers) =>
|
||||||
|
state.set(status.id, fixStatus(state, fromJS(status), expandSpoilers));
|
||||||
|
|
||||||
const importStatuses = (state, statuses) =>
|
const importStatuses = (state, statuses, expandSpoilers) =>
|
||||||
state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status)));
|
state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status, expandSpoilers)));
|
||||||
|
|
||||||
const deleteStatus = (state, id, references) => {
|
const deleteStatus = (state, id, references) => {
|
||||||
references.forEach(ref => {
|
references.forEach(ref => {
|
||||||
|
@ -126,9 +132,9 @@ 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, action.expandSpoilers);
|
||||||
case STATUSES_IMPORT:
|
case STATUSES_IMPORT:
|
||||||
return importStatuses(state, action.statuses);
|
return importStatuses(state, action.statuses, action.expandSpoilers);
|
||||||
case STATUS_CREATE_REQUEST:
|
case STATUS_CREATE_REQUEST:
|
||||||
return importPendingStatus(state, action.params);
|
return importPendingStatus(state, action.params);
|
||||||
case STATUS_CREATE_FAIL:
|
case STATUS_CREATE_FAIL:
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
isStaff,
|
isStaff,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isModerator,
|
isModerator,
|
||||||
|
accountToMention,
|
||||||
} from '../accounts';
|
} from '../accounts';
|
||||||
|
|
||||||
describe('getDomain', () => {
|
describe('getDomain', () => {
|
||||||
|
@ -115,3 +116,19 @@ describe('isModerator', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('accountToMention', () => {
|
||||||
|
it('converts the account to a mention', () => {
|
||||||
|
const account = fromJS(require('soapbox/__fixtures__/alex.json'));
|
||||||
|
|
||||||
|
const expected = fromJS({
|
||||||
|
id: '9v5bmRalQvjOy0ECcC',
|
||||||
|
username: 'alex',
|
||||||
|
acct: 'alex',
|
||||||
|
url: 'https://gleasonator.com/users/alex',
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = accountToMention(account);
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -71,3 +71,12 @@ export const isVerified = (account: ImmutableMap<string, any>): boolean => {
|
||||||
const tags: any = account.getIn(['pleroma', 'tags'], ImmutableList());
|
const tags: any = account.getIn(['pleroma', 'tags'], ImmutableList());
|
||||||
return tags.includes('verified');
|
return tags.includes('verified');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const accountToMention = (account: ImmutableMap<string, any>): ImmutableMap<string, any> => {
|
||||||
|
return ImmutableMap({
|
||||||
|
id: account.get('id'),
|
||||||
|
username: account.get('username'),
|
||||||
|
acct: account.get('acct'),
|
||||||
|
url: account.get('url'),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -89,6 +89,10 @@ export const getFeatures = createSelector([instance => instance], instance => {
|
||||||
birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
birthdays: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
||||||
ethereumLogin: v.software === MITRA,
|
ethereumLogin: v.software === MITRA,
|
||||||
accountMoving: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
accountMoving: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
||||||
|
notes: any([
|
||||||
|
v.software === MASTODON && gte(v.compatVersion, '3.2.0'),
|
||||||
|
v.software === PLEROMA && gte(v.version, '2.4.50'),
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
export const generateThemeCss = brandColor => {
|
export const generateThemeCss = (brandColor, accentColor) => {
|
||||||
if (!brandColor) return null;
|
if (!brandColor) return null;
|
||||||
return themeDataToCss(brandColorToThemeData(brandColor));
|
return themeDataToCss(brandColorToThemeData(brandColor).merge(accentColorToThemeData(brandColor, accentColor)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://stackoverflow.com/a/5624139
|
// https://stackoverflow.com/a/5624139
|
||||||
|
@ -74,10 +74,29 @@ export const brandColorToThemeData = brandColor => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const accentColorToThemeData = (brandColor, accentColor) => {
|
||||||
|
if (accentColor) {
|
||||||
|
const { h, s, l } = rgbToHsl(hexToRgb(accentColor));
|
||||||
|
|
||||||
|
return ImmutableMap({
|
||||||
|
'accent-color_h': h,
|
||||||
|
'accent-color_s': `${s}%`,
|
||||||
|
'accent-color_l': `${l}%`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { h } = rgbToHsl(hexToRgb(brandColor));
|
||||||
|
return ImmutableMap({
|
||||||
|
'accent-color_h': h - 15,
|
||||||
|
'accent-color_s': '86%',
|
||||||
|
'accent-color_l': '44%',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const themeDataToCss = themeData => (
|
export const themeDataToCss = themeData => (
|
||||||
themeData
|
themeData
|
||||||
.entrySeq()
|
.entrySeq()
|
||||||
.reduce((acc, cur) => acc + `--${cur[0]}:${cur[1]};`, '')
|
.reduce((acc, cur) => acc + `--${cur[0]}:${cur[1]};`, '')
|
||||||
);
|
);
|
||||||
|
|
||||||
export const brandColorToCSS = brandColor => themeDataToCss(brandColorToThemeData(brandColor));
|
export const themeColorsToCSS = (brandColor, accentColor) => themeDataToCss(brandColorToThemeData(brandColor).merge(accentColorToThemeData(brandColor, accentColor)));
|
||||||
|
|
|
@ -63,6 +63,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__version {
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
p.help-text {
|
p.help-text {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|
|
@ -126,7 +126,9 @@
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
@media screen and (max-width: 600px) { padding: 30px 2px; }
|
@media screen and (max-width: 600px) {
|
||||||
|
padding: 30px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
@ -342,7 +344,8 @@
|
||||||
.mute-modal,
|
.mute-modal,
|
||||||
.reactions-modal,
|
.reactions-modal,
|
||||||
.reblogs-modal,
|
.reblogs-modal,
|
||||||
.mentions-modal {
|
.mentions-modal,
|
||||||
|
.account-note-modal {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -411,7 +414,8 @@
|
||||||
|
|
||||||
.boost-modal__action-bar,
|
.boost-modal__action-bar,
|
||||||
.confirmation-modal__action-bar,
|
.confirmation-modal__action-bar,
|
||||||
.mute-modal__action-bar {
|
.mute-modal__action-bar,
|
||||||
|
.account-note-modal__action-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -464,7 +468,8 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.report-modal {
|
.report-modal,
|
||||||
|
.account-note-modal {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
}
|
}
|
||||||
|
@ -521,27 +526,6 @@
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-text {
|
|
||||||
display: block;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
background: var(--background-color);
|
|
||||||
padding: 10px;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 14px;
|
|
||||||
resize: vertical;
|
|
||||||
outline: 0;
|
|
||||||
border: 1px solid var(--background-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border: 1px solid var(--background-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-toggle {
|
.setting-toggle {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
@ -574,7 +558,9 @@
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions-modal__item-label { font-weight: 500; }
|
.actions-modal__item-label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -582,12 +568,18 @@
|
||||||
max-height: calc(100vh - 147px);
|
max-height: calc(100vh - 147px);
|
||||||
|
|
||||||
// NOTE - not sure what this is yet, leaving alone for now until I find out.
|
// NOTE - not sure what this is yet, leaving alone for now until I find out.
|
||||||
&.with-status { max-height: calc(80vh - 75px); }
|
&.with-status {
|
||||||
|
max-height: calc(80vh - 75px);
|
||||||
|
}
|
||||||
|
|
||||||
li:empty { margin: 0; }
|
li:empty {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
li:not(:empty) {
|
li:not(:empty) {
|
||||||
&:first-of-type { margin: 10px 0 0; }
|
&:first-of-type {
|
||||||
|
margin: 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -654,10 +646,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirmation-modal__action-bar,
|
.confirmation-modal__action-bar,
|
||||||
.mute-modal__action-bar {
|
.mute-modal__action-bar,
|
||||||
|
.account-note-modal__action-bar {
|
||||||
.confirmation-modal__secondary-button,
|
.confirmation-modal__secondary-button,
|
||||||
.confirmation-modal__cancel-button,
|
.confirmation-modal__cancel-button,
|
||||||
.mute-modal__cancel-button {
|
.mute-modal__cancel-button,
|
||||||
|
.account-note-modal__cancel-button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: var(--highlight-text-color);
|
color: var(--highlight-text-color);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -726,7 +720,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-layout {
|
.modal-layout {
|
||||||
background: var(--brand-color--med) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color(var(--background-color))}"/></svg>') repeat-x bottom fixed;
|
background: var(--brand-color--med)
|
||||||
|
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color(var(--background-color))}"/></svg>')
|
||||||
|
repeat-x bottom fixed;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
@ -1068,7 +1064,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirmation-modal,
|
.confirmation-modal,
|
||||||
.mute-modal {
|
.mute-modal,
|
||||||
|
.account-note-modal {
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -1090,3 +1087,35 @@
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.report-modal__comment,
|
||||||
|
.account-note-modal__container {
|
||||||
|
.setting-text {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
background: var(--background-color);
|
||||||
|
padding: 10px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 14px;
|
||||||
|
resize: vertical;
|
||||||
|
outline: 0;
|
||||||
|
border: 1px solid var(--background-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border: 1px solid var(--background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-note-modal {
|
||||||
|
.setting-text {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__join-date,
|
&__join-date,
|
||||||
&__birthday {
|
&__birthday,
|
||||||
|
&__note {
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--primary-text-color--faint);
|
color: var(--primary-text-color--faint);
|
||||||
|
@ -39,6 +40,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__note {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__stats {
|
&__stats {
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
|
|
||||||
|
@ -163,10 +173,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-info-panel__name-content::before {
|
.profile-info-panel__name-content::before {
|
||||||
content: '[';
|
content: "[";
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-info-panel__name-content::after {
|
.profile-info-panel__name-content::after {
|
||||||
content: ']';
|
content: "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,6 +418,10 @@ code {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
|
&#accent_color {
|
||||||
|
background: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
@ -510,7 +514,7 @@ code {
|
||||||
|
|
||||||
.label_input {
|
.label_input {
|
||||||
&__color {
|
&__color {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
.color-swatch {
|
.color-swatch {
|
||||||
|
|
|
@ -41,9 +41,6 @@ body,
|
||||||
--background-color_hsl: var(--background-color_h), var(--background-color_s), var(--background-color_l);
|
--background-color_hsl: var(--background-color_h), var(--background-color_s), var(--background-color_l);
|
||||||
--foreground-color_hsl: var(--foreground-color_h), var(--foreground-color_s), var(--foreground-color_l);
|
--foreground-color_hsl: var(--foreground-color_h), var(--foreground-color_s), var(--foreground-color_l);
|
||||||
--warning-color_hsl: var(--warning-color_h), var(--warning-color_s), var(--warning-color_l);
|
--warning-color_hsl: var(--warning-color_h), var(--warning-color_s), var(--warning-color_l);
|
||||||
--accent-color_h: calc(var(--brand-color_h) - 15);
|
|
||||||
--accent-color_s: 86%;
|
|
||||||
--accent-color_l: 44%;
|
|
||||||
|
|
||||||
// Modifiers
|
// Modifiers
|
||||||
--brand-color--faint: hsla(var(--brand-color_hsl), 0.1);
|
--brand-color--faint: hsla(var(--brand-color_hsl), 0.1);
|
||||||
|
|
Ładowanie…
Reference in New Issue