summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--config/config.exs10
-rw-r--r--config/description.exs39
-rw-r--r--docs/API/pleroma_api.md7
-rw-r--r--docs/configuration/cheatsheet.md29
-rw-r--r--lib/mix/tasks/pleroma/emoji.ex28
-rw-r--r--lib/pleroma/conversation/participation.ex13
-rw-r--r--lib/pleroma/marker.ex74
-rw-r--r--lib/pleroma/notification.ex15
-rw-r--r--lib/pleroma/user.ex3
-rw-r--r--lib/pleroma/user/info.ex12
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex4
-rw-r--r--lib/pleroma/web/activity_pub/relay.ex8
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex10
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex10
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex3
-rw-r--r--lib/pleroma/web/masto_fe_controller.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/marker_controller.ex32
-rw-r--r--lib/pleroma/web/mastodon_api/views/marker_view.ex17
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex9
-rw-r--r--lib/pleroma/web/router.ex10
-rw-r--r--lib/pleroma/web/templates/masto_fe/index.html.eex6
-rw-r--r--lib/pleroma/web/views/masto_fe_view.ex19
-rw-r--r--mix.exs2
-rw-r--r--priv/repo/migrations/20191014181019_create_markers.exs15
-rw-r--r--priv/repo/migrations/20191017225002_drop_websub_tables.exs8
-rw-r--r--priv/static/schemas/litepub-0.1.jsonld1
-rw-r--r--test/conversation/participation_test.exs14
-rw-r--r--test/fixtures/tesla_mock/relay@mastdon.example.org.json55
-rw-r--r--test/marker_test.exs51
-rw-r--r--test/notification_test.exs12
-rw-r--r--test/support/factory.ex9
-rw-r--r--test/support/http_request_mock.ex8
-rw-r--r--test/user_test.exs14
-rw-r--r--test/web/activity_pub/activity_pub_test.exs6
-rw-r--r--test/web/activity_pub/relay_test.exs6
-rw-r--r--test/web/activity_pub/views/user_view_test.exs6
-rw-r--r--test/web/mastodon_api/controllers/marker_controller_test.exs124
-rw-r--r--test/web/mastodon_api/controllers/status_controller_test.exs32
-rw-r--r--test/web/mastodon_api/views/marker_view_test.exs27
-rw-r--r--test/web/ostatus/ostatus_controller_test.exs1
-rw-r--r--test/web/pleroma_api/controllers/pleroma_api_controller_test.exs27
42 files changed, 747 insertions, 37 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d516080b4..d7be06bf9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -50,6 +50,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
+- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
+- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
### Changed
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
diff --git a/config/config.exs b/config/config.exs
index d0766a6e2..a69d41d17 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -322,6 +322,16 @@ config :pleroma, :assets,
],
default_mascot: :pleroma_fox_tan
+config :pleroma, :manifest,
+ icons: [
+ %{
+ src: "/static/logo.png",
+ type: "image/png"
+ }
+ ],
+ theme_color: "#282c37",
+ background_color: "#191b22"
+
config :pleroma, :activitypub,
unfollow_blocked: true,
outgoing_blocks: true,
diff --git a/config/description.exs b/config/description.exs
index 571c64bc1..70e963399 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -1100,6 +1100,45 @@ config :pleroma, :config_description, [
},
%{
group: :pleroma,
+ key: :manifest,
+ type: :group,
+ description:
+ "This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE",
+ children: [
+ %{
+ key: :icons,
+ type: {:list, :map},
+ description: "Describe the icons of the app",
+ suggestion: [
+ %{
+ src: "/static/logo.png"
+ },
+ %{
+ src: "/static/icon.png",
+ type: "image/png"
+ },
+ %{
+ src: "/static/icon.ico",
+ sizes: "72x72 96x96 128x128 256x256"
+ }
+ ]
+ },
+ %{
+ key: :theme_color,
+ type: :string,
+ description: "Describe the theme color of the app",
+ suggestions: ["#282c37", "mediumpurple"]
+ },
+ %{
+ key: :background_color,
+ type: :string,
+ description: "Describe the background color of the app",
+ suggestions: ["#191b22", "aliceblue"]
+ }
+ ]
+ },
+ %{
+ group: :pleroma,
key: :mrf_simple,
type: :group,
description: "Message Rewrite Facility",
diff --git a/docs/API/pleroma_api.md b/docs/API/pleroma_api.md
index 0517bbdd7..6c326dc9b 100644
--- a/docs/API/pleroma_api.md
+++ b/docs/API/pleroma_api.md
@@ -367,6 +367,13 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
* `recipients`: A list of ids of users that should receive posts to this conversation. This will replace the current list of recipients, so submit the full list. The owner of owner of the conversation will always be part of the set of recipients, though.
* Response: JSON, statuses (200 - healthy, 503 unhealthy)
+## `GET /api/v1/pleroma/conversations/read`
+### Marks all user's conversations as read.
+* Method `POST`
+* Authentication: required
+* Params: None
+* Response: JSON, returns a list of Mastodon Conversation entities that were marked as read (200 - healthy, 503 unhealthy).
+
## `GET /api/pleroma/emoji/packs`
### Lists the custom emoji packs on the server
* Method `GET`
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index 8d85276ed..3427ae419 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -247,6 +247,35 @@ relates to mascots on the mastodon frontend
* `default_mascot`: An element from `mascots` - This will be used as the default mascot
on MastoFE (default: `:pleroma_fox_tan`)
+## :manifest
+
+This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE.
+
+* `icons`: Describe the icons of the app, this a list of maps describing icons in the same way as the
+ [spec](https://www.w3.org/TR/appmanifest/#imageresource-and-its-members) describes it.
+
+ Example:
+
+ ```elixir
+ config :pleroma, :manifest,
+ icons: [
+ %{
+ src: "/static/logo.png"
+ },
+ %{
+ src: "/static/icon.png",
+ type: "image/png"
+ },
+ %{
+ src: "/static/icon.ico",
+ sizes: "72x72 96x96 128x128 256x256"
+ }
+ ]
+ ```
+
+* `theme_color`: Describe the theme color of the app. (Example: `"#282c37"`, `"rebeccapurple"`)
+* `background_color`: Describe the background color of the app. (Example: `"#191b22"`, `"aliceblue"`)
+
## :mrf_simple
* `media_removal`: List of instances to remove medias from
* `media_nsfw`: List of instances to put medias as NSFW(sensitive) from
diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex
index 6ef0a635d..35669af27 100644
--- a/lib/mix/tasks/pleroma/emoji.ex
+++ b/lib/mix/tasks/pleroma/emoji.ex
@@ -111,19 +111,21 @@ defmodule Mix.Tasks.Pleroma.Emoji do
file_list: files_to_unzip
)
- IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
-
- emoji_txt_str =
- Enum.map(
- files,
- fn {shortcode, path} ->
- emojo_path = Path.join("/emoji/#{pack_name}", path)
- "#{shortcode}, #{emojo_path}"
- end
- )
- |> Enum.join("\n")
-
- File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
+ IO.puts(IO.ANSI.format(["Writing pack.json for ", :bright, pack_name]))
+
+ pack_json = %{
+ pack: %{
+ "license" => pack["license"],
+ "homepage" => pack["homepage"],
+ "description" => pack["description"],
+ "fallback-src" => pack["src"],
+ "fallback-src-sha256" => pack["src_sha256"],
+ "share-files" => true
+ },
+ files: files
+ }
+
+ File.write!(Path.join(pack_path, "pack.json"), Jason.encode!(pack_json, pretty: true))
else
IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
end
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index e17f49e58..41918fa78 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -69,6 +69,19 @@ defmodule Pleroma.Conversation.Participation do
end
end
+ def mark_all_as_read(user) do
+ {_, participations} =
+ __MODULE__
+ |> where([p], p.user_id == ^user.id)
+ |> where([p], not p.read)
+ |> update([p], set: [read: true])
+ |> select([p], p)
+ |> Repo.update_all([])
+
+ User.set_unread_conversation_count(user)
+ {:ok, participations}
+ end
+
def mark_as_unread(participation) do
participation
|> read_cng(%{read: false})
diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex
new file mode 100644
index 000000000..7f87c86c3
--- /dev/null
+++ b/lib/pleroma/marker.ex
@@ -0,0 +1,74 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Marker do
+ use Ecto.Schema
+
+ import Ecto.Changeset
+ import Ecto.Query
+
+ alias Ecto.Multi
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ @timelines ["notifications"]
+
+ schema "markers" do
+ field(:last_read_id, :string, default: "")
+ field(:timeline, :string, default: "")
+ field(:lock_version, :integer, default: 0)
+
+ belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
+ timestamps()
+ end
+
+ def get_markers(user, timelines \\ []) do
+ Repo.all(get_query(user, timelines))
+ end
+
+ def upsert(%User{} = user, attrs) do
+ attrs
+ |> Map.take(@timelines)
+ |> Enum.reduce(Multi.new(), fn {timeline, timeline_attrs}, multi ->
+ marker =
+ user
+ |> get_marker(timeline)
+ |> changeset(timeline_attrs)
+
+ Multi.insert(multi, timeline, marker,
+ returning: true,
+ on_conflict: {:replace, [:last_read_id]},
+ conflict_target: [:user_id, :timeline]
+ )
+ end)
+ |> Repo.transaction()
+ end
+
+ defp get_marker(user, timeline) do
+ case Repo.find_resource(get_query(user, timeline)) do
+ {:ok, marker} -> %__MODULE__{marker | user: user}
+ _ -> %__MODULE__{timeline: timeline, user_id: user.id}
+ end
+ end
+
+ @doc false
+ defp changeset(marker, attrs) do
+ marker
+ |> cast(attrs, [:last_read_id])
+ |> validate_required([:user_id, :timeline, :last_read_id])
+ |> validate_inclusion(:timeline, @timelines)
+ end
+
+ defp by_timeline(query, timeline) do
+ from(m in query, where: m.timeline in ^List.wrap(timeline))
+ end
+
+ defp by_user_id(query, id), do: from(m in query, where: m.user_id == ^id)
+
+ defp get_query(user, timelines) do
+ __MODULE__
+ |> by_user_id(user.id)
+ |> by_timeline(timelines)
+ end
+end
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index d145f8d5b..e5da1492b 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -55,9 +55,19 @@ defmodule Pleroma.Notification do
)
|> preload([n, a, o], activity: {a, object: o})
|> exclude_muted(user, opts)
+ |> exclude_blocked(user)
|> exclude_visibility(opts)
end
+ defp exclude_blocked(query, user) do
+ query
+ |> where([n, a], a.actor not in ^user.info.blocks)
+ |> where(
+ [n, a],
+ fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
+ )
+ end
+
defp exclude_muted(query, _, %{with_muted: true}) do
query
end
@@ -65,11 +75,6 @@ defmodule Pleroma.Notification do
defp exclude_muted(query, user, _opts) do
query
|> where([n, a], a.actor not in ^user.info.muted_notifications)
- |> where([n, a], a.actor not in ^user.info.blocks)
- |> where(
- [n, a],
- fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
- )
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index ec705b8f6..2bbfaa55b 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -88,6 +88,9 @@ defmodule Pleroma.User do
def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true
def superuser?(_), do: false
+ def invisible?(%User{info: %User.Info{invisible: true}}), do: true
+ def invisible?(_), do: false
+
def avatar_url(user, options \\ []) do
case user.avatar do
%{"url" => [%{"href" => href} | _]} -> href
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 2d39abcb3..982fb61c6 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -53,6 +53,7 @@ defmodule Pleroma.User.Info do
field(:fields, {:array, :map}, default: nil)
field(:raw_fields, {:array, :map}, default: [])
field(:discoverable, :boolean, default: false)
+ field(:invisible, :boolean, default: false)
field(:notification_settings, :map,
default: %{
@@ -266,7 +267,8 @@ defmodule Pleroma.User.Info do
:follower_count,
:fields,
:following_count,
- :discoverable
+ :discoverable,
+ :invisible
])
|> validate_fields(true)
end
@@ -393,6 +395,14 @@ defmodule Pleroma.User.Info do
|> validate_required([:source_data])
end
+ def set_invisible(info, invisible) do
+ params = %{invisible: invisible}
+
+ info
+ |> cast(params, [:invisible])
+ |> validate_required([:invisible])
+ end
+
def admin_api_update(info, params) do
info
|> cast(params, [
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 94c467b69..9a0a3522a 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1106,6 +1106,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
locked = data["manuallyApprovesFollowers"] || false
data = Transmogrifier.maybe_fix_user_object(data)
discoverable = data["discoverable"] || false
+ invisible = data["invisible"] || false
user_data = %{
ap_id: data["id"],
@@ -1115,7 +1116,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
banner: banner,
fields: fields,
locked: locked,
- discoverable: discoverable
+ discoverable: discoverable,
+ invisible: invisible
},
avatar: avatar,
name: data["name"],
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
index 03fc434a9..de80612f1 100644
--- a/lib/pleroma/web/activity_pub/relay.ex
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -10,8 +10,12 @@ defmodule Pleroma.Web.ActivityPub.Relay do
require Logger
def get_actor do
- "#{Pleroma.Web.Endpoint.url()}/relay"
- |> User.get_or_create_service_actor_by_ap_id()
+ actor =
+ "#{Pleroma.Web.Endpoint.url()}/relay"
+ |> User.get_or_create_service_actor_by_ap_id()
+
+ {:ok, actor} = User.update_info(actor, &User.Info.set_invisible(&1, true))
+ actor
end
@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 2c1ce9c55..4a250d131 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -596,13 +596,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
data,
_options
)
- when object_type in ["Person", "Application", "Service", "Organization"] do
+ when object_type in [
+ "Person",
+ "Application",
+ "Service",
+ "Organization"
+ ] do
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
banner = new_user_data[:info][:banner]
locked = new_user_data[:info][:locked] || false
attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || []
+ invisible = new_user_data[:info][:invisible] || false
fields =
attachment
@@ -612,7 +618,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
update_data =
new_user_data
|> Map.take([:name, :bio, :avatar])
- |> Map.put(:info, %{banner: banner, locked: locked, fields: fields})
+ |> Map.put(:info, %{banner: banner, locked: locked, fields: fields, invisible: invisible})
actor
|> User.upgrade_changeset(update_data, true)
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 4ef479f96..6b28df92c 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -491,10 +491,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do
%Activity{data: %{"actor" => actor}},
object
) do
- announcements = take_announcements(object)
+ unless actor |> User.get_cached_by_ap_id() |> User.invisible?() do
+ announcements = take_announcements(object)
- with announcements <- Enum.uniq([actor | announcements]) do
- update_element_in_object("announcement", announcements, object)
+ with announcements <- Enum.uniq([actor | announcements]) do
+ update_element_in_object("announcement", announcements, object)
+ end
+ else
+ {:ok, object}
end
end
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 9b39d1629..8c5b4460b 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -55,7 +55,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"owner" => user.ap_id,
"publicKeyPem" => public_key
},
- "endpoints" => endpoints
+ "endpoints" => endpoints,
+ "invisible" => User.invisible?(user)
}
|> Map.merge(Utils.make_json_ld_header())
end
diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex
index 87860f1d5..93b38e8f4 100644
--- a/lib/pleroma/web/masto_fe_controller.ex
+++ b/lib/pleroma/web/masto_fe_controller.ex
@@ -34,6 +34,12 @@ defmodule Pleroma.Web.MastoFEController do
end
end
+ @doc "GET /web/manifest.json"
+ def manifest(conn, _params) do
+ conn
+ |> render("manifest.json")
+ end
+
@doc "PUT /api/web/settings"
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do
diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex
new file mode 100644
index 000000000..ce025624d
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex
@@ -0,0 +1,32 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerController do
+ use Pleroma.Web, :controller
+ alias Pleroma.Plugs.OAuthScopesPlug
+
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["read:statuses"]}
+ when action == :index
+ )
+
+ plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
+ plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
+ action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+
+ # GET /api/v1/markers
+ def index(%{assigns: %{user: user}} = conn, params) do
+ markers = Pleroma.Marker.get_markers(user, params["timeline"])
+ render(conn, "markers.json", %{markers: markers})
+ end
+
+ # POST /api/v1/markers
+ def upsert(%{assigns: %{user: user}} = conn, params) do
+ with {:ok, result} <- Pleroma.Marker.upsert(user, params),
+ markers <- Map.values(result) do
+ render(conn, "markers.json", %{markers: markers})
+ end
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex
new file mode 100644
index 000000000..38fbeed5f
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerView do
+ use Pleroma.Web, :view
+
+ def render("markers.json", %{markers: markers}) do
+ Enum.reduce(markers, %{}, fn m, acc ->
+ Map.put_new(acc, m.timeline, %{
+ last_read_id: m.last_read_id,
+ version: m.lock_version,
+ updated_at: NaiveDateTime.to_iso8601(m.updated_at)
+ })
+ end)
+ end
+end
diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
index 9d50a7ca9..fc39abf05 100644
--- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
@@ -79,6 +79,15 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
end
end
+ def read_conversations(%{assigns: %{user: user}} = conn, _params) do
+ with {:ok, participations} <- Participation.mark_all_as_read(user) do
+ conn
+ |> add_link_headers(participations)
+ |> put_view(ConversationView)
+ |> render("participations.json", participations: participations, for: user)
+ end
+ end
+
def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
with {:ok, notification} <- Notification.read_one(user, notification_id) do
conn
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index d68fb87da..f69c5c2bc 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -266,6 +266,7 @@ defmodule Pleroma.Web.Router do
get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
get("/conversations/:id", PleromaAPIController, :conversation)
+ post("/conversations/read", PleromaAPIController, :read_conversations)
end
scope [] do
@@ -404,6 +405,9 @@ defmodule Pleroma.Web.Router do
get("/push/subscription", SubscriptionController, :get)
put("/push/subscription", SubscriptionController, :update)
delete("/push/subscription", SubscriptionController, :delete)
+
+ get("/markers", MarkerController, :index)
+ post("/markers", MarkerController, :upsert)
end
scope "/api/web", Pleroma.Web do
@@ -592,6 +596,12 @@ defmodule Pleroma.Web.Router do
end
scope "/", Pleroma.Web do
+ pipe_through(:api)
+
+ get("/web/manifest.json", MastoFEController, :manifest)
+ end
+
+ scope "/", Pleroma.Web do
pipe_through(:mastodon_html)
get("/web/login", MastodonAPI.AuthController, :login)
diff --git a/lib/pleroma/web/templates/masto_fe/index.html.eex b/lib/pleroma/web/templates/masto_fe/index.html.eex
index feff36fae..c330960fa 100644
--- a/lib/pleroma/web/templates/masto_fe/index.html.eex
+++ b/lib/pleroma/web/templates/masto_fe/index.html.eex
@@ -4,9 +4,13 @@
<meta charset='utf-8'>
<meta content='width=device-width, initial-scale=1' name='viewport'>
<title>
-<%= Pleroma.Config.get([:instance, :name]) %>
+<%= Config.get([:instance, :name]) %>
</title>
<link rel="icon" type="image/png" href="/favicon.png"/>
+<link rel="manifest" type="applicaton/manifest+json" href="<%= masto_fe_path(Pleroma.Web.Endpoint, :manifest) %>" />
+
+<meta name="theme-color" content="<%= Config.get([:manifest, :theme_color]) %>" />
+
<script crossorigin='anonymous' src="/packs/locales.js"></script>
<script crossorigin='anonymous' src="/packs/locales/glitch/en.js"></script>
diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex
index 21b086d4c..85b164b59 100644
--- a/lib/pleroma/web/views/masto_fe_view.ex
+++ b/lib/pleroma/web/views/masto_fe_view.ex
@@ -99,4 +99,23 @@ defmodule Pleroma.Web.MastoFEView do
defp present?(nil), do: false
defp present?(false), do: false
defp present?(_), do: true
+
+ def render("manifest.json", _params) do
+ %{
+ name: Config.get([:instance, :name]),
+ description: Config.get([:instance, :description]),
+ icons: Config.get([:manifest, :icons]),
+ theme_color: Config.get([:manifest, :theme_color]),
+ background_color: Config.get([:manifest, :background_color]),
+ display: "standalone",
+ scope: Pleroma.Web.base_url(),
+ start_url: masto_fe_path(Pleroma.Web.Endpoint, :index, ["getting-started"]),
+ categories: [
+ "social"
+ ],
+ serviceworker: %{
+ src: "/sw.js"
+ }
+ }
+ end
end
diff --git a/mix.exs b/mix.exs
index 120092f1b..dcb9d9ea8 100644
--- a/mix.exs
+++ b/mix.exs
@@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
def project do
[
app: :pleroma,
- version: version("1.0.0"),
+ version: version("1.1.50"),
elixir: "~> 1.8",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
diff --git a/priv/repo/migrations/20191014181019_create_markers.exs b/priv/repo/migrations/20191014181019_create_markers.exs
new file mode 100644
index 000000000..c717831ba
--- /dev/null
+++ b/priv/repo/migrations/20191014181019_create_markers.exs
@@ -0,0 +1,15 @@
+defmodule Pleroma.Repo.Migrations.CreateMarkers do
+ use Ecto.Migration
+
+ def change do
+ create_if_not_exists table(:markers) do
+ add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
+ add(:timeline, :string, default: "", null: false)
+ add(:last_read_id, :string, default: "", null: false)
+ add(:lock_version, :integer, default: 0, null: false)
+ timestamps()
+ end
+
+ create_if_not_exists(unique_index(:markers, [:user_id, :timeline]))
+ end
+end
diff --git a/priv/repo/migrations/20191017225002_drop_websub_tables.exs b/priv/repo/migrations/20191017225002_drop_websub_tables.exs
new file mode 100644
index 000000000..9e483b2a1
--- /dev/null
+++ b/priv/repo/migrations/20191017225002_drop_websub_tables.exs
@@ -0,0 +1,8 @@
+defmodule Pleroma.Repo.Migrations.DropWebsubTables do
+ use Ecto.Migration
+
+ def change do
+ drop_if_exists(table(:websub_client_subscriptions))
+ drop_if_exists(table(:websub_server_subscriptions))
+ end
+end
diff --git a/priv/static/schemas/litepub-0.1.jsonld b/priv/static/schemas/litepub-0.1.jsonld
index 1cfcb7ec7..c8e69cab5 100644
--- a/priv/static/schemas/litepub-0.1.jsonld
+++ b/priv/static/schemas/litepub-0.1.jsonld
@@ -19,6 +19,7 @@
"value": "schema:value",
"sensitive": "as:sensitive",
"litepub": "http://litepub.social/ns#",
+ "invisible": "litepub:invisible",
"directMessage": "litepub:directMessage",
"listMessage": {
"@id": "litepub:listMessage",
diff --git a/test/conversation/participation_test.exs b/test/conversation/participation_test.exs
index a5af0d1b2..64c350904 100644
--- a/test/conversation/participation_test.exs
+++ b/test/conversation/participation_test.exs
@@ -133,6 +133,20 @@ defmodule Pleroma.Conversation.ParticipationTest do
refute participation.read
end
+ test "it marks all the user's participations as read" do
+ user = insert(:user)
+ other_user = insert(:user)
+ participation1 = insert(:participation, %{read: false, user: user})
+ participation2 = insert(:participation, %{read: false, user: user})
+ participation3 = insert(:participation, %{read: false, user: other_user})
+
+ {:ok, [%{read: true}, %{read: true}]} = Participation.mark_all_as_read(user)
+
+ assert Participation.get(participation1.id).read == true
+ assert Participation.get(participation2.id).read == true
+ assert Participation.get(participation3.id).read == false
+ end
+
test "gets all the participations for a user, ordered by updated at descending" do
user = insert(:user)
{:ok, activity_one} = CommonAPI.post(user, %{"status" => "x", "visibility" => "direct"})
diff --git a/test/fixtures/tesla_mock/relay@mastdon.example.org.json b/test/fixtures/tesla_mock/relay@mastdon.example.org.json
new file mode 100644
index 000000000..c1fab7d3b
--- /dev/null
+++ b/test/fixtures/tesla_mock/relay@mastdon.example.org.json
@@ -0,0 +1,55 @@
+{
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "sensitive": "as:sensitive",
+ "movedTo": "as:movedTo",
+ "Hashtag": "as:Hashtag",
+ "ostatus": "http://ostatus.org#",
+ "atomUri": "ostatus:atomUri",
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "conversation": "ostatus:conversation",
+ "toot": "http://joinmastodon.org/ns#",
+ "Emoji": "toot:Emoji"
+ }],
+ "id": "http://mastodon.example.org/users/admin",
+ "type": "Application",
+ "invisible": true,
+ "following": "http://mastodon.example.org/users/admin/following",
+ "followers": "http://mastodon.example.org/users/admin/followers",
+ "inbox": "http://mastodon.example.org/users/admin/inbox",
+ "outbox": "http://mastodon.example.org/users/admin/outbox",
+ "preferredUsername": "admin",
+ "name": null,
+ "summary": "\u003cp\u003e\u003c/p\u003e",
+ "url": "http://mastodon.example.org/@admin",
+ "manuallyApprovesFollowers": false,
+ "publicKey": {
+ "id": "http://mastodon.example.org/users/admin#main-key",
+ "owner": "http://mastodon.example.org/users/admin",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtc4Tir+3ADhSNF6VKrtW\nOU32T01w7V0yshmQei38YyiVwVvFu8XOP6ACchkdxbJ+C9mZud8qWaRJKVbFTMUG\nNX4+6Q+FobyuKrwN7CEwhDALZtaN2IPbaPd6uG1B7QhWorrY+yFa8f2TBM3BxnUy\nI4T+bMIZIEYG7KtljCBoQXuTQmGtuffO0UwJksidg2ffCF5Q+K//JfQagJ3UzrR+\nZXbKMJdAw4bCVJYs4Z5EhHYBwQWiXCyMGTd7BGlmMkY6Av7ZqHKC/owp3/0EWDNz\nNqF09Wcpr3y3e8nA10X40MJqp/wR+1xtxp+YGbq/Cj5hZGBG7etFOmIpVBrDOhry\nBwIDAQAB\n-----END PUBLIC KEY-----\n"
+ },
+ "attachment": [{
+ "type": "PropertyValue",
+ "name": "foo",
+ "value": "bar"
+ },
+ {
+ "type": "PropertyValue",
+ "name": "foo1",
+ "value": "bar1"
+ }
+ ],
+ "endpoints": {
+ "sharedInbox": "http://mastodon.example.org/inbox"
+ },
+ "icon": {
+ "type": "Image",
+ "mediaType": "image/jpeg",
+ "url": "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
+ },
+ "image": {
+ "type": "Image",
+ "mediaType": "image/png",
+ "url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
+ }
+}
diff --git a/test/marker_test.exs b/test/marker_test.exs
new file mode 100644
index 000000000..04bd67fe6
--- /dev/null
+++ b/test/marker_test.exs
@@ -0,0 +1,51 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.MarkerTest do
+ use Pleroma.DataCase
+ alias Pleroma.Marker
+
+ import Pleroma.Factory
+
+ describe "get_markers/2" do
+ test "returns user markers" do
+ user = insert(:user)
+ marker = insert(:marker, user: user)
+ insert(:marker, timeline: "home", user: user)
+ assert Marker.get_markers(user, ["notifications"]) == [refresh_record(marker)]
+ end
+ end
+
+ describe "upsert/2" do
+ test "creates a marker" do
+ user = insert(:user)
+
+ {:ok, %{"notifications" => %Marker{} = marker}} =
+ Marker.upsert(
+ user,
+ %{"notifications" => %{"last_read_id" => "34"}}
+ )
+
+ assert marker.timeline == "notifications"
+ assert marker.last_read_id == "34"
+ assert marker.lock_version == 0
+ end
+
+ test "updates exist marker" do
+ user = insert(:user)
+ marker = insert(:marker, user: user, last_read_id: "8909")
+
+ {:ok, %{"notifications" => %Marker{}}} =
+ Marker.upsert(
+ user,
+ %{"notifications" => %{"last_read_id" => "9909"}}
+ )
+
+ marker = refresh_record(marker)
+ assert marker.timeline == "notifications"
+ assert marker.last_read_id == "9909"
+ assert marker.lock_version == 0
+ end
+ end
+end
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 54c0f9877..96316f8dd 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -683,7 +683,7 @@ defmodule Pleroma.NotificationTest do
assert Notification.for_user(user) == []
end
- test "it returns notifications for muted user with notifications and with_muted parameter" do
+ test "it returns notifications from a muted user when with_muted is set" do
user = insert(:user)
muted = insert(:user)
{:ok, user} = User.mute(user, muted)
@@ -693,27 +693,27 @@ defmodule Pleroma.NotificationTest do
assert length(Notification.for_user(user, %{with_muted: true})) == 1
end
- test "it returns notifications for blocked user and with_muted parameter" do
+ test "it doesn't return notifications from a blocked user when with_muted is set" do
user = insert(:user)
blocked = insert(:user)
{:ok, user} = User.block(user, blocked)
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
- assert length(Notification.for_user(user, %{with_muted: true})) == 1
+ assert length(Notification.for_user(user, %{with_muted: true})) == 0
end
- test "it returns notificatitons for blocked domain and with_muted parameter" do
+ test "it doesn't return notifications from a domain-blocked user when with_muted is set" do
user = insert(:user)
blocked = insert(:user, ap_id: "http://some-domain.com")
{:ok, user} = User.block_domain(user, "some-domain.com")
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
- assert length(Notification.for_user(user, %{with_muted: true})) == 1
+ assert length(Notification.for_user(user, %{with_muted: true})) == 0
end
- test "it returns notifications for muted thread with_muted parameter" do
+ test "it returns notifications from muted threads when with_muted is set" do
user = insert(:user)
another_user = insert(:user)
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 0fdb1e952..41e2b8004 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -377,4 +377,13 @@ defmodule Pleroma.Factory do
)
}
end
+
+ def marker_factory do
+ %Pleroma.Marker{
+ user: build(:user),
+ timeline: "notifications",
+ lock_version: 0,
+ last_read_id: "1"
+ }
+ end
end
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 7d65209fb..eba22c40b 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -348,6 +348,14 @@ defmodule HttpRequestMock do
}}
end
+ def get("http://mastodon.example.org/users/relay", _, _, Accept: "application/activity+json") do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/relay@mastdon.example.org.json")
+ }}
+ end
+
def get("http://mastodon.example.org/users/gargron", _, _, Accept: "application/activity+json") do
{:error, :nxdomain}
end
diff --git a/test/user_test.exs b/test/user_test.exs
index ad050b7da..05bdb9a61 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -1232,6 +1232,20 @@ defmodule Pleroma.UserTest do
end
end
+ describe "invisible?/1" do
+ test "returns true for an invisible user" do
+ user = insert(:user, local: true, info: %{invisible: true})
+
+ assert User.invisible?(user)
+ end
+
+ test "returns false for a non-invisible user" do
+ user = insert(:user, local: true)
+
+ refute User.invisible?(user)
+ end
+ end
+
describe "visible_for?/2" do
test "returns true when the account is itself" do
user = insert(:user, local: true)
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 28a9b773c..8ae946969 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -179,6 +179,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
end
+ test "it returns a user that is invisible" do
+ user_id = "http://mastodon.example.org/users/relay"
+ {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
+ assert User.invisible?(user)
+ end
+
test "it fetches the appropriate tag-restricted posts" do
user = insert(:user)
diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs
index 4a0a03944..ac2007b2c 100644
--- a/test/web/activity_pub/relay_test.exs
+++ b/test/web/activity_pub/relay_test.exs
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
@@ -19,6 +20,11 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay"
end
+ test "relay actor is invisible" do
+ user = Relay.get_actor()
+ assert User.invisible?(user)
+ end
+
describe "follow/1" do
test "returns errors when user not found" do
assert capture_log(fn ->
diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs
index 3155749aa..a31b4c92e 100644
--- a/test/web/activity_pub/views/user_view_test.exs
+++ b/test/web/activity_pub/views/user_view_test.exs
@@ -76,6 +76,12 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
assert result["image"]["url"] == "https://somebanner"
end
+ test "renders an invisible user with the invisible property set to true" do
+ user = insert(:user, %{info: %{invisible: true}})
+
+ assert %{"invisible" => true} = UserView.render("service.json", %{user: user})
+ end
+
describe "endpoints" do
test "local users have a usable endpoints structure" do
user = insert(:user)
diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/web/mastodon_api/controllers/marker_controller_test.exs
new file mode 100644
index 000000000..1fcad873d
--- /dev/null
+++ b/test/web/mastodon_api/controllers/marker_controller_test.exs
@@ -0,0 +1,124 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import Pleroma.Factory
+
+ describe "GET /api/v1/markers" do
+ test "gets markers with correct scopes", %{conn: conn} do
+ user = insert(:user)
+ token = insert(:oauth_token, user: user, scopes: ["read:statuses"])
+
+ {:ok, %{"notifications" => marker}} =
+ Pleroma.Marker.upsert(
+ user,
+ %{"notifications" => %{"last_read_id" => "69420"}}
+ )
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, token)
+ |> get("/api/v1/markers", %{timeline: ["notifications"]})
+ |> json_response(200)
+
+ assert response == %{
+ "notifications" => %{
+ "last_read_id" => "69420",
+ "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
+ "version" => 0
+ }
+ }
+ end
+
+ test "gets markers with missed scopes", %{conn: conn} do
+ user = insert(:user)
+ token = insert(:oauth_token, user: user, scopes: [])
+
+ Pleroma.Marker.upsert(user, %{"notifications" => %{"last_read_id" => "69420"}})
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, token)
+ |> get("/api/v1/markers", %{timeline: ["notifications"]})
+ |> json_response(403)
+
+ assert response == %{"error" => "Insufficient permissions: read:statuses."}
+ end
+ end
+
+ describe "POST /api/v1/markers" do
+ test "creates a marker with correct scopes", %{conn: conn} do
+ user = insert(:user)
+ token = insert(:oauth_token, user: user, scopes: ["write:statuses"])
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, token)
+ |> post("/api/v1/markers", %{
+ home: %{last_read_id: "777"},
+ notifications: %{"last_read_id" => "69420"}
+ })
+ |> json_response(200)
+
+ assert %{
+ "notifications" => %{
+ "last_read_id" => "69420",
+ "updated_at" => _,
+ "version" => 0
+ }
+ } = response
+ end
+
+ test "updates exist marker", %{conn: conn} do
+ user = insert(:user)
+ token = insert(:oauth_token, user: user, scopes: ["write:statuses"])
+
+ {:ok, %{"notifications" => marker}} =
+ Pleroma.Marker.upsert(
+ user,
+ %{"notifications" => %{"last_read_id" => "69477"}}
+ )
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, token)
+ |> post("/api/v1/markers", %{
+ home: %{last_read_id: "777"},
+ notifications: %{"last_read_id" => "69888"}
+ })
+ |> json_response(200)
+
+ assert response == %{
+ "notifications" => %{
+ "last_read_id" => "69888",
+ "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at),
+ "version" => 0
+ }
+ }
+ end
+
+ test "creates a marker with missed scopes", %{conn: conn} do
+ user = insert(:user)
+ token = insert(:oauth_token, user: user, scopes: [])
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, token)
+ |> post("/api/v1/markers", %{
+ home: %{last_read_id: "777"},
+ notifications: %{"last_read_id" => "69420"}
+ })
+ |> json_response(403)
+
+ assert response == %{"error" => "Insufficient permissions: write:statuses."}
+ end
+ end
+end
diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs
index 2de2725e0..4da610b28 100644
--- a/test/web/mastodon_api/controllers/status_controller_test.exs
+++ b/test/web/mastodon_api/controllers/status_controller_test.exs
@@ -12,12 +12,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.ScheduledActivity
+ alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
+ clear_config([:instance, :federating])
+ clear_config([:instance, :allow_relay])
+
describe "posting statuses" do
setup do
user = insert(:user)
@@ -29,6 +33,34 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
[conn: conn]
end
+ test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
+ Pleroma.Config.put([:instance, :federating], true)
+ Pleroma.Config.get([:instance, :allow_relay], true)
+ user = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("api/v1/statuses", %{
+ "content_type" => "text/plain",
+ "source" => "Pleroma FE",
+ "status" => "Hello world",
+ "visibility" => "public"
+ })
+ |> json_response(200)
+
+ assert response["reblogs_count"] == 0
+ ObanHelpers.perform_all()
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> get("api/v1/statuses/#{response["id"]}", %{})
+ |> json_response(200)
+
+ assert response["reblogs_count"] == 0
+ end
+
test "posting a status", %{conn: conn} do
idempotency_key = "Pikachu rocks!"
diff --git a/test/web/mastodon_api/views/marker_view_test.exs b/test/web/mastodon_api/views/marker_view_test.exs
new file mode 100644
index 000000000..8a5c89d56
--- /dev/null
+++ b/test/web/mastodon_api/views/marker_view_test.exs
@@ -0,0 +1,27 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.MastodonAPI.MarkerView
+ import Pleroma.Factory
+
+ test "returns markers" do
+ marker1 = insert(:marker, timeline: "notifications", last_read_id: "17")
+ marker2 = insert(:marker, timeline: "home", last_read_id: "42")
+
+ assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{
+ "home" => %{
+ last_read_id: "42",
+ updated_at: NaiveDateTime.to_iso8601(marker2.updated_at),
+ version: 0
+ },
+ "notifications" => %{
+ last_read_id: "17",
+ updated_at: NaiveDateTime.to_iso8601(marker1.updated_at),
+ version: 0
+ }
+ }
+ end
+end
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
index 58534396e..37b7b62f5 100644
--- a/test/web/ostatus/ostatus_controller_test.exs
+++ b/test/web/ostatus/ostatus_controller_test.exs
@@ -5,7 +5,6 @@
defmodule Pleroma.Web.OStatus.OStatusControllerTest do
use Pleroma.Web.ConnCase
- import ExUnit.CaptureLog
import Pleroma.Factory
alias Pleroma.Object
diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs
index 8a6528cbb..9cccc8c8a 100644
--- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs
+++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs
@@ -95,6 +95,33 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
assert other_user in participation.recipients
end
+ test "POST /api/v1/pleroma/conversations/read", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"})
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"})
+
+ [participation2, participation1] = Participation.for_user(other_user)
+ assert Participation.get(participation2.id).read == false
+ assert Participation.get(participation1.id).read == false
+ assert User.get_cached_by_id(other_user.id).info.unread_conversation_count == 2
+
+ [%{"unread" => false}, %{"unread" => false}] =
+ conn
+ |> assign(:user, other_user)
+ |> post("/api/v1/pleroma/conversations/read", %{})
+ |> json_response(200)
+
+ [participation2, participation1] = Participation.for_user(other_user)
+ assert Participation.get(participation2.id).read == true
+ assert Participation.get(participation1.id).read == true
+ assert User.get_cached_by_id(other_user.id).info.unread_conversation_count == 0
+ end
+
describe "POST /api/v1/pleroma/notifications/read" do
test "it marks a single notification as read", %{conn: conn} do
user1 = insert(:user)