diff --git a/app/icons/COPYING.md b/app/icons/COPYING.md
index d99855b13..de5c4fcf8 100644
--- a/app/icons/COPYING.md
+++ b/app/icons/COPYING.md
@@ -2,11 +2,9 @@
- dashboard-filled.svg - Modified from Tabler icons, MIT
- fediverse.svg - Modified from Wikipedia, CC0
-- gavel.svg - Created by ramsha61 for this project, MIT
- home-squared.svg - Modified from Tabler icons, MIT
- pen-plus.svg - Modified from Tabler icons, MIT
Tabler: https://tabler-icons.io/
Feather: https://feathericons.com/
Fediverse logo: https://en.wikipedia.org/wiki/Fediverse#/media/File:Fediverse_logo_proposal.svg
-ramsha61: https://www.fiverr.com/ramsha61
diff --git a/app/icons/gavel.svg b/app/icons/gavel.svg
deleted file mode 100644
index 38a0ff272..000000000
--- a/app/icons/gavel.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/app/soapbox/__fixtures__/alex.json b/app/soapbox/__fixtures__/alex.json
new file mode 100644
index 000000000..022978fde
--- /dev/null
+++ b/app/soapbox/__fixtures__/alex.json
@@ -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": "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"
+ }
+ ],
+ "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.
I'm vegan btw
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"
+}
diff --git a/app/soapbox/__fixtures__/fedibird-instance.json b/app/soapbox/__fixtures__/fedibird-instance.json
new file mode 100644
index 000000000..31e17e34e
--- /dev/null
+++ b/app/soapbox/__fixtures__/fedibird-instance.json
@@ -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": "
主に、Fediverseへの関心に基づいた投稿を行うアカウントです。DTP・印刷に関する話をしたり、同人の話をしたり、カレーをブーストしたりします。
Mastodonサーバ『Fedibird』の管理者アカウントでもあります。ご連絡は当アカウントへ、サーバインフォメーションについては https://fedibird.com/about/more と @info を参照してください。
", + "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": "https://liberapay.com/noellabo", + "verified_at": "2020-10-22T03:04:43.206+00:00" + }, + { + "name": ":mastodon: DTP-Mstdn.jp", + "value": "https://dtp-mstdn.jp/@noellabo", + "verified_at": "2020-05-23T00:14:02.232+00:00" + }, + { + "name": "別宅", + "value": "https://gorone.xyz/@noellabo", + "verified_at": "2021-08-11T07:48:53.479+00:00" + }, + { + "name": "bluesky community", + "value": "https://mastodon.blueskycommunity.net/@noellabo", + "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・閲覧注意を使用してください" + } + ] +} diff --git a/app/soapbox/__fixtures__/friendica-instance.json b/app/soapbox/__fixtures__/friendica-instance.json new file mode 100644 index 000000000..cb6902d1f --- /dev/null +++ b/app/soapbox/__fixtures__/friendica-instance.json @@ -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": [] +} diff --git a/app/soapbox/__fixtures__/friendica-status.json b/app/soapbox/__fixtures__/friendica-status.json new file mode 100644 index 000000000..fc64e430e --- /dev/null +++ b/app/soapbox/__fixtures__/friendica-status.json @@ -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 +} diff --git a/app/soapbox/__fixtures__/gotosocial-account.json b/app/soapbox/__fixtures__/gotosocial-account.json new file mode 100644 index 000000000..3700bc473 --- /dev/null +++ b/app/soapbox/__fixtures__/gotosocial-account.json @@ -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": "My GoToSocial profile
", + "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": "My GoToSocial profile
", + "fields": [] + } +} diff --git a/app/soapbox/__fixtures__/gotosocial-instance.json b/app/soapbox/__fixtures__/gotosocial-instance.json new file mode 100644 index 000000000..fdaf4c96b --- /dev/null +++ b/app/soapbox/__fixtures__/gotosocial-instance.json @@ -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 +} diff --git a/app/soapbox/__fixtures__/gotosocial-status.json b/app/soapbox/__fixtures__/gotosocial-status.json new file mode 100644 index 000000000..3546482b2 --- /dev/null +++ b/app/soapbox/__fixtures__/gotosocial-status.json @@ -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": "Hello GoToSocial!
", + "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!" +} diff --git a/app/soapbox/__fixtures__/mastodon-reply-to-self.json b/app/soapbox/__fixtures__/mastodon-reply-to-self.json new file mode 100644 index 000000000..7cfc756f3 --- /dev/null +++ b/app/soapbox/__fixtures__/mastodon-reply-to-self.json @@ -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": "test reply to self
", + "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": "", + "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 +} diff --git a/app/soapbox/__fixtures__/mitra-instance.json b/app/soapbox/__fixtures__/mitra-instance.json new file mode 100644 index 000000000..2c476ba30 --- /dev/null +++ b/app/soapbox/__fixtures__/mitra-instance.json @@ -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" +} diff --git a/app/soapbox/__fixtures__/status-custom-emoji.json b/app/soapbox/__fixtures__/status-custom-emoji.json new file mode 100644 index 000000000..ac3f184ee --- /dev/null +++ b/app/soapbox/__fixtures__/status-custom-emoji.json @@ -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" +} diff --git a/app/soapbox/__fixtures__/status-cw.json b/app/soapbox/__fixtures__/status-cw.json new file mode 100644 index 000000000..af9978cfb --- /dev/null +++ b/app/soapbox/__fixtures__/status-cw.json @@ -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": "hello world
", + "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": "", + "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 +} diff --git a/app/soapbox/__fixtures__/status-with-poll.json b/app/soapbox/__fixtures__/status-with-poll.json new file mode 100644 index 000000000..9dfd90d7e --- /dev/null +++ b/app/soapbox/__fixtures__/status-with-poll.json @@ -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": "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" + } + ], + "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.What is tolerance?
", + "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" +} diff --git a/app/soapbox/actions/account_notes.js b/app/soapbox/actions/account_notes.js new file mode 100644 index 000000000..d6aeefc49 --- /dev/null +++ b/app/soapbox/actions/account_notes.js @@ -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, + }; +} \ No newline at end of file diff --git a/app/soapbox/actions/importer/index.js b/app/soapbox/actions/importer/index.js index 3a019e636..56efcf651 100644 --- a/app/soapbox/actions/importer/index.js +++ b/app/soapbox/actions/importer/index.js @@ -2,7 +2,6 @@ import { getSettings } from '../settings'; import { normalizeAccount, - normalizeStatus, normalizePoll, } from './normalizer'; @@ -22,11 +21,17 @@ export function importAccounts(accounts) { } 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) { - 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) { @@ -60,11 +65,6 @@ export function importFetchedStatus(status, idempotencyKey) { // Skip broken statuses 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) { dispatch(importFetchedStatus(status.reblog)); } @@ -83,7 +83,7 @@ export function importFetchedStatus(status, idempotencyKey) { } dispatch(importFetchedAccount(status.account)); - dispatch(importStatus(normalizedStatus, idempotencyKey)); + dispatch(importStatus(status, idempotencyKey)); }; } @@ -113,10 +113,7 @@ export function importFetchedStatuses(statuses) { // Skip broken statuses if (isBroken(status)) return; - const normalOldStatus = getState().getIn(['statuses', status.id]); - const expandSpoilers = getSettings(getState()).get('expandSpoilers'); - - normalStatuses.push(normalizeStatus(status, normalOldStatus, expandSpoilers)); + normalStatuses.push(status); accounts.push(status.account); if (status.reblog?.id) { diff --git a/app/soapbox/actions/importer/normalizer.js b/app/soapbox/actions/importer/normalizer.js index 7d40357f0..4de148c0b 100644 --- a/app/soapbox/actions/importer/normalizer.js +++ b/app/soapbox/actions/importer/normalizer.js @@ -1,12 +1,8 @@ import escapeTextContentForBrowser from 'escape-html'; -import { stripCompatibilityFeatures } from 'soapbox/utils/html'; - import emojify from '../../features/emoji/emoji'; import { unescapeHTML } from '../../utils/html'; -const domParser = new DOMParser(); - const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => { obj[`:${emoji.shortcode}:`] = emoji; return obj; @@ -45,61 +41,6 @@ export function normalizeAccount(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(//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) {
const normalPoll = { ...poll };
diff --git a/app/soapbox/actions/soapbox.js b/app/soapbox/actions/soapbox.js
index d85bd0f0f..03814cf7c 100644
--- a/app/soapbox/actions/soapbox.js
+++ b/app/soapbox/actions/soapbox.js
@@ -40,6 +40,7 @@ export const makeDefaultConfig = features => {
logo: '',
banner: '',
brandColor: '', // Empty
+ accentColor: '',
customCss: ImmutableList(),
promoPanel: ImmutableMap({
items: ImmutableList(),
@@ -59,6 +60,8 @@ export const makeDefaultConfig = features => {
}),
aboutPages: ImmutableMap(),
authenticatedProfile: true,
+ singleUserMode: false,
+ singleUserModeProfile: '',
});
};
diff --git a/app/soapbox/components/error_boundary.js b/app/soapbox/components/error_boundary.js
index b73944575..2e7c3f717 100644
--- a/app/soapbox/components/error_boundary.js
+++ b/app/soapbox/components/error_boundary.js
@@ -4,6 +4,7 @@ import { FormattedMessage } from 'react-intl';
import Icon from 'soapbox/components/icon';
import { captureException } from 'soapbox/monitoring';
+import sourceCode from 'soapbox/utils/code';
export default class ErrorBoundary extends React.PureComponent {
@@ -72,7 +73,7 @@ export default class ErrorBoundary extends React.PureComponent {
{browser.getBrowserName()} {browser.getBrowserVersion()}
} +{sourceCode.displayName} {sourceCode.version}
/g, '\n\n');
+ const emojiMap = makeEmojiMap(status);
- // 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);
+ 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 => {
return Boolean(status.get('quote_id') || status.getIn(['pleroma', 'quote_url']));
};
// Preserve quote if an existing status already has it
-const fixQuote = (state, status) => {
- const oldStatus = state.get(status.get('id'));
-
+const fixQuote = (status, oldStatus) => {
if (oldStatus && !status.get('quote') && isQuote(status)) {
return status
.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 => {
- fixMentions(status);
- fixQuote(state, status);
- normalizeAttachments(status);
+ normalizeStatus(status);
+ fixQuote(status, oldStatus);
+ 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) =>
- state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status)));
+const importStatuses = (state, statuses, expandSpoilers) =>
+ state.withMutations(mutable => statuses.forEach(status => importStatus(mutable, status, expandSpoilers)));
const deleteStatus = (state, id, references) => {
references.forEach(ref => {
@@ -126,9 +132,9 @@ const initialState = ImmutableMap();
export default function statuses(state = initialState, action) {
switch(action.type) {
case STATUS_IMPORT:
- return importStatus(state, action.status);
+ return importStatus(state, action.status, action.expandSpoilers);
case STATUSES_IMPORT:
- return importStatuses(state, action.statuses);
+ return importStatuses(state, action.statuses, action.expandSpoilers);
case STATUS_CREATE_REQUEST:
return importPendingStatus(state, action.params);
case STATUS_CREATE_FAIL:
diff --git a/app/soapbox/utils/__tests__/accounts-test.js b/app/soapbox/utils/__tests__/accounts-test.js
index 15a42ec57..d3fd6f9ba 100644
--- a/app/soapbox/utils/__tests__/accounts-test.js
+++ b/app/soapbox/utils/__tests__/accounts-test.js
@@ -6,6 +6,7 @@ import {
isStaff,
isAdmin,
isModerator,
+ accountToMention,
} from '../accounts';
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);
+ });
+});
diff --git a/app/soapbox/utils/accounts.ts b/app/soapbox/utils/accounts.ts
index 361ecbc25..a6d418968 100644
--- a/app/soapbox/utils/accounts.ts
+++ b/app/soapbox/utils/accounts.ts
@@ -71,3 +71,12 @@ export const isVerified = (account: ImmutableMap