Merge remote-tracking branch 'origin/main' into feature/calendar

This commit is contained in:
summersamara 2023-12-24 07:51:52 +01:00
commit dec26525c0
81 changed files with 1645 additions and 785 deletions

View file

@ -50,8 +50,8 @@ LABEL org.opencontainers.image.title="mobilizon" \
org.opencontainers.image.revision=$VCS_REF \
org.opencontainers.image.created=$BUILD_DATE
RUN apk add --no-cache curl openssl ca-certificates ncurses-libs file postgresql-client libgcc libstdc++ imagemagick python3 py3-pip py3-pillow py3-cffi py3-brotli gcc g++ musl-dev python3-dev pango libxslt-dev ttf-cantarell openssl1.1-compat
RUN pip --no-cache-dir install weasyprint pyexcel-ods3
RUN apk add --no-cache curl openssl ca-certificates ncurses-libs file postgresql-client libgcc libstdc++ imagemagick python3 py3-pip py3-pillow py3-cffi py3-brotli gcc g++ musl-dev python3-dev pango libxslt-dev ttf-cantarell
RUN pip --no-cache-dir install --break-system-packages weasyprint pyexcel-ods3
# Create every data directory
RUN mkdir -p /var/lib/mobilizon/uploads && chown nobody:nobody /var/lib/mobilizon/uploads

View file

@ -135,7 +135,7 @@ defmodule Mobilizon.Federation.ActivityPub do
%Page{total: total_events, elements: events} =
if actor_id == relay_actor_id do
Events.list_public_local_events(page, limit)
Events.list_public_local_events(page, limit, :publish_at, :desc)
else
Events.list_public_events_for_actor(actor, page, limit)
end

View file

@ -13,10 +13,9 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Transmogrifier}
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.WebFinger
alias Mobilizon.Federation.{NodeInfo, WebFinger}
alias Mobilizon.GraphQL.API.Follows
alias Mobilizon.Service.Workers.Background
import Mobilizon.Federation.ActivityPub.Utils, only: [create_full_domain_string: 1]
require Logger
@ -185,17 +184,17 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
defp fetch_actor("http://" <> address), do: fetch_actor(address)
defp fetch_actor(address) do
%URI{host: host} = uri = URI.parse("http://" <> address)
%URI{host: host} = URI.parse("http://" <> address)
cond do
String.contains?(address, "@") ->
check_actor(address)
!is_nil(host) ->
uri
|> create_full_domain_string()
|> then(&Kernel.<>("relay@", &1))
|> check_actor()
case NodeInfo.application_actor(host) do
nil -> check_actor("relay@#{host}")
actor_url when is_binary(actor_url) -> {:ok, actor_url}
end
true ->
{:error, :bad_url}

View file

@ -24,9 +24,13 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Address do
}
res =
if is_nil(object["address"]) or not is_map(object["address"]) do
res
else
cond do
is_binary(object["address"]) ->
Map.merge(res, %{
"street" => object["address"]
})
is_map(object["address"]) ->
Map.merge(res, %{
"country" => object["address"]["addressCountry"],
"postal_code" => object["address"]["postalCode"],
@ -34,6 +38,9 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Address do
"street" => object["address"]["streetAddress"],
"locality" => object["address"]["addressLocality"]
})
is_nil(object["address"]) ->
res
end
latitude = Map.get(object, "latitude")

View file

@ -198,6 +198,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
defp calculate_timezone(_object, nil), do: nil
defp calculate_timezone(_object, %Address{geom: nil}), do: nil
defp calculate_timezone(_object, %Address{geom: geom}) do
TimezoneDetector.detect(
nil,

View file

@ -278,8 +278,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
end
@spec get_address(map | binary | nil) :: Address.t() | nil
def get_address(address_url) when is_binary(address_url) do
get_address(%{"id" => address_url})
def get_address(text_address) when is_binary(text_address) do
get_address(%{"type" => "Place", "name" => text_address})
end
def get_address(%{"id" => url} = map) when is_map(map) and is_binary(url) do

105
lib/federation/node_info.ex Normal file
View file

@ -0,0 +1,105 @@
defmodule Mobilizon.Federation.NodeInfo do
@moduledoc """
Performs NodeInfo requests
"""
alias Mobilizon.Service.HTTP.WebfingerClient
require Logger
@application_uri "https://www.w3.org/ns/activitystreams#Application"
@nodeinfo_rel_2_0 "http://nodeinfo.diaspora.software/ns/schema/2.0"
@nodeinfo_rel_2_1 "http://nodeinfo.diaspora.software/ns/schema/2.1"
@env Application.compile_env(:mobilizon, :env)
@spec application_actor(String.t()) :: String.t() | nil
def application_actor(host) do
Logger.debug("Fetching application actor from NodeInfo data for domain #{host}")
case fetch_nodeinfo_endpoint(host) do
{:ok, body} ->
extract_application_actor(body)
{:error, :node_info_meta_http_error} ->
nil
end
end
@spec nodeinfo(String.t()) :: {:ok, map()} | {:error, atom()}
def nodeinfo(host) do
Logger.debug("Fetching NodeInfo details for domain #{host}")
with {:ok, endpoint} when is_binary(endpoint) <- fetch_nodeinfo_details(host),
:ok <- Logger.debug("Going to get NodeInfo information from URL #{endpoint}"),
{:ok, %{body: body, status: code}} when code in 200..299 <- WebfingerClient.get(endpoint) do
Logger.debug("Found nodeinfo information for domain #{host}")
{:ok, body}
else
{:error, err} ->
{:error, err}
err ->
Logger.debug("Failed to fetch NodeInfo data from endpoint #{inspect(err)}")
{:error, :node_info_endpoint_http_error}
end
end
defp extract_application_actor(body) do
body
|> Map.get("links", [])
|> Enum.find(%{"rel" => @application_uri, "href" => nil}, fn %{"rel" => rel, "href" => href} ->
rel == @application_uri and is_binary(href)
end)
|> Map.get("href")
end
@spec fetch_nodeinfo_endpoint(String.t()) :: {:ok, map()} | {:error, atom()}
defp fetch_nodeinfo_endpoint(host) do
prefix = if @env !== :dev, do: "https", else: "http"
case WebfingerClient.get("#{prefix}://#{host}/.well-known/nodeinfo") do
{:ok, %{body: body, status: code}} when code in 200..299 ->
{:ok, body}
err ->
Logger.debug("Failed to fetch NodeInfo data #{inspect(err)}")
{:error, :node_info_meta_http_error}
end
end
@spec fetch_nodeinfo_details(String.t()) :: {:ok, String.t()} | {:error, atom()}
defp fetch_nodeinfo_details(host) do
with {:ok, body} <- fetch_nodeinfo_endpoint(host) do
extract_nodeinfo_endpoint(body)
end
end
@spec extract_nodeinfo_endpoint(map()) ::
{:ok, String.t()}
| {:error, :no_node_info_endpoint_found | :no_valid_node_info_endpoint_found}
defp extract_nodeinfo_endpoint(body) do
links = Map.get(body, "links", [])
relation =
find_nodeinfo_relation(links, @nodeinfo_rel_2_1) ||
find_nodeinfo_relation(links, @nodeinfo_rel_2_0)
if is_nil(relation) do
{:error, :no_node_info_endpoint_found}
else
endpoint = Map.get(relation, "href")
if is_nil(endpoint) do
{:error, :no_valid_node_info_endpoint_found}
else
{:ok, endpoint}
end
end
end
defp find_nodeinfo_relation(links, relation) do
Enum.find(links, fn %{"rel" => rel, "href" => href} ->
rel == relation and is_binary(href)
end)
end
end

View file

@ -490,19 +490,35 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
context: %{current_user: %User{role: role}}
})
when is_admin(role) do
remote_relay = Actors.get_relay(domain)
remote_relay = Instances.get_instance_actor(domain)
local_relay = Relay.get_actor()
result = %{
has_relay: !is_nil(remote_relay),
relay_address:
if(is_nil(remote_relay),
do: nil,
else: "#{remote_relay.preferred_username}@#{remote_relay.domain}"
),
follower_status: follow_status(remote_relay, local_relay),
followed_status: follow_status(local_relay, remote_relay)
result =
if is_nil(remote_relay) do
%{
has_relay: false,
relay_address: nil,
follower_status: nil,
followed_status: nil,
software: nil,
software_version: nil
}
else
%{
has_relay: !is_nil(remote_relay.actor),
relay_address:
if(is_nil(remote_relay.actor),
do: nil,
else: Actor.preferred_username_and_domain(remote_relay.actor)
),
follower_status: follow_status(remote_relay.actor, local_relay),
followed_status: follow_status(local_relay, remote_relay.actor),
instance_name: remote_relay.instance_name,
instance_description: remote_relay.instance_description,
software: remote_relay.software,
software_version: remote_relay.software_version
}
end
case Instances.instance(domain) do
nil -> {:error, :not_found}

View file

@ -227,6 +227,16 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
field(:relay_address, :string,
description: "If this instance has a relay, it's federated username"
)
field(:instance_name, :string, description: "This instance's name")
field(:instance_description, :string, description: "This instance's description")
field(:software, :string, description: "The software this instance declares running")
field(:software_version, :string,
description: "The software version this instance declares running"
)
end
@desc """

View file

@ -387,13 +387,15 @@ defmodule Mobilizon.Events do
|> Repo.stream()
end
@spec list_public_local_events(integer | nil, integer | nil) :: Page.t(Event.t())
def list_public_local_events(page \\ nil, limit \\ nil) do
@spec list_public_local_events(integer | nil, integer | nil, atom | nil, atom | nil) ::
Page.t(Event.t())
def list_public_local_events(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do
Event
|> filter_public_visibility()
|> filter_draft()
|> filter_local()
|> preload_for_event()
|> event_order_by(sort, direction)
|> Page.build_page(page, limit)
end

View file

@ -0,0 +1,39 @@
defmodule Mobilizon.Instances.InstanceActor do
@moduledoc """
An instance actor
"""
use Ecto.Schema
import Ecto.Changeset
alias Mobilizon.Actors.Actor
@type t :: %__MODULE__{
domain: String.t(),
actor: Actor.t(),
instance_name: String.t(),
instance_description: String.t(),
software: String.t(),
software_version: String.t()
}
schema "instance_actors" do
field(:domain, :string)
field(:instance_name, :string)
field(:instance_description, :string)
field(:software, :string)
field(:software_version, :string)
belongs_to(:actor, Actor)
timestamps()
end
@required_attrs [:domain]
@optional_attrs [:actor_id, :instance_name, :instance_description, :software, :software_version]
@attrs @required_attrs ++ @optional_attrs
def changeset(%__MODULE__{} = instance_actor, attrs) do
instance_actor
|> cast(attrs, @attrs)
|> validate_required(@required_attrs)
|> unique_constraint(:domain)
end
end

View file

@ -4,7 +4,7 @@ defmodule Mobilizon.Instances do
"""
alias Ecto.Adapters.SQL
alias Mobilizon.Actors.{Actor, Follower}
alias Mobilizon.Instances.Instance
alias Mobilizon.Instances.{Instance, InstanceActor}
alias Mobilizon.Storage.{Page, Repo}
import Ecto.Query
@ -12,13 +12,13 @@ defmodule Mobilizon.Instances do
@spec instances(Keyword.t()) :: Page.t(Instance.t())
def instances(options) do
page = Keyword.get(options, :page)
limit = Keyword.get(options, :limit)
order_by = Keyword.get(options, :order_by)
direction = Keyword.get(options, :direction)
page = Keyword.get(options, :page, 1)
limit = Keyword.get(options, :limit, 10)
order_by = Keyword.get(options, :order_by, :event_count)
direction = Keyword.get(options, :direction, :desc)
filter_domain = Keyword.get(options, :filter_domain)
# suspend_status = Keyword.get(options, :filter_suspend_status)
follow_status = Keyword.get(options, :filter_follow_status)
# suspend_status = Keyword.get(options, :filter_suspend_status, :all)
follow_status = Keyword.get(options, :filter_follow_status, :all)
order_by_options = Keyword.new([{direction, order_by}])
@ -42,7 +42,9 @@ defmodule Mobilizon.Instances do
query =
Instance
|> join(:left, [i], s in subquery(subquery), on: i.domain == s.domain)
|> select([i, s], {i, s})
|> join(:left, [i], ia in InstanceActor, on: i.domain == ia.domain)
|> join(:left, [_i, _s, ia], a in Actor, on: ia.actor_id == a.id)
|> select([i, s, ia, a], {i, s, ia, a})
|> order_by(^order_by_options)
query =
@ -100,16 +102,72 @@ defmodule Mobilizon.Instances do
following: following,
following_approved: following_approved,
has_relay: has_relay
}}
}, instance_meta, instance_actor}
) do
instance
|> Map.put(:follower_status, follow_status(following, following_approved))
|> Map.put(:followed_status, follow_status(follower, follower_approved))
|> Map.put(:has_relay, has_relay)
|> Map.put(:instance_actor, instance_actor)
|> add_metadata_details(instance_meta)
end
@spec add_metadata_details(map(), InstanceActor.t() | nil) :: map()
defp add_metadata_details(instance, nil), do: instance
defp add_metadata_details(instance, instance_meta) do
instance
|> Map.put(:instance_name, instance_meta.instance_name)
|> Map.put(:instance_description, instance_meta.instance_description)
|> Map.put(:software, instance_meta.software)
|> Map.put(:software_version, instance_meta.software_version)
end
defp follow_status(true, true), do: :approved
defp follow_status(true, false), do: :pending
defp follow_status(false, _), do: :none
defp follow_status(nil, _), do: :none
@spec get_instance_actor(String.t()) :: InstanceActor.t() | nil
def get_instance_actor(domain) do
InstanceActor
|> Repo.get_by(domain: domain)
|> Repo.preload(:actor)
end
@doc """
Creates an instance actor.
"""
@spec create_instance_actor(map) :: {:ok, InstanceActor.t()} | {:error, Ecto.Changeset.t()}
def create_instance_actor(attrs \\ %{}) do
with {:ok, %InstanceActor{} = instance_actor} <-
%InstanceActor{}
|> InstanceActor.changeset(attrs)
|> Repo.insert(on_conflict: :replace_all, conflict_target: :domain) do
{:ok, Repo.preload(instance_actor, :actor)}
end
end
@doc """
Updates an instance actor.
"""
@spec update_instance_actor(InstanceActor.t(), map) ::
{:ok, InstanceActor.t()} | {:error, Ecto.Changeset.t()}
def update_instance_actor(%InstanceActor{} = instance_actor, attrs) do
with {:ok, %InstanceActor{} = instance_actor} <-
instance_actor
|> Repo.preload(:actor)
|> InstanceActor.changeset(attrs)
|> Repo.update() do
{:ok, Repo.preload(instance_actor, :actor)}
end
end
@doc """
Deletes a post
"""
@spec delete_instance_actor(InstanceActor.t()) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
def delete_instance_actor(%InstanceActor{} = instance_actor) do
Repo.delete(instance_actor)
end
end

View file

@ -22,11 +22,13 @@ defmodule Mobilizon.Posts do
:private
])
@spec list_public_local_posts(integer | nil, integer | nil) :: Page.t(Post.t())
def list_public_local_posts(page \\ nil, limit \\ nil) do
@spec list_public_local_posts(integer | nil, integer | nil, atom | nil, atom | nil) ::
Page.t(Post.t())
def list_public_local_posts(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do
Post
|> filter_public()
|> filter_local()
|> order_posts(sort, direction)
|> preload_post_associations()
|> Page.build_page(page, limit)
end
@ -171,4 +173,13 @@ defmodule Mobilizon.Posts do
defp preload_post_associations(query, associations \\ @post_preloads) do
preload(query, ^associations)
end
@spec order_posts(Ecto.Queryable.t(), atom | nil, atom | nil) :: Ecto.Queryable.t()
def order_posts(query, nil, _direction), do: query
def order_posts(query, _sort, nil), do: query
def order_posts(query, sort, direction) do
order_by_param = Keyword.new([{direction, sort}])
order_by(query, ^order_by_param)
end
end

View file

@ -20,7 +20,7 @@ defmodule Mobilizon.Storage.Page do
@doc """
Returns a Page struct for a query.
`field` is use to define the field that will be used for the count aggregate, which should be the same as the field used for order_by
`field` is used to define the field that will be used for the count aggregate, which should be the same as the field used for order_by
See https://stackoverflow.com/q/12693089/10204399
"""
@spec build_page(Ecto.Queryable.t(), integer | nil, integer | nil, atom()) :: t(any)

View file

@ -96,8 +96,8 @@ defmodule Mobilizon.Service.Export.Common do
@spec fetch_instance_public_content(integer()) :: {:ok, list(Event.t()), list(Post.t())}
def fetch_instance_public_content(limit) do
%Page{elements: events} = Events.list_public_local_events(1, limit)
%Page{elements: posts} = Posts.list_public_local_posts(1, limit)
%Page{elements: events} = Events.list_public_local_events(1, limit, :begins_on, :desc)
%Page{elements: posts} = Posts.list_public_local_posts(1, limit, :publish_at, :desc)
{:ok, events, posts}
end

View file

@ -21,7 +21,7 @@ defmodule Mobilizon.Service.Export.Feed do
@behaviour Cachable
@item_limit 500
@item_limit 5000
@spec version :: String.t()
defp version, do: Config.instance_version()

View file

@ -3,14 +3,16 @@ defmodule Mobilizon.Service.Workers.RefreshInstances do
Worker to refresh the instances materialized view and the relay actors
"""
use Oban.Worker, unique: [period: :infinity, keys: [:event_uuid, :action]]
use Oban.Worker
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityPub.Relay
alias Mobilizon.Federation.NodeInfo
alias Mobilizon.Instances
alias Mobilizon.Instances.Instance
alias Mobilizon.Instances.{Instance, InstanceActor}
alias Oban.Job
require Logger
@impl Oban.Worker
@spec perform(Oban.Job.t()) :: :ok
@ -30,6 +32,8 @@ defmodule Mobilizon.Service.Workers.RefreshInstances do
{:error, :not_remote_instance}
end
@spec refresh_instance_actor(Instance.t()) ::
{:ok, InstanceActor.t()} | {:error, Ecto.Changeset.t()} | {:error, atom}
def refresh_instance_actor(%Instance{domain: domain}) do
%Actor{url: url} = Relay.get_actor()
%URI{host: host} = URI.new!(url)
@ -37,7 +41,67 @@ defmodule Mobilizon.Service.Workers.RefreshInstances do
if host == domain do
{:error, :not_remote_instance}
else
ActivityPubActor.find_or_make_actor_from_nickname("relay@#{domain}")
actor_id =
case fetch_actor(domain) do
{:ok, %Actor{id: actor_id}} -> actor_id
_ -> nil
end
with instance_metadata <- fetch_instance_metadata(domain),
:ok <- Logger.debug("Ready to save instance actor details"),
{:ok, %InstanceActor{}} <-
Instances.create_instance_actor(%{
domain: domain,
actor_id: actor_id,
instance_name: get_in(instance_metadata, ["metadata", "nodeName"]),
instance_description: get_in(instance_metadata, ["metadata", "nodeDescription"]),
software: get_in(instance_metadata, ["software", "name"]),
software_version: get_in(instance_metadata, ["software", "version"])
}) do
Logger.info("Saved instance actor details for domain #{host}")
else
err ->
Logger.error(inspect(err))
end
end
end
defp mobilizon(domain), do: "relay@#{domain}"
defp peertube(domain), do: "peertube@#{domain}"
defp mastodon(domain), do: "#{domain}@#{domain}"
defp fetch_actor(domain) do
case NodeInfo.application_actor(domain) do
nil -> guess_application_actor(domain)
url -> ActivityPubActor.get_or_fetch_actor_by_url(url)
end
end
defp fetch_instance_metadata(domain) do
case NodeInfo.nodeinfo(domain) do
{:error, _} ->
%{}
{:ok, metadata} ->
metadata
end
end
defp guess_application_actor(domain) do
Enum.find_value(
[
&mobilizon/1,
&peertube/1,
&mastodon/1
],
{:error, :no_application_actor_found},
fn username_pattern ->
case ActivityPubActor.find_or_make_actor_from_nickname(username_pattern.(domain)) do
{:ok, %Actor{type: :Application} = actor} -> {:ok, actor}
{:error, _err} -> false
{:ok, _actor} -> false
end
end
)
end
end

View file

@ -7,13 +7,17 @@ defmodule Mobilizon.Web.NodeInfoController do
use Mobilizon.Web, :controller
alias Mobilizon.Config
alias Mobilizon.Federation.ActivityPub.Relay
alias Mobilizon.Service.Statistics
@node_info_supported_versions ["2.0", "2.1"]
@node_info_schema_uri "http://nodeinfo.diaspora.software/ns/schema/"
@application_uri "https://www.w3.org/ns/activitystreams#Application"
@spec schemas(Plug.Conn.t(), any) :: Plug.Conn.t()
def schemas(conn, _params) do
relay = Relay.get_actor()
links =
@node_info_supported_versions
|> Enum.map(fn version ->
@ -22,6 +26,12 @@ defmodule Mobilizon.Web.NodeInfoController do
href: url(~p"/.well-known/nodeinfo/#{version}")
}
end)
|> Kernel.++([
%{
rel: @application_uri,
href: relay.url
}
])
json(conn, %{
links: links

View file

@ -143,7 +143,7 @@ defmodule Mobilizon.Mixfile do
{:phoenix_html, "~> 3.0"},
{:phoenix_live_view, "~> 0.20.0"},
{:phoenix_view, "~> 2.0"},
{:gettext, "~> 0.23.1"},
{:gettext, "~> 0.24"},
{:cowboy, "~> 2.6"},
{:guardian, "~> 2.0"},
{:guardian_db, "~> 3.0.0"},
@ -185,7 +185,7 @@ defmodule Mobilizon.Mixfile do
{:floki, "~> 0.31"},
{:ip_reserved, "~> 0.1.0"},
{:fast_sanitize, "~> 0.1"},
{:ueberauth, "~> 0.10", override: true},
{:ueberauth, "0.10.5", override: true},
{:ueberauth_twitter, "~> 0.4"},
{:ueberauth_discord, "~> 0.7"},
{:ueberauth_github, "~> 0.8.1"},

View file

@ -6,7 +6,7 @@
"atomex": {:hex, :atomex, "0.5.1", "706a8241fd6d1719b27a77b6d1192d2f85e6ecc78e6eadab29207d8cb9bb7ae5", [:mix], [{:xml_builder, "~> 2.1", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm", "6248891b5fcab8503982e090eedeeadb757a6311c2ef2e2998b874f7d319ab3f"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
"castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"},
"castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"},
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
"cldr_utils": {:hex, :cldr_utils, "2.24.2", "364fa30be55d328e704629568d431eb74cd2f085752b27f8025520b566352859", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "3362b838836a9f0fa309de09a7127e36e67310e797d556db92f71b548832c7cf"},
"codepagex": {:hex, :codepagex, "0.1.6", "49110d09a25ee336a983281a48ef883da4c6190481e0b063afe2db481af6117e", [:mix], [], "hexpm", "1521461097dde281edf084062f525a4edc6a5e49f4fd1f5ec41c9c4955d5bd59"},
@ -25,27 +25,27 @@
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
"digital_token": {:hex, :digital_token, "0.6.0", "13e6de581f0b1f6c686f7c7d12ab11a84a7b22fa79adeb4b50eec1a2d278d258", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2455d626e7c61a128b02a4a8caddb092548c3eb613ac6f6a85e4cbb6caddc4d1"},
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
"earmark_parser": {:hex, :earmark_parser, "1.4.38", "b42252eddf63bda05554ba8be93a1262dc0920c721f1aaf989f5de0f73a2e367", [:mix], [], "hexpm", "2cd0907795aaef0c7e8442e376633c5b3bd6edc8dbbdc539b22f095501c1cdb6"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"},
"ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"},
"ecto": {:hex, :ecto, "3.11.1", "4b4972b717e7ca83d30121b12998f5fcdc62ba0ed4f20fd390f16f3270d85c3e", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ebd3d3772cd0dfcd8d772659e41ed527c28b2a8bde4b00fe03e0463da0f1983b"},
"ecto_autoslug_field": {:hex, :ecto_autoslug_field, "3.1.0", "ddf26e814baf3c32c6aebfed56a637f10a097db83f65d71e6f2d1e7faf2e9e51", [:mix], [{:ecto, ">= 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:slugify, "~> 1.3", [hex: :slugify, repo: "hexpm", optional: false]}], "hexpm", "b6ddd614805263e24b5c169532c934440d0289181cce873061fca3a8e92fd9ff"},
"ecto_dev_logger": {:hex, :ecto_dev_logger, "0.9.0", "cb631469ac1940e97655d6fce85905b792ac9250ab18b19c664978b79f8dad59", [:mix], [{:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2e8bc98b4ae4fcc7108896eef7da5a109afad829f4fb2eb46d677fdc9101c2d5"},
"ecto_dev_logger": {:hex, :ecto_dev_logger, "0.10.0", "5b3a3900b845e0d40127bed9bdf9d02bf20aa38198a60fe108cddff63ed0048f", [:mix], [{:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "a55e58bad5d5c9b8ef2a3c3347dbdf7efa880a5371cf1457e44b41f489a43927"},
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
"ecto_shortuuid": {:hex, :ecto_shortuuid, "0.2.0", "57cae7b6016cc56a04457b4fc8f63957398dfd9023ff3e900eaf6805a40f8043", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:shortuuid, "~> 2.1 or ~> 3.0", [hex: :shortuuid, repo: "hexpm", optional: false]}], "hexpm", "b92e3b71e86be92f5a7ef6f3de170e7864454e630f7b01dd930414baf38efb65"},
"ecto_sql": {:hex, :ecto_sql, "3.11.0", "c787b24b224942b69c9ff7ab9107f258ecdc68326be04815c6cce2941b6fad1c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "77aa3677169f55c2714dda7352d563002d180eb33c0dc29cd36d39c0a1a971f5"},
"ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"},
"elixir_feed_parser": {:hex, :elixir_feed_parser, "2.1.0", "bb96fb6422158dc7ad59de62ef211cc69d264acbbe63941a64a5dce97bbbc2e6", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2d3c62fe7b396ee3b73d7160bc8fadbd78bfe9597c98c7d79b3f1038d9cba28f"},
"elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"erlport": {:hex, :erlport, "0.11.0", "8bb46a520e6eb9146e655fbf9b824433d9d532194667069d9aa45696aae9684b", [:rebar3], [], "hexpm", "8eb136ccaf3948d329b8d1c3278ad2e17e2a7319801bc4cc2da6db278204eee4"},
"eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},
"ex_cldr": {:hex, :ex_cldr, "2.37.5", "9da6d97334035b961d2c2de167dc6af8cd3e09859301a5b8f49f90bd8b034593", [:mix], [{:cldr_utils, "~> 2.21", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "74ad5ddff791112ce4156382e171a5f5d3766af9d5c4675e0571f081fe136479"},
"ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.22.1", "3e5150f1fe7698e0fa118aeedcca1b5920d0a552bc40c81cf65ca9b0a4ea4cc3", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.16", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e7408cd9e8318b2ef93b76728e84484ddc3ea6d7c894fbc811c54122a7140169"},
"ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.23.0", "b18d62e23d74f7b4ce567e53dc91f900ea7ebb81a48565b4415554b8bdd3676c", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.16", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "06d2407e699032d5cdc515593b7ce7869f10ce28e98a4ed68d9b21e5001036d4"},
"ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.15.1", "e92ba17c41e7405b7784e0e65f406b5f17cfe313e0e70de9befd653e12854822", [:mix], [{:ex_cldr, "~> 2.34", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "31df8bd37688340f8819bdd770eb17d659652078d34db632b85d4a32864d6a25"},
"ex_cldr_dates_times": {:hex, :ex_cldr_dates_times, "2.16.0", "d9848a5de83b6f1bcba151cc43d63b5c6311813cd605b1df1afd896dfdd21001", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_calendars, "~> 1.22", [hex: :ex_cldr_calendars, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:tz, "~> 0.26", [hex: :tz, repo: "hexpm", optional: true]}], "hexpm", "0f2f250d479cadda4e0ef3a5e3d936ae7ba1a3f1199db6791e284e86203495b1"},
"ex_cldr_languages": {:hex, :ex_cldr_languages, "0.3.3", "9787002803552b15a7ade19496c9e46fc921baca992ea80d0394e11fe3acea45", [:mix], [{:ex_cldr, "~> 2.25", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "22fb1fef72b7b4b4872d243b34e7b83734247a78ad87377986bf719089cc447a"},
"ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.32.3", "b631ff94c982ec518e46bf4736000a30a33d6b58facc085d5f240305f512ad4a", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:digital_token, "~> 0.3 or ~> 1.0", [hex: :digital_token, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, ">= 2.14.2", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "7b626ff1e59a0ec9c3c5db5ce9ca91a6995e2ab56426b71f3cbf67181ea225f5"},
"ex_cldr_plugs": {:hex, :ex_cldr_plugs, "1.3.1", "ae58748df815ad21b8618830374a28b2ab593230e5df70ed9f647e953a884bec", [:mix], [{:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "4f7b4a5fe061734cef7b62ff29118ed6ac72698cdd7bcfc97495db73611fe0fe"},
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
"ex_doc": {:hex, :ex_doc, "0.31.0", "06eb1dfd787445d9cab9a45088405593dd3bb7fe99e097eaa71f37ba80c7a676", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5350cafa6b7f77bdd107aa2199fe277acf29d739aba5aee7e865fc680c62a110"},
"ex_ical": {:hex, :ex_ical, "0.2.0", "4b928b554614704016cc0c9ee226eb854da9327a1cc460457621ceacb1ac29a6", [:mix], [{:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "db76473b2ae0259e6633c6c479a5a4d8603f09497f55c88f9ef4d53d2b75befb"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
"ex_optimizer": {:hex, :ex_optimizer, "0.1.1", "62da37e206fc2233ff7a4e54e40eae365c40f96c81992fcd15b782eb25169b80", [:mix], [{:file_info, "~> 0.0.4", [hex: :file_info, repo: "hexpm", optional: false]}], "hexpm", "e6f5c059bcd58b66be2f6f257fdc4f69b74b0fa5c9ddd669486af012e4b52286"},
@ -53,7 +53,7 @@
"excoveralls": {:hex, :excoveralls, "0.18.0", "b92497e69465dc51bc37a6422226ee690ab437e4c06877e836f1c18daeb35da9", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1109bb911f3cb583401760be49c02cbbd16aed66ea9509fc5479335d284da60b"},
"exgravatar": {:hex, :exgravatar, "2.0.3", "2349709832ee535f826f48db98cddd294ae62b01acb44d539a16419bd8ebc3e5", [:mix], [], "hexpm", "aca18ff9bd8991d3be3e5446d3bdefc051be084c1ffc9ab2d43b3e65339300e1"},
"exkismet": {:git, "https://github.com/tcitworld/exkismet.git", "8b5485fde00fafbde20f315bec387a77f7358334", []},
"expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"},
"expo": {:hex, :expo, "0.5.1", "249e826a897cac48f591deba863b26c16682b43711dd15ee86b92f25eafd96d9", [:mix], [], "hexpm", "68a4233b0658a3d12ee00d27d37d856b1ba48607e7ce20fd376958d0ba6ce92b"},
"export": {:hex, :export, "0.1.1", "6dfd268b0692428f89b9285859a2dc02b6dcd2e8fdfbca34ac6e6a331351df91", [:mix], [{:erlport, "~> 0.9", [hex: :erlport, repo: "hexpm", optional: false]}], "hexpm", "3da7444ff4053f1824352f4bdb13fbd2c28c93c2011786fb686b649fdca1021f"},
"fast_html": {:hex, :fast_html, "2.2.0", "6c5ef1be087a4ed613b0379c13f815c4d11742b36b67bb52cee7859847c84520", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "064c4f23b4a6168f9187dac8984b056f2c531bb0787f559fd6a8b34b38aefbae"},
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
@ -66,7 +66,7 @@
"geohax": {:hex, :geohax, "1.0.0", "89717442c5474a57d242b2361590bb33a7fbe6d8ec02a31d9865c3f33756d348", [:mix], [], "hexpm", "893ef2f905213acb67c615d2c955d926b1be3676bfc2bd5ed7271b641dfa2224"},
"geolix": {:hex, :geolix, "2.0.0", "7e65764bedfc98d08a3ddb24c417657c9d438eff163280b45fbb7de289626acd", [:mix], [], "hexpm", "8742bf588ed0bb7def2c443204d09d355990846c6efdff96ded66aac24c301df"},
"geolix_adapter_mmdb2": {:hex, :geolix_adapter_mmdb2, "0.6.0", "6ab9dbf6ea395817aa1fd2597be25d0dda1853c7f664e62716e47728d18bc4f9", [:mix], [{:geolix, "~> 2.0", [hex: :geolix, repo: "hexpm", optional: false]}, {:mmdb2_decoder, "~> 3.0", [hex: :mmdb2_decoder, repo: "hexpm", optional: false]}], "hexpm", "06ff962feae8a310cffdf86b74bfcda6e2d0dccb439bb1f62df2b657b1c0269b"},
"gettext": {:hex, :gettext, "0.23.1", "821e619a240e6000db2fc16a574ef68b3bd7fe0167ccc264a81563cc93e67a31", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "19d744a36b809d810d610b57c27b934425859d158ebd56561bc41f7eeb8795db"},
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
"guardian": {:hex, :guardian, "2.3.2", "78003504b987f2b189d76ccf9496ceaa6a454bb2763627702233f31eb7212881", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "b189ff38cd46a22a8a824866a6867ca8722942347f13c33f7d23126af8821b52"},
"guardian_db": {:hex, :guardian_db, "3.0.0", "c42902e3f1af1ba1e2d0c10913b926a1421f3a7e38eb4fc382b715c17489abdb", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "9c2ec4278efa34f9f1cc6ba795e552d41fdc7ffba5319d67eeb533b89392d183"},
"guardian_phoenix": {:hex, :guardian_phoenix, "2.0.1", "89a817265af09a6ddf7cb1e77f17ffca90cea2db10ff888375ef34502b2731b1", [:mix], [{:guardian, "~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "21f439246715192b231f228680465d1ed5fbdf01555a4a3b17165532f5f9a08c"},
@ -87,7 +87,7 @@
"linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.3", "d684f4bac8690e70b06eb52dad65d26de2eefa44cd19d64a8095e1417df7c8fd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
@ -103,23 +103,23 @@
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"},
"oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"},
"oban": {:hex, :oban, "2.16.3", "33ebe7da637cce4da5438c1636bc25448a8628994a0c064ac6078bbe6dc97bd6", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4d8a7fb62f63cf2f2080c78954425f5fd8916ef57196b7f79b5bc657abb2ac5f"},
"oban": {:hex, :oban, "2.17.1", "42d6221a1c17b63d81c19e3bad9ea82b59e39c47c1f9b7670ee33628569a449b", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c02686ada7979b00e259c0efbafeae2749f8209747b3460001fe695c5bdbeee6"},
"paasaa": {:hex, :paasaa, "0.6.0", "07c8ed81010caa25db351d474f0c053072c809821c60f9646f7b1547bec52f6d", [:mix], [], "hexpm", "732ddfc21bac0831edb26aec468af3ec2b8997d74f6209810b1cc53199c29f2e"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.3", "86e9878f833829c3f66da03d75254c155d91d72a201eb56ae83482328dc7ca93", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d36c401206f3011fefd63d04e8ef626ec8791975d9d107f9a0817d426f61ac07"},
"phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.1", "92a37acf07afca67ac98bd326532ba8f44ad7d4bdf3e4361b03f7f02594e5ae9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be494fd1215052729298b0e97d5c2ce8e719c00854b82cd8cf15c1cd7fcf6294"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.2", "025391424257d6c1ed2fab5c9fb11b1e5b21a8b47a416659b9a36492217dd5cb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "381c7f8a5702be838a6bf440a59ea010b458018e1a85cc432e8628641c00f07a"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"},
"phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"},
"plug": {:hex, :plug, "1.15.2", "94cf1fa375526f30ff8770837cb804798e0045fd97185f0bb9e5fcd858c792a3", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02731fa0c2dcb03d8d21a1d941bdbbe99c2946c0db098eee31008e04c6283615"},
"plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"},
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"},
"postgrex": {:hex, :postgrex, "0.17.4", "5777781f80f53b7c431a001c8dad83ee167bcebcf3a793e3906efff680ab62b3", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "6458f7d5b70652bc81c3ea759f91736c16a31be000f306d3c64bcdfe9a18b3cc"},
"progress_bar": {:hex, :progress_bar, "3.0.0", "f54ff038c2ac540cfbb4c2bfe97c75e7116ead044f3c2b10c9f212452194b5cd", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "6981c2b25ab24aecc91a2dc46623658e1399c21a2ae24db986b90d678530f2b7"},
"rajska": {:git, "https://github.com/tcitworld/rajska.git", "0c036448e261e8be6a512581c592fadf48982d84", [branch: "mobilizon"]},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
@ -135,12 +135,12 @@
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"struct_access": {:hex, :struct_access, "1.1.2", "a42e6ceedd9b9ea090ee94a6da089d56e16f374dbbc010c3eebdf8be17df286f", [:mix], [], "hexpm", "e4c411dcc0226081b95709909551fc92b8feb1a3476108348ea7e3f6c12e586a"},
"sweet_xml": {:hex, :sweet_xml, "0.7.4", "a8b7e1ce7ecd775c7e8a65d501bc2cd933bff3a9c41ab763f5105688ef485d08", [:mix], [], "hexpm", "e7c4b0bdbf460c928234951def54fe87edf1a170f6896675443279e2dbeba167"},
"swoosh": {:hex, :swoosh, "1.14.1", "d8813699ba410509008dd3dfdb2df057e3fce367d45d5e6d76b146a7c9d559cd", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.4 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87da72260b4351678f96aec61db5c2acc8a88cda2cf2c4f534eb4c9c461350c7"},
"swoosh": {:hex, :swoosh, "1.14.3", "949e6bf6dd469449238a94ec6f19ec10b63fc8753de7f3ebe3d3aeaf772f4c6b", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.4 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6c565103fc8f086bdd96e5c948660af8e20922b7a90a75db261f06a34f805c8b"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"tesla": {:hex, :tesla, "1.8.0", "d511a4f5c5e42538d97eef7c40ec4f3e44effdc5068206f42ed859e09e51d1fd", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "10501f360cd926a309501287470372af1a6e1cbed0f43949203a4c13300bc79f"},
"timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"},
"tls_certificate_check": {:hex, :tls_certificate_check, "1.20.0", "1ac0c53f95e201feb8d398ef9d764ae74175231289d89f166ba88a7f50cd8e73", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "ab57b74b1a63dc5775650699a3ec032ec0065005eff1f020818742b7312a8426"},
"tz_world": {:hex, :tz_world, "1.3.1", "dedb8373fce594098909ff36d37f5e5e30e47cb40ef846d1dfc91eb39f7ebaaf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "901ed2b4a4430ecab3765244da4a19e6f19141867c2ab3753924919b87ed2224"},
"tls_certificate_check": {:hex, :tls_certificate_check, "1.21.0", "042ab2c0c860652bc5cf69c94e3a31f96676d14682e22ec7813bd173ceff1788", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6cee6cffc35a390840d48d463541d50746a7b0e421acaadb833cfc7961e490e7"},
"tz_world": {:hex, :tz_world, "1.3.2", "15d331ad1ff22735dfcc8c98bfc7b2a9fdc17f1f071e31e21cdafe2d9318a300", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d1a345e07b3378c4c902ad54fbd5d54c8c3dd55dba883b7407fe57bcec45ff2a"},
"tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"},
"ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"},
"ueberauth_cas": {:hex, :ueberauth_cas, "2.3.1", "df45a1f2c5df8bc80191cbca4baeeed808d697702ec5ebe5bd5d5a264481752f", [:mix], [{:httpoison, "~> 1.8", [hex: :httpoison, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "5068ae2b9e217c2f05aa9a67483a6531e21ba0be9a6f6c8749bb7fd1599be321"},
@ -155,7 +155,7 @@
"unplug": {:hex, :unplug, "1.0.0", "8ec2479de0baa9a6283c04a1cc616c5ca6c5b80b8ff1d857481bb2943368dbbc", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d171a85758aa412d4e85b809c203e1b1c4c76a4d6ab58e68dc9a8a8acd9b7c3a"},
"unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"},
"vite_phx": {:hex, :vite_phx, "0.3.1", "0f7d8cd98018547a7d97122552fff3e2d70df2820bc70e8d4f31156dcb60f23a", [:mix], [{:jason, ">= 0.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, ">= 0.0.0", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "08b1726094a131490ff0a2c7764c4cdd4b5cdf8ba9762638a5dd4bcd9e5fc936"},
"web_push_encryption": {:git, "https://github.com/danhper/elixir-web-push-encryption.git", "70f00d06cbd88c9ac382e0ad2539e54448e9d8da", []},
"web_push_encryption": {:git, "https://github.com/danhper/elixir-web-push-encryption.git", "6e143dcde0a2854c4f0d72816b7ecab696432779", []},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"},
"xml_builder": {:hex, :xml_builder, "2.2.0", "cc5f1eeefcfcde6e90a9b77fb6c490a20bc1b856a7010ce6396f6da9719cbbab", [:mix], [], "hexpm", "9d66d52fb917565d358166a4314078d39ef04d552904de96f8e73f68f64a62c9"},

555
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -32,7 +32,8 @@
"@fullcalendar/icalendar": "^6.1.10",
"@fullcalendar/interaction": "^6.1.10",
"@fullcalendar/vue3": "^6.1.10",
"@oruga-ui/oruga-next": "^0.7.0",
"@oruga-ui/oruga-next": "^0.8.2",
"@oruga-ui/theme-oruga": "^0.2.0",
"@sentry/tracing": "^7.1",
"@sentry/vue": "^7.1",
"@tiptap/core": "^2.0.0-beta.41",

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-25 07:56+0000\n"
"PO-Revision-Date: 2023-12-05 16:34+0000\n"
"PO-Revision-Date: 2023-12-21 20:11+0000\n"
"Last-Translator: Milo Ivir <mail@milotype.de>\n"
"Language-Team: Croatian <https://weblate.framasoft.org/projects/mobilizon/"
"backend/hr/>\n"
@ -13,7 +13,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 5.2.1\n"
"X-Generator: Weblate 5.3.1\n"
#: lib/web/templates/email/password_reset.html.heex:66
#, elixir-autogen, elixir-format
@ -2125,14 +2125,14 @@ msgid "Events"
msgstr "Događaji"
#: lib/web/templates/email/report.html.heex:115
#, elixir-autogen, elixir-format, fuzzy
#, elixir-autogen, elixir-format
msgid "Flagged events"
msgstr "Označeni komentari"
msgstr "Označeni događaji"
#: lib/service/export/participants/common.ex:65
#, elixir-autogen, elixir-format, fuzzy
#, elixir-autogen, elixir-format
msgid "Participant registration date"
msgstr "Stanje sudionika"
msgstr "Datum registracije sudionika"
#: lib/web/templates/email/anonymous_participation_confirmation.html.heex:113
#: lib/web/templates/email/event_participation_confirmed.html.heex:122

View file

@ -0,0 +1,17 @@
defmodule Mobilizon.Storage.Repo.Migrations.AddActorInstances do
use Ecto.Migration
def change do
create table(:instance_actors) do
add(:domain, :string)
add(:instance_name, :string)
add(:instance_description, :string)
add(:software, :string)
add(:software_version, :string)
add(:actor_id, references(:actors, on_delete: :delete_all))
timestamps()
end
create(unique_index(:instance_actors, [:domain]))
end
end

BIN
public/img/gancio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="800px" viewBox="0 0 96.24 96.24" xml:space="preserve"
>
<g>
<path d="M48.122,0C21.587,0,0.001,21.585,0.001,48.118c0,26.535,21.587,48.122,48.12,48.122c26.532,0,48.117-21.587,48.117-48.122
C96.239,21.586,74.654,0,48.122,0z M4.857,48.118c0-6.271,1.345-12.227,3.746-17.606l20.638,56.544
C14.81,80.042,4.857,65.243,4.857,48.118z M48.122,91.385c-4.247,0-8.346-0.623-12.222-1.763L48.88,51.903l13.301,36.433
c0.086,0.215,0.191,0.411,0.308,0.596C57.992,90.514,53.16,91.385,48.122,91.385z M54.083,27.834
c2.604-0.137,4.953-0.412,4.953-0.412c2.33-0.276,2.057-3.701-0.277-3.564c0,0-7.007,0.549-11.532,0.549
c-4.25,0-11.396-0.549-11.396-0.549c-2.332-0.137-2.604,3.427-0.273,3.564c0,0,2.208,0.275,4.537,0.412l6.74,18.469l-9.468,28.395
L21.615,27.835c2.608-0.136,4.952-0.412,4.952-0.412c2.33-0.275,2.055-3.702-0.278-3.562c0,0-7.004,0.549-11.53,0.549
c-0.813,0-1.77-0.021-2.784-0.052C19.709,12.611,33.008,4.856,48.122,4.856c11.265,0,21.519,4.306,29.215,11.357
c-0.187-0.01-0.368-0.035-0.562-0.035c-4.248,0-7.264,3.702-7.264,7.679c0,3.564,2.055,6.582,4.248,10.146
c1.647,2.882,3.567,6.585,3.567,11.932c0,3.704-1.422,8-3.293,13.986l-4.315,14.421L54.083,27.834z M69.871,85.516l13.215-38.208
c2.471-6.171,3.29-11.106,3.29-15.497c0-1.591-0.104-3.07-0.292-4.449c3.38,6.163,5.303,13.236,5.301,20.758
C91.384,64.08,82.732,78.016,69.871,85.516z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -139,24 +139,31 @@ body {
@apply pl-10;
}
/* InputItems */
.inputitems-item {
/* TagInput */
.taginput {
@apply rounded bg-white dark:bg-zinc-700;
}
.taginput-item {
@apply bg-primary mr-2;
}
.inputitems-item:first-child {
.taginput-item:first-child {
@apply ml-2;
}
/* Autocomplete */
.autocomplete-menu {
@apply max-h-[200px] drop-shadow-md text-black;
.autocomplete {
@apply max-h-[200px] drop-shadow-md text-black z-10;
}
.autocomplete-item {
@apply py-1.5 px-4 text-start;
}
.autocomplete-item-group-title {
@apply opacity-50 py-0 px-2;
}
/* Dropdown */
.dropdown {
@apply inline-flex relative;
@ -218,6 +225,9 @@ body {
}
/* Radio */
.radio {
@apply mr-2;
}
.form-radio {
@apply bg-none text-primary accent-primary;
}
@ -293,7 +303,7 @@ button.menubar__button {
@apply px-3 dark:text-black;
}
.pagination-link-current {
@apply bg-primary dark:bg-primary cursor-not-allowed pointer-events-none border-primary text-white dark:text-zinc-900;
@apply bg-primary dark:bg-primary cursor-not-allowed pointer-events-none border-primary text-white;
}
.pagination-ellipsis {
@apply text-center m-1 text-gray-300;

View file

@ -1,5 +1,5 @@
<template>
<o-inputitems
<o-taginput
:modelValue="modelValueWithDisplayName"
@update:modelValue="(val: IActor[]) => $emit('update:modelValue', val)"
:data="availableActors"
@ -8,12 +8,12 @@
:open-on-focus="false"
field="displayName"
:placeholder="t('Add a recipient')"
@typing="getActors"
@input="getActors"
>
<template #default="props">
<ActorInline :actor="props.option" />
</template>
</o-inputitems>
</o-taginput>
</template>
<script setup lang="ts">

View file

@ -235,7 +235,7 @@ import type { Locale } from "date-fns";
import ReportModal from "@/components/Report/ReportModal.vue";
import { useCreateReport } from "@/composition/apollo/report";
import { Snackbar } from "@/plugins/snackbar";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
import RouteName from "@/router/name";
const router = useRouter();
@ -372,7 +372,7 @@ const reportComment = async (
};
const snackbar = inject<Snackbar>("snackbar");
const { oruga } = useProgrammatic();
const { notification } = useOruga();
onCreateReportError((e) => {
isReportModalActive.value = false;
@ -387,7 +387,7 @@ onCreateReportError((e) => {
oneCreateReportDone(() => {
isReportModalActive.value = false;
oruga.notification.open({
notification.open({
message: t("Comment from {'@'}{username} reported", {
username: props.comment.actor?.preferredUsername,
}),

View file

@ -157,7 +157,7 @@ import type { Locale } from "date-fns";
import { useI18n } from "vue-i18n";
import { useCreateReport } from "@/composition/apollo/report";
import { Snackbar } from "@/plugins/snackbar";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
import ReportModal from "@/components/Report/ReportModal.vue";
const Editor = defineAsyncComponent(
@ -220,7 +220,7 @@ const reportComment = async (
};
const snackbar = inject<Snackbar>("snackbar");
const { oruga } = useProgrammatic();
const { notification } = useOruga();
onCreateReportError((e) => {
isReportModalActive.value = false;
@ -235,7 +235,7 @@ onCreateReportError((e) => {
oneCreateReportDone(() => {
isReportModalActive.value = false;
oruga.notification.open({
notification.open({
message: t("Comment from {'@'}{username} reported", {
username: props.modelValue.actor?.preferredUsername,
}),

View file

@ -381,7 +381,7 @@ import { ApolloCache, FetchResult } from "@apollo/client/core";
import { useMutation } from "@vue/apollo-composable";
import { useCreateReport } from "@/composition/apollo/report";
import { useDeleteEvent } from "@/composition/apollo/event";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
import ExternalParticipationButton from "./ExternalParticipationButton.vue";
import AccountMultiple from "vue-material-design-icons/AccountMultiple.vue";
import Bullhorn from "vue-material-design-icons/Bullhorn.vue";
@ -619,11 +619,11 @@ onJoinEventMutationDone(({ data }) => {
}
});
const { oruga } = useProgrammatic();
const { notification } = useOruga();
onJoinEventMutationError((error) => {
if (error.message) {
oruga.notification.open({
notification.open({
message: error.message,
variant: "danger",
position: "bottom-right",

View file

@ -10,9 +10,9 @@
</div>
</div>
<o-field
grouped
:label="$t('Find or add an element')"
label-for="event-metadata-autocomplete"
class="flex-wrap justify-center gap-2"
>
<o-autocomplete
expanded
@ -27,6 +27,7 @@
id="event-metadata-autocomplete"
@select="addElement"
dir="auto"
class="flex-1 min-w-[200px]"
>
<template v-slot="props">
<div class="dark:bg-violet-3 p-1 flex items-center gap-1">

View file

@ -337,7 +337,7 @@ import ViewCompact from "vue-material-design-icons/ViewCompact.vue";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import AccountGroup from "vue-material-design-icons/AccountGroup.vue";
import Video from "vue-material-design-icons/Video.vue";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
import { computed, inject } from "vue";
import { useI18n } from "vue-i18n";
import { Dialog } from "@/plugins/dialog";
@ -401,7 +401,7 @@ const openDeleteEventModal = (
});
};
const { oruga } = useProgrammatic();
const { notification } = useOruga();
const snackbar = inject<Snackbar>("snackbar");
const {
@ -419,7 +419,7 @@ onDeleteEventDone(() => {
*/
emit("eventDeleted", props.participation.event.id);
oruga.notification.open({
notification.open({
message: t("Event {eventTitle} deleted", {
eventTitle: props.participation.event.title,
}),
@ -460,7 +460,7 @@ const gotToWithCheck = async (
) {
const organizerActor = participation.event.organizerActor as IPerson;
await changeIdentity(organizerActor);
oruga.notification.open({
notification.open({
message: t(
"Current identity has been changed to {identityName} in order to manage this event.",
{

View file

@ -24,9 +24,9 @@
:required="isRequired"
v-model="queryTextWithDefault"
:placeholder="placeholderWithDefault"
:customFormatter="(elem: IAddress) => addressFullName(elem)"
:debounceTyping="debounceDelay"
@typing="asyncData"
:formatter="(elem: IAddress) => addressFullName(elem)"
:debounce="debounceDelay"
@input="asyncData"
:icon="canShowLocateMeButton ? null : 'map-marker'"
expanded
@select="setSelected"
@ -115,30 +115,39 @@
required
v-model="selected.description"
id="addressNameInput"
expanded
/>
</o-field>
<o-field :label="t('Street')" labelFor="streetInput">
<o-input v-model="selected.street" id="streetInput" />
<o-input v-model="selected.street" id="streetInput" expanded />
</o-field>
<o-field grouped>
<o-field :label="t('Postal Code')" labelFor="postalCodeInput">
<o-input v-model="selected.postalCode" id="postalCodeInput" />
<o-input
v-model="selected.postalCode"
id="postalCodeInput"
expanded
/>
</o-field>
<o-field :label="t('Locality')" labelFor="localityInput">
<o-input v-model="selected.locality" id="localityInput" />
<o-input
v-model="selected.locality"
id="localityInput"
expanded
/>
</o-field>
</o-field>
<o-field grouped>
<o-field :label="t('Region')" labelFor="regionInput">
<o-input v-model="selected.region" id="regionInput" />
<o-input v-model="selected.region" id="regionInput" expanded />
</o-field>
<o-field :label="t('Country')" labelFor="countryInput">
<o-input v-model="selected.country" id="countryInput" />
<o-input v-model="selected.country" id="countryInput" expanded />
</o-field>
</o-field>
</section>

View file

@ -11,7 +11,7 @@
<HelpCircleOutline :size="16" />
</o-tooltip>
</template>
<o-inputitems
<o-taginput
v-model="tagsStrings"
:data="filteredTags"
:allow-autocomplete="true"
@ -21,11 +21,11 @@
:maxlength="20"
:maxitems="10"
:placeholder="$t('Eg: Stockholm, Dance, Chess…')"
@typing="debouncedGetFilteredTags"
@input="debouncedGetFilteredTags"
:id="id"
dir="auto"
>
</o-inputitems>
</o-taginput>
</o-field>
</template>
<script lang="ts" setup>

View file

@ -17,21 +17,20 @@
autocomplete="off"
autocorrect="off"
maxlength="1024"
expanded
/>
<o-button native-type="submit" icon-left="magnify">
</o-button>
<o-button native-type="submit" icon-left="magnify"> </o-button>
</form>
</template>
<script lang="ts" setup>
import { IAddress } from "@/types/address.model";
import { AddressSearchType } from "@/types/enums";
import { computed, defineAsyncComponent } from "vue";
import { useI18n } from "vue-i18n";
import { useRouter, useRoute } from "vue-router";
import RouteName from "@/router/name";
const FullAddressAutoComplete = defineAsyncComponent(
defineAsyncComponent(
() => import("@/components/Event/FullAddressAutoComplete.vue")
);
@ -91,7 +90,7 @@ const submit = () => {
const { t } = useI18n({ useScope: "global" });
</script>
<style scoped>
#search-anchor :deep(.o-ctrl-input) {
#search-anchor :deep(.o-input__wrapper) {
flex: 1;
}
</style>

View file

@ -239,7 +239,7 @@ import { useMutation } from "@vue/apollo-composable";
import { UPDATE_DEFAULT_ACTOR } from "@/graphql/actor";
import { changeIdentity } from "@/utils/identity";
import { useRegistrationConfig } from "@/composition/apollo/config";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
import SearchFields from "@/components/Home/SearchFields.vue";
const { currentUser } = useCurrentUserClient();
@ -298,12 +298,12 @@ onDone(({ data }) => {
const showMobileMenu = ref(false);
const { oruga } = useProgrammatic();
const { notification } = useOruga();
const performLogout = async () => {
console.debug("Logging out client...");
await logout();
oruga.notification.open({
notification.open({
message: t("You have been logged-out"),
variant: "success",
position: "bottom-right",

View file

@ -55,6 +55,7 @@
id="additional-comments"
autofocus
ref="reportAdditionalCommentsInput"
expanded
/>
</o-field>

View file

@ -2,8 +2,8 @@
<li
class="setting-menu-item"
:class="{
'cursor-pointer bg-mbz-yellow-alt-500 dark:bg-mbz-purple-500': isActive,
'bg-mbz-yellow-alt-100 hover:bg-mbz-yellow-alt-200 dark:bg-mbz-purple-300 dark:hover:bg-mbz-purple-400 dark:text-white':
'cursor-pointer bg-mbz-yellow-alt-500 dark:bg-mbz-purple-600': isActive,
'bg-mbz-yellow-alt-100 hover:bg-mbz-yellow-alt-200 dark:bg-mbz-purple-500 dark:hover:bg-mbz-purple-600 dark:text-white':
!isActive,
}"
>

View file

@ -1,6 +1,6 @@
<template>
<li
class="bg-mbz-yellow-alt-300 text-violet-2 dark:bg-mbz-purple-500 dark:text-zinc-100 text-xl"
class="bg-mbz-yellow-alt-300 text-violet-2 dark:bg-mbz-purple-700 dark:text-zinc-100 text-xl"
>
<router-link
class="cursor-pointer my-2 mx-0 py-2 px-3 font-medium block no-underline"

View file

@ -13,10 +13,9 @@
<o-tooltip
:label="t('URL copied to clipboard')"
:active="showCopiedTooltip"
always
variant="success"
position="left"
>
/>
<o-button
variant="primary"
icon-right="content-paste"
@ -25,7 +24,6 @@
@keyup.enter="copyURL"
:title="t('Copy URL to clipboard')"
/>
</o-tooltip>
</p>
</o-field>
<div class="flex flex-wrap gap-1">
@ -132,7 +130,7 @@ const props = withDefaults(
const { t } = useI18n({ useScope: "global" });
const URLInput = ref<{ $refs: { input: HTMLInputElement } } | null>(null);
const URLInput = ref<{ $refs: { inputRef: HTMLInputElement } } | null>(null);
const showCopiedTooltip = ref(false);
@ -162,7 +160,7 @@ const mastodonShare = computed((): string | undefined =>
);
const copyURL = (): void => {
URLInput.value?.$refs.input.select();
URLInput.value?.$refs.inputRef.select();
document.execCommand("copy");
showCopiedTooltip.value = true;
setTimeout(() => {

View file

@ -81,6 +81,10 @@ export const INSTANCE_FRAGMENT = gql`
fragment InstanceFragment on Instance {
domain
hasRelay
instanceName
instanceDescription
software
softwareVersion
relayAddress
followerStatus
followedStatus

View file

@ -1638,5 +1638,8 @@
"Return to the event page": "Return to the event page",
"Cancel participation": "Cancel participation",
"Add a recipient": "Add a recipient",
"Announcements for {eventTitle}": "Announcements for {eventTitle}"
"Announcements for {eventTitle}": "Announcements for {eventTitle}",
"Visit {instance_domain}": "Visit {instance_domain}",
"Software details: {software_details}": "Software details: {software_details}",
"Only instances with an application actor can be followed": "Only instances with an application actor can be followed"
}

View file

@ -1632,5 +1632,8 @@
"Return to the event page": "Retourner à la page de l'événement",
"Cancel participation": "Annuler la participation",
"Add a recipient": "Ajouter un·e destinataire",
"Announcements for {eventTitle}": "Annonces pour {eventTitle}"
"Announcements for {eventTitle}": "Annonces pour {eventTitle}",
"Visit {instance_domain}": "Visiter {instance_domain}",
"Software details: {software_details}": "Détails du logiciel : {software_details}",
"Only instances with an application actor can be followed": "Seules les instances avec un acteur application peuvent être suivies"
}

View file

@ -45,7 +45,7 @@
"Accept": "Aceptar",
"Accepted": "Aceptado",
"Accessibility": "Accesibilidade",
"Accessible only by link": "",
"Accessible only by link": "Só accesible con ligazón",
"Accessible only to members": "Accesible só para membros",
"Accessible through link": "Accesible por ligazón",
"Account": "Conta",
@ -64,15 +64,15 @@
"Add a todo": "Engadir tarefas pendentes",
"Add an address": "Engadir un enderezo",
"Add an instance": "Engadir unha instancia",
"Add link": "",
"Add link": "Engadir ligazón",
"Add new…": "Engadir novo…",
"Add picture": "",
"Add picture": "Engadir imaxe",
"Add some tags": "Engadir etiquetas",
"Add to my calendar": "Engadir ó meu calendario",
"Additional comments": "Comentarios adicionais",
"Admin": "Admin",
"Admin dashboard": "",
"Admin settings": "",
"Admin dashboard": "Taboleiro de Admin",
"Admin settings": "Axustes de Admin",
"Admin settings successfully saved.": "Gardáronse os axustes de Admin.",
"Administration": "Administración",
"Administrator": "Administradora",
@ -93,15 +93,16 @@
"An event from one of my groups has been published": "Publicouse un evento nun dos meus grupos",
"An event from one of my groups has been updated or deleted": "Actualizouse ou eliminouse un evento nun dos meus grupos",
"An instance is an installed version of the Mobilizon software running on a server. An instance can be run by anyone using the {mobilizon_software} or other federated apps, aka the “fediverse”. This instance's name is {instance_name}. Mobilizon is a federated network of multiple instances (just like email servers), users registered on different instances may communicate even though they didn't register on the same instance.": "Unha instancia é unha versión do software Mobilizon instalada nun servidor. Calquera persoa pode instalar unha instancia usando o {mobilizon_software} ou outras apps federadas, coñecidas como \"fediverso\". O nome desta instancia é {instance_name}. Mobilizon é unha rede federada de múltiples instancias (como os servidores de correo), usuarias rexistradas en diferentes servidores que poden comunicarse incluso se non están rexistradas na mesma instancia.",
"An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events, automatically and remotely.": "Unha \"interface de programación de aplicacións\" ou \"API\" é un protocolo de comunicación que permite aos compoñentes do software comunicarse entre eles. A API de Mobilizon, por exemplo, pode permitir que ferramentas de terceiras partes se comuniquen con instancias Mobilizon para realizar certas accións, como publicar eventos de xeito automático e remoto.",
"And {number} comments": "E {number} comentarios",
"Announcements and mentions notifications are always sent straight away.": "As notificacións de mencións e anuncios sempre son enviadas de todas formas.",
"Anonymous participant": "Participante anónimo",
"Anonymous participants will be asked to confirm their participation through e-mail.": "Ós participantes anónimos pediráselle que confirmen a súa participación a través de email.",
"Anonymous participations": "Participacións anónimas",
"Any day": "Calquera día",
"Any type": "",
"Any type": "Calquera tipo",
"Anyone can join freely": "Calquera pode unirse",
"Anyone can request being a member, but an administrator needs to approve the membership.": "",
"Anyone can request being a member, but an administrator needs to approve the membership.": "Calquera pode solicitar membresía, pero a administración ten que aprobar a solicitude.",
"Anyone wanting to be a member from your group will be able to from your group page.": "Calquera que queira ser membro do teu grupo pode facelo desde a páxina do grupo.",
"Application": "Aplicación",
"Apply filters": "Aplicar filtros",
@ -118,12 +119,12 @@
"Are you sure you want to delete this entire discussion?": "Tes a certeza de querer eliminar o debate completo?",
"Are you sure you want to delete this event? This action cannot be reverted.": "¿Tes a certeza de que queres eliminar este evento? Esta acción non é reversible.",
"Are you sure you want to delete this post? This action cannot be reverted.": "Tes certeza de querer eliminar a publicación? Esta acción non ten volta.",
"Are you sure you want to leave the group {groupName}? You'll loose access to this group's private content. This action cannot be undone.": "",
"Are you sure you want to leave the group {groupName}? You'll loose access to this group's private content. This action cannot be undone.": "Tes certeza de querer deixar o grupo {groupName}? Non poderás acceder ao contido privado do grupo. Esta acción non ten volta atrás.",
"As the event organizer has chosen to manually validate participation requests, your participation will be really confirmed only once you receive an email stating it's being accepted.": "Como a organización do evento escolleu validar manualmente as solicitudes, a túa participación estará realmente confirmada cando recibas un correo informándote.",
"Ask your instance admin to {enable_feature}.": "Pídelle á administración da instancia que {enable_feature}.",
"Assigned to": "Asignado a",
"Atom feed for events and posts": "Fonte Atom para eventos e publicacións",
"Attending": "",
"Attending": "Participan",
"Avatar": "Avatar",
"Back to group list": "",
"Back to previous page": "Volver á páxina anterior",
@ -139,7 +140,7 @@
"Booking": "Reservas",
"Breadcrumbs": "Breadcrumbs",
"Browser notifications": "Notificacións do navegador",
"Bullet list": "",
"Bullet list": "Lista de puntos",
"By others": "Por outras",
"By {group}": "Por {group}",
"By {username}": "Por {username}",
@ -147,10 +148,10 @@
"Cancel": "Cancelar",
"Cancel anonymous participation": "Cancelar participación anónima",
"Cancel creation": "Cancelar creación",
"Cancel discussion title edition": "",
"Cancel discussion title edition": "Desbotar a edición do título da conversa",
"Cancel edition": "Cancelar edición",
"Cancel follow request": "",
"Cancel membership request": "",
"Cancel follow request": "Desbotar solicitude de seguimento",
"Cancel membership request": "Desbotar a solicitude de membresía",
"Cancel my participation request…": "Cancelar a miña solicitude de participación…",
"Cancel my participation…": "Cancelar a miña participación…",
"Cancelled": "Cancelado",
@ -164,21 +165,21 @@
"Change timezone": "Cambiar zona horaria",
"Change user email": "Cambiar correo da usuaria",
"Check your inbox (and your junk mail folder).": "Comproba a caixa de correo (e o cartafol de spam).",
"Choose the source of the instance's Privacy Policy": "",
"Choose the source of the instance's Terms": "",
"Choose the source of the instance's Privacy Policy": "Escolle a orixe da Política de Privacidade da instancia",
"Choose the source of the instance's Terms": "Elixe a orixe dos Termos da instancia",
"City or region": "Cidade ou rexión",
"Clear": "Baleirar",
"Clear address field": "",
"Clear date filter field": "",
"Clear address field": "Limpar campo de enderezo",
"Clear date filter field": "Limpar campo de data no filtro",
"Clear participation data for all events": "Eliminar os datos de participación para todos os eventos",
"Clear participation data for this event": "Eliminar os datos de participación para este evento",
"Clear timezone field": "",
"Clear timezone field": "Limpar o campo de zona horaria",
"Click for more information": "Preme para saber máis",
"Click to upload": "Preme para subir",
"Close": "Pechar",
"Close comments for all (except for admins)": "Pechar comentarios para todos (excepto admins)",
"Closed": "Pechado",
"Comment body": "",
"Comment body": "Corpo do comentario",
"Comment deleted": "Comentario eliminado",
"Comment from {'@'}{username} reported": "Denuncia sobre o comentario de {'@'}{username}",
"Comment text can't be empty": "O texto do comentario non pode estar baleiro",
@ -212,7 +213,7 @@
"Create discussion": "Crear conversa",
"Create event": "Crear evento",
"Create group": "Crear grupo",
"Create identity": "",
"Create identity": "Crear identidade",
"Create my event": "Crear o meu evento",
"Create my group": "Crear o meu grupo",
"Create my profile": "Crear o meu perfil",
@ -235,7 +236,7 @@
"Date and time settings": "Axustes de data e hora",
"Date parameters": "Parámetros da data",
"Decline": "Rexeitar",
"Decrease": "",
"Decrease": "Diminuir",
"Default": "Por omisión",
"Default Mobilizon privacy policy": "Política de privacidade por omisión para Mobilizon",
"Default Mobilizon terms": "Termos Mobilizon por omisión",
@ -263,7 +264,7 @@
"Didn't receive the instructions?": "Non recibiches as instruccións?",
"Disabled": "Desactivado",
"Discussions": "Comentando",
"Discussions list": "",
"Discussions list": "Lista de conversas",
"Display name": "Mostrar nome",
"Display participation price": "Mostrar prezo da participación",
"Displayed nickname": "Nome mostrado",
@ -272,7 +273,7 @@
"Do you really want to suspend the account « {emailAccount} » ?": "Tes certeza de querer suspender a conta « {emailAccount} » ?",
"Do you wish to {create_event} or {explore_events}?": "Queres {create_event} ou {explore_events}?",
"Do you wish to {create_group} or {explore_groups}?": "Queres {create_group} ou {explore_groups}?",
"Does the event needs to be confirmed later or is it cancelled?": "",
"Does the event needs to be confirmed later or is it cancelled?": "Precísase confirmar posteriormente o evento ou se foi cancelado?",
"Domain": "Dominio",
"Draft": "Borrador",
"Drafts": "Borradores",
@ -323,12 +324,12 @@
"Event already passed": "O evento xa rematou",
"Event cancelled": "Evento cancelado",
"Event creation": "Evento creado",
"Event description body": "",
"Event description body": "Corpo da descrición do evento",
"Event edition": "Edición do evento",
"Event list": "Lista de eventos",
"Event metadata": "Metadatos do evento",
"Event page settings": "Páxina de axustes do evento",
"Event timezone will default to the timezone of the event's address if there is one, or to your own timezone setting.": "",
"Event timezone will default to the timezone of the event's address if there is one, or to your own timezone setting.": "A zona horaria do evento terá por defecto a zona horaria do enderezo do evento (se está indicada), ou a túa zona horaria nos axustes.",
"Event to be confirmed": "Evento agardando confirmación",
"Event {eventTitle} deleted": "Eliminado o evento {eventTitle}",
"Event {eventTitle} reported": "Denunciado o evento {eventTitle}",
@ -341,7 +342,7 @@
"Explore": "Descubre",
"Explore events": "Descubrir eventos",
"Explore!": "Descubre!",
"Export": "",
"Export": "Exportar",
"Failed to get location.": "Non se obtivo a localización.",
"Failed to save admin settings": "Fallo ó gardar os axustes de admin",
"Featured events": "Eventos destacados",
@ -356,19 +357,19 @@
"Find an instance": "Atopar unha instancia",
"Find another instance": "Atopa outra instancia",
"Find or add an element": "Atopa ou engade un elemento",
"First steps": "",
"First steps": "Primeiros pasos",
"Follow": "Seguir",
"Follower": "Seguidora",
"Followers": "Seguidoras",
"Followers will receive new public events and posts.": "As seguidoras recibirán os novos eventos públicos e publicacións.",
"Following the group will allow you to be informed of the {group_upcoming_public_events}, whereas joining the group means you will {access_to_group_private_content_as_well}, including group discussions, group resources and members-only posts.": "",
"Following the group will allow you to be informed of the {group_upcoming_public_events}, whereas joining the group means you will {access_to_group_private_content_as_well}, including group discussions, group resources and members-only posts.": "Ao seguir o grupo poderás recibir información sobre {group_upcoming_public_events}, mentras que ao unirte ao grupo poderás {access_to_group_private_content_as_well}, incluíndo conversas do grupo, recursos do grupo e publicacións só para membros.",
"Followings": "Seguindo",
"For instance: London": "Por exemplo: Allariz",
"For instance: London, Taekwondo, Architecture…": "Por exemplo: Leiro, Natación, Arquitectura…",
"Forgot your password ?": "¿Esqueceches o contrasinal?",
"Forgot your password?": "Esqueceches o contrasinal?",
"Framadate poll": "Enquisa Framadate",
"From my groups": "",
"From my groups": "Dos meus grupos",
"From the {startDate} at {startTime} to the {endDate}": "Desde o {startDate} ás {startTime} ata o {endDate}",
"From the {startDate} at {startTime} to the {endDate} at {endTime}": "Desde o {startDate} ás {startTime} ata o {endDate} ás {endTime}",
"From the {startDate} to the {endDate}": "Desde o {startDate} ata o {endDate}",
@ -378,7 +379,7 @@
"General": "Xeral",
"General information": "Información xeral",
"General settings": "Axustes xerais",
"Geolocation was not determined in time.": "",
"Geolocation was not determined in time.": "Non se obtivo a xeolocalización a tempo.",
"Getting location": "Obtendo localización",
"Getting there": "Chegar alí",
"Glossary": "Glosario",
@ -392,10 +393,10 @@
"Group URL": "URL do grupo",
"Group activity": "Actividade do grupo",
"Group address": "Enderezo do grupo",
"Group description body": "",
"Group description body": "Corpo da descrición do grupo",
"Group display name": "Nome mostrado do grupo",
"Group name": "Nome do grupo",
"Group profiles": "",
"Group profiles": "Perfís do grupo",
"Group settings": "Axustes do grupo",
"Group settings saved": "Gardáronse os axustes",
"Group short description": "Descrición curta do grupo",
@ -405,9 +406,9 @@
"Groups": "Grupos",
"Groups are not enabled on this instance.": "Os grupos non están activados nesta instancia.",
"Groups are spaces for coordination and preparation to better organize events and manage your community.": "Os grupos son espazos de coordinación e preparación, para organizar os eventos e xestionar a comunidade.",
"Heading Level 1": "",
"Heading Level 2": "",
"Heading Level 3": "",
"Heading Level 1": "Cabeceira Nivel 1",
"Heading Level 2": "Cabeceira Nivel 2",
"Heading Level 3": "Cabeceira Nivel 3",
"Headline picture": "Imaxe de cabeceira",
"Hide replies": "Agochar respostas",
"Home": "Inicio",
@ -437,11 +438,11 @@
"If you have opted for manual validation of participants, Mobilizon will send you an email to inform you of new participations to be processed. You can choose the frequency of these notifications below.": "Se optaches pola validación manual das participantes, Mobilizon enviarache un correo para informarte das novas solicitudes a tratar. Podes escoller a frecuencia destas notificacións.",
"If you want, you may send a message to the event organizer here.": "Se o desexas, aquí podes enviar unha mensaxe á organización do evento.",
"Ignore": "Ignorar",
"In person": "",
"In person": "En persoa",
"In the following context, an application is a software, either provided by the Mobilizon team or by a 3rd-party, used to interact with your instance.": "No seguinte contexto, unha aplicación é un software, proporcionado polo equipo Mobilizon ou por terceiros, utilizado para interactuar coa túa instancia.",
"In the past": "",
"In the past": "No pasado",
"In this instance's network": "Na rede desta instancia",
"Increase": "",
"Increase": "Aumentar",
"Instance": "Instancia",
"Instance Long Description": "Descrición longa da instancia",
"Instance Name": "Nome da instancia",
@ -464,7 +465,7 @@
"Instances following you": "Instancias que te seguen",
"Instances you follow": "Instancias que segues",
"Integrate this event with 3rd-party tools and show metadata for the event.": "Integrar este evento en ferramentas de terceiras parte e mostrar metadatos do evento.",
"Interact": "",
"Interact": "Interactuar",
"Interact with a remote content": "Interactuar cun evento remoto",
"Invite a new member": "Convida a un novo membro",
"Invite member": "Convida a persoa",
@ -492,7 +493,7 @@
"Least recently published": "Publicado máis antigo",
"Leave": "Saír",
"Leave event": "Deixar o evento",
"Leave group": "",
"Leave group": "Deixar o grupo",
"Leaving event \"{title}\"": "Saíndo do evento \"{title}\"",
"Legal": "Legal",
"Let's define a few settings": "Define algúns parámetros",
@ -504,7 +505,8 @@
"Load more activities": "Cargar máis actividades",
"Loading comments…": "Cargando comentarios…",
"Local": "Local",
"Local time ({timezone})": "",
"Local time ({timezone})": "Hora local ({timezone})",
"Local times ({timezone})": "Horas locais ({timezone})",
"Locality": "Localidade",
"Location": "Localización",
"Log in": "Acceder",
@ -535,7 +537,7 @@
"Mobilizon uses a system of profiles to compartiment your activities. You will be able to create as many profiles as you want.": "Mobilizon utiliza un sistema de perfís para organizar as túas actividades. Poderás crear tantos perfís como queiras.",
"Mobilizon version": "Versión Mobilizon",
"Mobilizon will send you an email when the events you are attending have important changes: date and time, address, confirmation or cancellation, etc.": "Mobilizón enviarache un correo cando os eventos nos que participes teñan cambios importantes: data e hora, enderezos, confirmación ou cancelación, etc.",
"Moderate new members": "",
"Moderate new members": "Moderar novos membros",
"Moderated comments (shown after approval)": "Comentarios moderados (mostrados após aprobación)",
"Moderation": "Moderación",
"Moderation log": "Rexistro da moderación",
@ -551,7 +553,7 @@
"My identities": "Identidades",
"NOTE! The default terms have not been checked over by a lawyer and thus are unlikely to provide full legal protection for all situations for an instance admin using them. They are also not specific to all countries and jurisdictions. If you are unsure, please check with a lawyer.": "AVISO! Os termos por omisión non foron revisados por avogados e dificilmente proporcionan cobertura legal completa para todas as situacións que se dean nun instancia. Tampouco son específicas para todos os países e xurisdicións. Se non estás segura, consúltaas cun avogado.",
"Name": "Nome",
"Navigated to {pageTitle}": "",
"Navigated to {pageTitle}": "Ir a {pageTitle}",
"New discussion": "Novo debate",
"New email": "Novo correo",
"New folder": "Novo cartafol",
@ -629,7 +631,7 @@
"On {date} from {startTime} to {endTime}": "O {date} desde {startTime} ás {endTime}",
"On {date} starting at {startTime}": "O {date} comezando ás {startTime}",
"On {instance} and other federated instances": "En {instance} e outras instancias federadas",
"Online": "",
"Online": "En liña",
"Online ticketing": "Entradas por internet",
"Only accessible through link": "Accesible só a través da ligazón",
"Only accessible through link (private)": "Só accesible desde a ligazón {private}",
@ -638,13 +640,13 @@
"Only group members can access discussions": "Só os membros do grupo poden acceder ás conversas",
"Only group moderators can create, edit and delete events.": "Só os moderadores do grupo poden crear, editar e eliminar eventos.",
"Only group moderators can create, edit and delete posts.": "Só as moderadoras do grupo poden crear, editar e eliminar publicacións.",
"Only registered users may fetch remote events from their URL.": "",
"Only registered users may fetch remote events from their URL.": "Só as usuarias rexistradas poden recibir elementos remotos co seu URL.",
"Open": "Abrir",
"Open a topic on our forum": "Abrir un tema no noso foro",
"Open an issue on our bug tracker (advanced users)": "Abrir un informe no noso seguimento de fallos (usuarias avanzadas)",
"Opened reports": "Denuncias abertas",
"Or": "Ou",
"Ordered list": "",
"Ordered list": "Lista ordenada",
"Organized": "Organizado",
"Organized by": "Organizado por",
"Organized by {name}": "Organizado por {name}",
@ -670,8 +672,8 @@
"Participation confirmation": "Confirmación da participación",
"Participation notifications": "Notificacións da participación",
"Participation requested!": "Participación solicitada!",
"Participation with account": "",
"Participation without account": "",
"Participation with account": "Participación coa conta",
"Participation without account": "Participación sen conta",
"Participations": "Participacións",
"Password": "Contrasinal",
"Password (confirmation)": "Contrasinal (confirmación)",
@ -693,19 +695,19 @@
"Please make sure the address is correct and that the page hasn't been moved.": "Comproba ben o enderezo e que a páxina non foi movida a outro lugar.",
"Please read the {fullRules} published by {instance}'s administrators.": "Por favor le as {fullRules} publicadas pola administración de {instance}.",
"Post": "Publicación",
"Post URL": "",
"Post URL": "URL da publicación",
"Post a comment": "Comenta",
"Post a reply": "Publica unha resposta",
"Post body": "",
"Post body": "Corpo da publicación",
"Post {eventTitle} reported": "Denunciado o evento {eventTitle}",
"Postal Code": "Código Postal",
"Posts": "Publicacións",
"Powered by Mobilizon": "",
"Powered by Mobilizon": "Grazas a Mobilizon",
"Powered by {mobilizon}. © 2018 - {date} The Mobilizon Contributors - Made with the financial support of {contributors}.": "Funciona grazas a {mobilizon}. © 2018 - {date} The Mobilizon Contributors - Co soporte financieiro das {contributors}.",
"Preferences": "Preferencias",
"Previous": "Anterior",
"Previous email": "Correo anterior",
"Previous month": "",
"Previous month": "Mes anterior",
"Previous page": "Páxina anterior",
"Price sheet": "Tarifa",
"Privacy": "Privacidade",
@ -727,7 +729,7 @@
"Public preview": "Vista previa pública",
"Publication date": "Data de publicación",
"Publish": "Publicar",
"Published by {name}": "",
"Published by {name}": "Publicado por {name}",
"Published events with <b>{comments}</b> comments and <b>{participations}</b> confirmed participations": "Eventos publicados con <b>{comments}</b> comentarios e <b>{participations}</b> participacións confirmadas",
"Push": "Push",
"Quote": "Citar",
@ -737,9 +739,9 @@
"Receive one email for each activity": "Recibir un correo por cada actividade",
"Receive one email per request": "Recibir un correo por solicitude",
"Redirecting in progress…": "Redireccionando…",
"Redirecting to Mobilizon": "",
"Redirecting to Mobilizon": "Redirixindo a Mobilizon",
"Redirecting to content…": "Redirixindo ó contido…",
"Redo": "",
"Redo": "Volver facer",
"Refresh profile": "Actualiza perfil",
"Regenerate new links": "Recrear novas ligazóns",
"Region": "Rexión",
@ -767,7 +769,7 @@
"Report this comment": "Denunciar este comentario",
"Report this event": "Denunciar este evento",
"Report this group": "Denunciar este grupo",
"Report this post": "",
"Report this post": "Denuncia esta publicación",
"Reported": "Denunciado",
"Reported by": "Denunciado por",
"Reported by someone on {domain}": "Foi denunciado por alguén desde {domain}",
@ -775,7 +777,7 @@
"Reported group": "Grupo denunciado",
"Reported identity": "Identidade denunciada",
"Reports": "Denuncias",
"Reports list": "",
"Reports list": "Lista de denuncias",
"Request for participation confirmation sent": "Solicitar o envío da confirmación de participación",
"Resend confirmation email": "Reenviar correo de confirmación",
"Resent confirmation email": "Reenviado o correo de confirmación",
@ -806,7 +808,7 @@
"Send": "Enviar",
"Send email": "Enviar correo",
"Send notification e-mails": "Enviar emails de notificacións",
"Send password reset": "",
"Send password reset": "Enviar restablecemento de contrasinal",
"Send the confirmation email again": "Enviar o correo de confirmación outra vez",
"Send the report": "Enviar a denuncia",
"Set an URL to a page with your own privacy policy.": "Establece o URL da páxina coa túa política de privacidade.",
@ -815,20 +817,22 @@
"Share": "Compartir",
"Share this event": "Compartir este evento",
"Share this group": "Compartir este grupo",
"Share this post": "",
"Share this post": "Comparte esta publicación",
"Short bio": "Bio curta",
"Show map": "Mostrar mapa",
"Show me where I am": "Mostra onde me atopo",
"Show remaining number of places": "Mostrar o número de prazas restantes",
"Show the time when the event begins": "Mostrar a hora á que comeza o evento",
"Show the time when the event ends": "Mostar a hora na que remata o evento",
"Showing events before": "",
"Showing events starting on": "",
"Showing events before": "Mostrando eventos antes do",
"Showing events starting on": "Mostrando eventos a partir do",
"Sign Language": "Idioma de signos",
"Sign in with": "Acceder con",
"Sign up": "Rexistro",
"Since you are a new member, private content can take a few minutes to appear.": "Como es un novo membro, o contido privado podería tardar uns minutos en aparecer.",
"Skip to main content": "",
"Skip to main content": "Ir directamente ao contido",
"Smoke free": "Libre de fume",
"Smoking allowed": "Permítese fumar",
"Social": "Social",
"Some terms, technical or otherwise, used in the text below may cover concepts that are difficult to grasp. We have provided a glossary here to help you understand them better:": "Algúns termos, técnicos ou doutro tipo, utilizados no texto poderían referirse a conceptos difíciles de entender. Aquí tes un glosario para axudarche a entendelos mellor:",
"Starts on…": "Comeza o…",
@ -839,7 +843,7 @@
"Suspend": "Suspender",
"Suspend group": "Suspende grupo",
"Suspended": "Suspendida",
"Tag search": "",
"Tag search": "Buscar etiqueta",
"Task lists": "Listas de tarefas",
"Technical details": "Detalles técnicos",
"Tentative": "Provisional",
@ -847,7 +851,7 @@
"Terms": "Termos",
"Terms of service": "Termos do servizo",
"Text": "Texto",
"That you follow or of which you are a member": "",
"That you follow or of which you are a member": "Que sigues ou do que es membro",
"The Big Blue Button video teleconference URL": "URL da videoconferencia utilizando The Big Blue Button",
"The Google Meet video teleconference URL": "URL da videoconferencia a través de Google Meet",
"The Jitsi Meet video teleconference URL": "URL da videoconferencia usando Jitsi Meet",
@ -867,7 +871,7 @@
"The event has been updated": "O evento foi actualizado",
"The event has been updated and published": "O evento foi actualizado e publicado",
"The event hasn't got a sign language interpreter": "O evento non ten intérprete de lingua de signos",
"The event is fully online": "",
"The event is fully online": "Evento totalmente en liña",
"The event live video contains subtitles": "O vídeo do evento en directo contén subtítulos",
"The event live video does not contain subtitles": "O vídeo do evento en directo non contén subtítulos",
"The event organiser has chosen to validate manually participations. Do you want to add a little note to explain why you want to participate to this event?": "O organizador do evento escolleu validar manualmente as participacións. Desexas engadir unha nota explicando a razón pola que queres participar no evento?",
@ -882,7 +886,7 @@
"The event {event} was updated by {profile}.": "{profile} actualizou o evento {evento}.",
"The events you created are not shown here.": "Os eventos que ti creaches non se mostran aquí.",
"The geolocation prompt was denied.": "",
"The group can now be joined by anyone, but new members need to be approved by an administrator.": "",
"The group can now be joined by anyone, but new members need to be approved by an administrator.": "Agora calquera pode unirse ao grupo, pero os novos membros teñen que ser aprobados pola administración.",
"The group can now be joined by anyone.": "Agora calquera pode unirse ao grupo.",
"The group can now only be joined with an invite.": "Agora só se pode acceder ao grupo cun convite.",
"The group will be publicly listed in search results and may be suggested in the explore section. Only public informations will be shown on it's page.": "Este grupo aparecerá en resultados de buscas e podería ser suxerido na sección descubrir. Só se mostrará información pública nesta páxina.",
@ -933,7 +937,7 @@
"This month": "Este mes",
"This post is accessible only for members. You have access to it for moderation purposes only because you are an instance moderator.": "Esta publicación só é accesible para membros. Ti tes acceso a ela para poder moderala xa que es moderadora da instancia.",
"This post is accessible only through it's link. Be careful where you post this link.": "Este evento só é accesible coa súa ligazón. Ten tino de onde publicas esta ligazón.",
"This profile is from another instance, the informations shown here may be incomplete.": "",
"This profile is from another instance, the informations shown here may be incomplete.": "Este perfil pertence a outra instancia, a información que ves podería estar incompleta.",
"This profile was not found": "",
"This setting will be used to display the website and send you emails in the correct language.": "Este axuste usarase para mostrar o sitio web e enviarche os correos no idioma correcto.",
"This user was not found": "",
@ -941,8 +945,8 @@
"This week": "Esta semana",
"This weekend": "Este fin de semana",
"This will delete / anonymize all content (events, comments, messages, participations…) created from this identity.": "Esto eliminará / anonimazará todo o contido (eventos, comentarios, mensaxes, participacións...) creado con esta identidade.",
"Time in your timezone ({timezone})": "",
"Times in your timezone ({timezone})": "",
"Time in your timezone ({timezone})": "Hora na túa zona horaria ({timezone})",
"Times in your timezone ({timezone})": "Horas na túa zona horaria ({timezone})",
"Timezone": "Zona horaria",
"Timezone detected as {timezone}.": "Zona horaria detectada como {timezone}.",
"Title": "Título",
@ -973,24 +977,24 @@
"Unable to load event for participation. The error details are provided below:": "Non se puido cargar a participación no evento. Aquí abaixo tes detalles do erro:",
"Unable to save your participation in this browser.": "Non se puido gardar a túa participación neste navegador.",
"Unable to update the profile. The avatar picture may be too heavy.": "Non se puido actualizar o perfil. A imaxe do avatar podería ser demasiado grande.",
"Underline": "",
"Undo": "",
"Underline": "Subliñar",
"Undo": "Desfacer",
"Unfollow": "Deixar de seguir",
"Unfortunately, your participation request was rejected by the organizers.": "Desgraciadamente a túa solicitude de participación foi rexeitada pola organización.",
"Unknown": "Descoñecido",
"Unknown actor": "Actor descoñecido",
"Unknown error.": "Erro descoñecido.",
"Unknown value for the openness setting.": "Axuste descoñecido para o acceso ao grupo.",
"Unlogged participation": "",
"Unlogged participation": "Participación sen rexistrarse",
"Unsaved changes": "Cambios non gardados",
"Unsubscribe to browser push notifications": "Retirar subscrición ás notificacións push do navegdor",
"Unsuspend": "Reactivar",
"Upcoming": "Próximamente",
"Upcoming events": "Eventos próximos",
"Upcoming events from your groups": "",
"Upcoming events from your groups": "Eventos previstos para os teus grupos",
"Update": "Actualizar",
"Update app": "Actualizar app",
"Update discussion title": "",
"Update discussion title": "Actualiza o título da conversa",
"Update event {name}": "Actualizar evento {name}",
"Update group": "Actualizar grupo",
"Update my event": "Actualizar o meu evento",
@ -1002,7 +1006,7 @@
"User settings": "Axustes da usuaria",
"Username": "Identificador",
"Users": "Usuarias",
"Validating account": "",
"Validating account": "Validando a conta",
"Validating email": "Validando o correo",
"Video Conference": "Videoconferencia",
"View a reply": "|Ver unha resposta|Ver {totalReplies} respostas",
@ -1012,7 +1016,7 @@
"View all posts": "Ver publicacións",
"View event page": "Ver páxina do evento",
"View everything": "Velo todo",
"View full profile": "",
"View full profile": "Ver perfil completo",
"View less": "Ver menos",
"View more": "Ver máis",
"View page on {hostname} (in a new window)": "Ver páxina en {hostname} (nova ventá)",
@ -1040,12 +1044,13 @@
"What can I do to help?": "Que podo facer para axudar?",
"Wheelchair accessibility": "Accesible con cadeira de rodas",
"When a moderator from the group creates an event and attributes it to the group, it will show up here.": "Aquí aparecerá un evento cando un moderador do grupo o cree e o atribúa ó grupo.",
"When the event is private, you'll need to share the link around.": "",
"When the post is private, you'll need to share the link around.": "",
"When the event is private, you'll need to share the link around.": "Se o evento é privado, deberás compartir ti a ligazón.",
"When the post is private, you'll need to share the link around.": "Se a publicación é privada, deberás compartir ti a ligazón.",
"Whether smoking is prohibited during the event": "Se está prohibido fumar durante o evento",
"Whether the event is accessible with a wheelchair": "Se o evento é accesible ou non con cadeira de rodas",
"Whether the event is interpreted in sign language": "Se o evento está interpretado en lingua de signos",
"Whether the event live video is subtitled": "Se o evento está subtitulado ou non",
"Who can post a comment?": "",
"Who can post a comment?": "Quen pode comentar?",
"Who can view this event and participate": "Quen pode ver e participar neste evento",
"Who can view this post": "Quen pode ver esta publicación",
"Who published {number} events": "Que publicaron {number} eventos",
@ -1055,7 +1060,7 @@
"Yesterday": "Onte",
"You accepted the invitation to join the group.": "Aceptaches o convite para unirte ao grupo.",
"You added the member {member}.": "Engadiches a {member}.",
"You approved {member}'s membership.": "",
"You approved {member}'s membership.": "Aprobaches a membresía de {member}.",
"You archived the discussion {discussion}.": "Arquivaches o debate {discussion}.",
"You are not an administrator for this group.": "Non es administradora deste grupo.",
"You are not part of any group.": "Non formas parte de ningún grupo.",
@ -1084,9 +1089,9 @@
"You demoted {member} to simple member.": "Degradaches a {member] a membresía básica.",
"You didn't create or join any event yet.": "Aínda non te uniches nin creaches un evento.",
"You don't follow any instances yet.": "Aínda non segues ningunha instancia.",
"You don't have any upcoming events. Maybe try another filter?": "",
"You don't have any upcoming events. Maybe try another filter?": "Non tes eventos previstos. Queres probar con outro filtro?",
"You excluded member {member}.": "Excluíches a {member}.",
"You have attended {count} events in the past.": "",
"You have attended {count} events in the past.": "Non participaches en ningún evento no pasado.|Participaches nun evento no pasado.|Participaches en {count} eventos no pasado.",
"You have been invited by {invitedBy} to the following group:": "Foches convidada por {invitedBy} ó seguinte grupo:",
"You have been removed from this group's members.": "Sacáronte deste grupo.",
"You have cancelled your participation": "Cancelaches a túa participación",
@ -1126,7 +1131,7 @@
"You will be able to add an avatar and set other options in your account settings.": "Poderás engadir un avatar e establecer outras opcións nos axustes da conta.",
"You will be redirected to the original instance": "Vas ser redirixida á instancia orixinal",
"You will find here all the events you have created or of which you are a participant, as well as events organized by groups you follow or are a member of.": "Aquí atoparás tódolos eventos que creaches ou dos que es partícipe, tamén os eventos organizados por grupos que segues ou aos que pertences.",
"You will receive notifications about this group's public activity depending on %{notification_settings}.": "",
"You will receive notifications about this group's public activity depending on %{notification_settings}.": "Vas recibir notificacións sobre a actividade pública deste grupo dependendo dos %{notification_settings}.",
"You wish to participate to the following event": "Queres participar no seguinte evento",
"You'll get a weekly recap every Monday for upcoming events, if you have any.": "Recibirás un resumen semanal cada Luns para os próximos eventos, se os tes.",
"You'll need to change the URLs where there were previously entered.": "Precisarás cambiar os URLs alá onde fosen previamente utilizados.",
@ -1146,7 +1151,7 @@
"Your email is being changed": "Estase cambiando o teu correo",
"Your email will only be used to confirm that you're a real person and send you eventual updates for this event. It will NOT be transmitted to other instances or to the event organizer.": "O teu correo só se utilizará para confirmar que es unha persoa real e enviarche posibles actualizacións para o evento. NON será transferido a outras instancias ou á organización do evento.",
"Your federated identity": "A túa identidade federada",
"Your membership was approved by {profile}.": "",
"Your membership was approved by {profile}.": "{profile} aprobou a túa membresía.",
"Your participation has been confirmed": "A túa participación foi confirmada",
"Your participation has been rejected": "A túa participación foi rexeitada",
"Your participation has been requested": "A túa participación foi solicitada",
@ -1157,7 +1162,7 @@
"Your participation still has to be approved by the organisers.": "A participación aínda debe ser aprobada pola organización.",
"Your participation will be validated once you click the confirmation link into the email, and after the organizer manually validates your participation.": "A túa participación validarase unha vez premas na ligazón de confirmación do correo, e após a organización validará manualmente a participación.",
"Your participation will be validated once you click the confirmation link into the email.": "A túa participación será validada unha vez premas na ligazón de confirmación do correo.",
"Your position was not available.": "",
"Your position was not available.": "A túa posición non estaba dispoñible.",
"Your profile will be shown as contact.": "O teu perfil será mostrado como contacto.",
"Your timezone is currently set to {timezone}.": "Zona horaria actual establecida a {timezone}.",
"Your timezone was detected as {timezone}.": "Zona horaria detectada como {timezone}.",
@ -1170,7 +1175,7 @@
"[This comment has been deleted]": "[Este comentario foi eliminado]",
"[deleted]": "[eliminado]",
"a non-existent report": "denuncia non existente",
"access to the group's private content as well": "",
"access to the group's private content as well": "accede tamén ao contido privado do grupo",
"and {number} groups": "e {number} grupos",
"any distance": "calquera distancia",
"as {identity}": "como {identity}",
@ -1185,7 +1190,7 @@
"explore the events": "descubre os eventos",
"explore the groups": "atopar grupos",
"full rules": "normas completas",
"group's upcoming public events": "",
"group's upcoming public events": "Próximos eventos públicos do grupo",
"https://mensuel.framapad.org/p/some-secret-token": "https://mensuel.framapad.org/p/some-secret-token",
"iCal Feed": "Fonte iCal",
"instance rules": "normas da instancia",
@ -1198,21 +1203,21 @@
"terms of service": "termos do servizo",
"tool designed to serve you": "ferramenta creada para servirte",
"with another identity…": "con outra identidade…",
"your notification settings": "",
"your notification settings": "axustes das túas notificacións",
"{'@'}{username}": "{'@'}{username}",
"{approved} / {total} seats": "{approved} / {total} prazas",
"{available}/{capacity} available places": "Non quedan prazas|{available}/{capacity} prazas dispoñibles",
"{count} km": "{count} km",
"{count} members": "Sen membros|Un membro|{count} membros",
"{count} members or followers": "",
"{count} members or followers": "Sen membros ou seguidoras|Un membro ou seguidora|{count} membros ou seguidoras",
"{count} participants": "Sen participantes | Un participante | {count} participantes",
"{count} requests waiting": "{count} solicitudes agardando",
"{folder} - Resources": "{folder} - Recursos",
"{group} activity timeline": "Cronoloxía de actividade de {group}",
"{group} events": "Eventos de {group}",
"{group} posts": "",
"{group} posts": "{group} publicacións",
"{group}'s events": "Eventos de {group}",
"{group}'s todolists": "",
"{group}'s todolists": "Tarefas pendentes de {group}",
"{instanceName} is an instance of the {mobilizon} software.": "{instanceName} é unha instancia do software {mobilizon}.",
"{instanceName} is an instance of {mobilizon_link}, a free software built with the community.": "{instanceName} é unha instancia de {mobilizon_link}, software libre feito para a comunidade.",
"{member} accepted the invitation to join the group.": "{member} aceptou o convite para unirse ao grupo.",
@ -1239,11 +1244,11 @@
"{number} organized events": "Sen eventos organizados|Un evento organizado|{number} eventos organizados",
"{number} participations": "Sen participacións|Unha participación|{number} participacións",
"{number} posts": "Sen publicacións|Unha publicación|{number} publicacións",
"{number} seats left": "",
"{number} seats left": "Quedan {number} asentos",
"{old_group_name} was renamed to {group}.": "{old_group_name} mudou de nome a {group}.",
"{profile} (by default)": "{profile} (by default)",
"{profile} added the member {member}.": "{profile} engadiu a {member}.",
"{profile} approved {member}'s membership.": "",
"{profile} approved {member}'s membership.": "{profile} aprobou a membresía de {member}.",
"{profile} archived the discussion {discussion}.": "{profile} arquivou o debate {discussion}.",
"{profile} created the discussion {discussion}.": "{profile} creou o debate {discussion}.",
"{profile} created the folder {resource}.": "{profile} creou o cartafol {resource}.",
@ -1273,7 +1278,7 @@
"{profile} replied to the discussion {discussion}.": "{profile} respondeu ao debate {discussion}.",
"{profile} updated the group {group}.": "{profile} actualizou o grupo {group}.",
"{profile} updated the member {member}.": "{profile} actualizou a participante {member].",
"{timezoneLongName} ({timezoneShortName})": "",
"{timezoneLongName} ({timezoneShortName})": "{timezoneLongName} ({timezoneShortName})",
"{title} ({count} todos)": "{title} ({count} pendentes)",
"{username} was invited to {group}": "{username} foi convidada a {group}",
"© The OpenStreetMap Contributors": "© The OpenStreetMap Contributors"

1
src/i18n/th.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -18,7 +18,7 @@ import { notifierPlugin } from "./plugins/notifier";
import FloatingVue from "floating-vue";
import "floating-vue/dist/style.css";
import Oruga from "@oruga-ui/oruga-next";
import "@oruga-ui/oruga-next/dist/oruga.css";
import "@oruga-ui/theme-oruga/dist/oruga.css";
import "./assets/oruga-tailwindcss.css";
import { orugaConfig } from "./oruga-config";
import MaterialIcon from "./components/core/MaterialIcon.vue";

View file

@ -22,22 +22,24 @@ export const orugaConfig = {
roundedClass: "rounded",
variantClass: "input-",
iconRightClass: "input-icon-right",
sizeClass: "input-size-",
},
inputitems: {
itemClass: "inputitems-item",
containerClass: "rounded",
taginput: {
itemClass: "taginput-item",
rootClass: "taginput",
},
autocomplete: {
menuClass: "autocomplete-menu",
rootClass: "autocomplete",
itemClass: "autocomplete-item",
itemGroupTitleClass: "autocomplete-item-group-title",
},
icon: {
variantClass: "icon-",
},
checkbox: {
rootClass: "checkbox",
checkClass: "checkbox-check",
checkCheckedClass: "checkbox-checked",
inputClass: "checkbox-check",
inputCheckedClass: "checkbox-checked",
labelClass: "checkbox-label",
},
dropdown: {
@ -62,14 +64,15 @@ export const orugaConfig = {
},
switch: {
labelClass: "switch-label",
checkCheckedClass: "switch-check-checked",
switchCheckedClass: "switch-check-checked",
},
select: {
selectClass: "select",
},
radio: {
checkCheckedClass: "radio-checked",
checkClass: "form-radio",
rootClass: "radio",
inputCheckedClass: "radio-checked",
inputClass: "form-radio",
labelClass: "radio-label",
},
notification: {
@ -90,8 +93,8 @@ export const orugaConfig = {
linkClass: "pagination-link",
linkCurrentClass: "pagination-link-current",
linkDisabledClass: "pagination-link-disabled",
nextBtnClass: "pagination-next",
prevBtnClass: "pagination-previous",
nextButtonClass: "pagination-next",
prevButtonClass: "pagination-previous",
ellipsisClass: "pagination-ellipsis",
},
tabs: {
@ -99,7 +102,7 @@ export const orugaConfig = {
navTabsClass: "tabs-nav",
navTypeClass: "tabs-nav-",
navSizeClass: "tabs-nav-",
tabItemWrapperClass: "tabs-nav-item-wrapper",
itemWrapperClass: "tabs-nav-item-wrapper",
itemHeaderTypeClass: "tabs-nav-item-",
itemHeaderActiveClass: "tabs-nav-item-active-",
},

View file

@ -26,7 +26,6 @@ export class Notifier {
duration: 5000,
position: "bottom-right",
type,
hasIcon: true,
});
}
}

View file

@ -3,6 +3,10 @@ import { InstanceFollowStatus } from "./enums";
export interface IInstance {
domain: string;
hasRelay: boolean;
instanceName: string | null;
instanceDescription: string | null;
software: string | null;
softwareVersion: string | null;
relayAddress: string | null;
followerStatus: InstanceFollowStatus;
followedStatus: InstanceFollowStatus;

View file

@ -28,6 +28,7 @@
@input="(event: any) => updateUsername(event.target.value)"
id="identity-display-name"
dir="auto"
expanded
/>
</o-field>
@ -69,6 +70,7 @@
aria-required="false"
v-model="identity.summary"
id="identity-summary"
expanded
/>
</o-field>
@ -118,10 +120,9 @@
<o-tooltip
:label="t('URL copied to clipboard')"
:active="showCopiedTooltip.atom"
always
variant="success"
position="left"
>
/>
<o-button
tag="a"
icon-left="rss"
@ -133,14 +134,12 @@
target="_blank"
>{{ t("RSS/Atom Feed") }}</o-button
>
</o-tooltip>
<o-tooltip
:label="t('URL copied to clipboard')"
:active="showCopiedTooltip.ics"
always
variant="success"
position="left"
>
/>
<o-button
tag="a"
@click="
@ -152,7 +151,6 @@
target="_blank"
>{{ t("ICS/WebCal Feed") }}</o-button
>
</o-tooltip>
<o-button
icon-left="refresh"
variant="text"

View file

@ -2,12 +2,50 @@
<div v-if="instance">
<breadcrumbs-nav
:links="[
{ name: RouteName.ADMIN, text: $t('Admin') },
{ name: RouteName.INSTANCES, text: $t('Instances') },
{ name: RouteName.ADMIN, text: t('Admin') },
{ name: RouteName.INSTANCES, text: t('Instances') },
{ text: instance.domain },
]"
/>
<h1 class="text-2xl">{{ instance.domain }}</h1>
<section
class="flex flex-wrap md:flex-nowrap items-center justify-between gap-4"
>
<div>
<h2 class="text-4xl font-bold" v-if="instance.instanceName">
{{ instance.instanceName }}
</h2>
<h2 class="text-4xl font-bold" v-else>{{ instance.domain }}</h2>
<p
v-if="instance.instanceDescription"
class="text-slate-700 dark:text-slate-400 my-4"
>
{{ instance.instanceDescription }}
</p>
<i18n-t
v-if="instance.software && instance.softwareVersion"
keypath="Software details: {software_details}"
class="my-4"
>
<template #software_details>
<span class="capitalize">
{{ instance.software }} - {{ instance.softwareVersion }}
</span>
</template>
</i18n-t>
</div>
<o-button
tag="a"
:href="`https://${instance.domain}`"
target="_blank"
rel="noopener noreferrer"
icon-right="open-in-new"
class="mx-auto md:mx-0"
>{{
t("Visit {instance_domain}", { instance_domain: instance.domain })
}}
</o-button>
</section>
<section>
<div
class="grid md:grid-cols-2 xl:grid-cols-4 gap-2 content-center text-center mt-2"
>
@ -21,7 +59,7 @@
<span class="mb-4 text-xl font-semibold block">{{
instance.personCount
}}</span>
<span class="text-sm block">{{ $t("Profiles") }}</span>
<span class="text-sm block">{{ t("Profiles") }}</span>
</router-link>
</div>
<div class="bg-gray-50 dark:bg-mbz-purple-500 rounded-xl p-8">
@ -34,42 +72,52 @@
<span class="mb-4 text-xl font-semibold block">{{
instance.groupCount
}}</span>
<span class="text-sm block">{{ $t("Groups") }}</span>
<span class="text-sm block">{{ t("Groups") }}</span>
</router-link>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<span class="mb-4 text-xl font-semibold block">{{
instance.followingsCount
}}</span>
<span class="text-sm block">{{ $t("Followings") }}</span>
<span class="text-sm block">{{ t("Followings") }}</span>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<span class="mb-4 text-xl font-semibold block">{{
instance.followersCount
}}</span>
<span class="text-sm block">{{ $t("Followers") }}</span>
<span class="text-sm block">{{ t("Followers") }}</span>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<router-link
:to="{ name: RouteName.REPORTS, query: { domain: instance.domain } }"
:to="{
name: RouteName.REPORTS,
query: { domain: instance.domain },
}"
>
<span class="mb-4 text-xl font-semibold block">{{
instance.reportsCount
}}</span>
<span class="text-sm block">{{ $t("Reports") }}</span>
<span class="text-sm block">{{ t("Reports") }}</span>
</router-link>
</div>
<div class="bg-zinc-50 dark:bg-mbz-purple-500 rounded-xl p-8">
<span class="mb-4 font-semibold block">{{
formatBytes(instance.mediaSize)
}}</span>
<span class="text-sm block">{{ $t("Uploaded media size") }}</span>
<span class="text-sm block">{{ t("Uploaded media size") }}</span>
</div>
</div>
</section>
<section>
<div class="mt-3 grid xl:grid-cols-2 gap-4">
<div
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md"
v-if="instance.hasRelay"
v-if="
instance.hasRelay &&
!['mastodon', 'peertube'].includes(
instance.software?.toLowerCase() ?? ''
)
"
>
<button
@click="
@ -80,7 +128,7 @@
v-if="instance.followedStatus == InstanceFollowStatus.APPROVED"
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Stop following instance") }}
{{ t("Stop following instance") }}
</button>
<button
@click="
@ -91,18 +139,18 @@
v-else-if="instance.followedStatus == InstanceFollowStatus.PENDING"
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Cancel follow request") }}
{{ t("Cancel follow request") }}
</button>
<button
@click="followInstance"
v-else
class="bg-primary hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Follow instance") }}
{{ t("Follow instance") }}
</button>
</div>
<div v-else class="md:h-48 py-16 text-center opacity-50">
{{ $t("Only Mobilizon instances can be followed") }}
{{ t("Only instances with an application actor can be followed") }}
</div>
<div
class="border bg-white dark:bg-mbz-purple-500 dark:border-mbz-purple-700 p-6 shadow-md rounded-md flex flex-col gap-2"
@ -116,7 +164,7 @@
v-if="instance.followerStatus == InstanceFollowStatus.PENDING"
class="bg-green-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Accept follow") }}
{{ t("Accept follow") }}
</button>
<button
@click="
@ -127,13 +175,14 @@
v-if="instance.followerStatus != InstanceFollowStatus.NONE"
class="bg-red-700 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2 focus:ring-offset-gray-50 text-white hover:text-white font-semibold h-12 px-6 rounded-lg w-full flex items-center justify-center sm:w-auto"
>
{{ $t("Reject follow") }}
{{ t("Reject follow") }}
</button>
<p v-if="instance.followerStatus == InstanceFollowStatus.NONE">
{{ $t("This instance doesn't follow yours.") }}
{{ t("This instance doesn't follow yours.") }}
</p>
</div>
</div>
</section>
</div>
</template>
<script lang="ts" setup>
@ -152,6 +201,7 @@ import { InstanceFollowStatus } from "@/types/enums";
import { useMutation, useQuery } from "@vue/apollo-composable";
import { computed, inject } from "vue";
import { Notifier } from "@/plugins/notifier";
import { useI18n } from "vue-i18n";
const props = defineProps<{ domain: string }>();
@ -164,6 +214,8 @@ const instance = computed(() => instanceResult.value?.instance);
const notifier = inject<Notifier>("notifier");
const { t } = useI18n({ useScope: "global" });
const { mutate: acceptInstance, onError: onAcceptInstanceError } = useMutation(
ACCEPT_RELAY,
() => ({

View file

@ -72,21 +72,65 @@
name: RouteName.INSTANCE,
params: { domain: instance.domain },
}"
class="flex items-center mb-2 rounded bg-mbz-yellow-alt-300 dark:bg-mbz-purple-400 p-4 flex-wrap justify-center gap-x-2 gap-y-3"
class="flex items-center mb-2 rounded bg-mbz-yellow-alt-300 hover:bg-mbz-yellow-alt-200 dark:bg-mbz-purple-600 dark:hover:bg-mbz-purple-700 p-4 flex-wrap justify-center gap-x-2 gap-y-3"
v-for="instance in instances.elements"
:key="instance.domain"
>
<div class="grow overflow-hidden flex items-center gap-1">
<img
class="w-12"
v-if="instance.hasRelay"
v-if="instance.software === 'Mobilizon'"
src="/img/logo.svg"
alt=""
/>
<CloudQuestion v-else :size="36" />
<mastodon-logo
class="w-8 mx-2"
alt=""
v-else-if="instance.software?.toLowerCase() === 'mastodon'"
/>
<img
class="w-8 mx-2"
v-else-if="instance.software?.toLowerCase() === 'gancio'"
src="/img/gancio.png"
alt=""
/>
<img
class="w-8 mx-2"
v-else-if="instance.software?.toLowerCase() === 'wordpress'"
src="/img/wordpress-logo.svg"
alt=""
/>
<CloudQuestion class="mx-1.5" v-else :size="36" />
<div class="">
<h3 class="text-lg truncate">{{ instance.domain }}</h3>
<h3
class="text-lg truncate font-bold text-slate-800 dark:text-slate-100"
v-if="instance.instanceName"
>
{{ instance.instanceName }}
</h3>
<h3
class="text-lg truncate font-bold text-slate-800 dark:text-slate-100"
v-else
>
{{ instance.domain }}
</h3>
<p
v-if="instance.instanceName"
class="inline-flex gap-2 text-slate-700 dark:text-slate-300"
>
<span class="capitalize" v-if="instance.software">{{
instance.software
}}</span>
-
<span>{{ instance.domain }}</span>
</p>
<p
v-else-if="instance.software"
class="capitalize text-slate-700 dark:text-slate-300"
>
{{ instance.software }}
</p>
<span
class="text-sm"
v-if="instance.followedStatus === InstanceFollowStatus.APPROVED"
@ -186,6 +230,7 @@ import { useRouter } from "vue-router";
import { useHead } from "@unhead/vue";
import CloudQuestion from "../../../node_modules/vue-material-design-icons/CloudQuestion.vue";
import { Notifier } from "@/plugins/notifier";
import MastodonLogo from "@/components/Share/MastodonLogo.vue";
const INSTANCES_PAGE_LIMIT = 10;

View file

@ -10,7 +10,11 @@
<section v-if="settingsToWrite">
<form @submit.prevent="updateSettings">
<o-field :label="t('Instance Name')" label-for="instance-name">
<o-input v-model="settingsToWrite.instanceName" id="instance-name" />
<o-input
v-model="settingsToWrite.instanceName"
id="instance-name"
expanded
/>
</o-field>
<div class="field flex flex-col">
<label class="" for="instance-description">{{
@ -74,7 +78,7 @@
<small>
{{ t("Main languages you/your moderators speak") }}
</small>
<o-inputitems
<o-taginput
v-model="instanceLanguages"
:data="filteredLanguages"
allow-autocomplete
@ -82,11 +86,11 @@
field="name"
icon="label"
:placeholder="t('Select languages')"
@typing="getFilteredLanguages"
@input="getFilteredLanguages"
id="instance-languages"
>
<template #empty>{{ t("No languages found") }}</template>
</o-inputitems>
</o-taginput>
</div>
<div class="field flex flex-col">
<label class="" for="instance-long-description">{{

View file

@ -56,7 +56,7 @@ import ConversationListItem from "../../components/Conversations/ConversationLis
import EmptyContent from "../../components/Utils/EmptyContent.vue";
import { useHead } from "@unhead/vue";
import { IPerson } from "@/types/actor";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
import { arrayTransformer } from "@/utils/route";
const page = useRouteQuery("page", 1, integerTransformer);
@ -89,14 +89,14 @@ const conversations = computed(
}
);
const { oruga } = useProgrammatic();
const { modal } = useOruga();
const NewConversation = defineAsyncComponent(
() => import("@/components/Conversations/NewConversation.vue")
);
const openNewMessageModal = () => {
oruga.modal.open({
modal.open({
component: NewConversation,
props: {
personMentions: personMentions.value,

View file

@ -36,6 +36,7 @@
<o-input
aria-required="true"
required
expanded
v-model="discussion.title"
id="discussion-title"
/>

View file

@ -65,6 +65,7 @@
<o-input
:value="discussion.title"
v-model="newTitle"
expanded
id="discussion-title"
/>
</o-field>

View file

@ -31,6 +31,7 @@
v-model="event.title"
id="title"
dir="auto"
expanded
/>
</o-field>
@ -72,7 +73,6 @@
:locale="$i18n.locale.replace('_', '-')"
v-model="beginsOn"
horizontal-time-picker
editable
:tz-offset="tzOffset(beginsOn)"
:first-day-of-week="firstDayOfWeek"
:datepicker="{
@ -99,7 +99,6 @@
horizontal-time-picker
:min-datetime="beginsOn"
:tz-offset="tzOffset(endsOn)"
editable
:first-day-of-week="firstDayOfWeek"
:datepicker="{
id: 'ends-on-field',
@ -145,6 +144,7 @@
v-model="event.onlineAddress"
placeholder="URL"
id="website-url"
expanded
/>
</o-field>
@ -373,44 +373,55 @@
<o-radio
v-model="event.status"
name="status"
class="mr-2 p-2 rounded border"
:class="{
'btn-warning': event.status === EventStatus.TENTATIVE,
'btn-outlined-warning': event.status !== EventStatus.TENTATIVE,
}"
variant="warning"
:native-value="EventStatus.TENTATIVE"
>
<div
class="mr-2 p-2 rounded border flex gap-x-1"
:class="{
'btn-warning': event.status === EventStatus.TENTATIVE,
'btn-outlined-warning':
event.status !== EventStatus.TENTATIVE,
}"
>
<o-icon icon="calendar-question" />
{{ t("Tentative: Will be confirmed later") }}
</div>
</o-radio>
<o-radio
v-model="event.status"
name="status"
variant="success"
class="mr-2 p-2 rounded border"
:native-value="EventStatus.CONFIRMED"
>
<div
class="mr-2 p-2 rounded border flex gap-x-1"
:class="{
'btn-success': event.status === EventStatus.CONFIRMED,
'btn-outlined-success': event.status !== EventStatus.CONFIRMED,
'btn-outlined-success':
event.status !== EventStatus.CONFIRMED,
}"
:native-value="EventStatus.CONFIRMED"
>
<o-icon icon="calendar-check" />
{{ t("Confirmed: Will happen") }}
</div>
</o-radio>
<o-radio
v-model="event.status"
name="status"
class="p-2 rounded border"
variant="danger"
:native-value="EventStatus.CANCELLED"
>
<div
class="p-2 rounded border flex gap-x-1"
:class="{
'btn-danger': event.status === EventStatus.CANCELLED,
'btn-outlined-danger': event.status !== EventStatus.CANCELLED,
}"
variant="danger"
:native-value="EventStatus.CANCELLED"
>
<o-icon icon="calendar-remove" />
{{ t("Cancelled: Won't happen") }}
</div>
</o-radio>
</o-field>
</fieldset>
@ -625,7 +636,7 @@ import { useMutation } from "@vue/apollo-composable";
import { Dialog } from "@/plugins/dialog";
import { Notifier } from "@/plugins/notifier";
import { useHead } from "@unhead/vue";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
import type { Locale } from "date-fns";
import sortBy from "lodash/sortBy";
import { escapeHtml } from "@/utils/html";
@ -806,10 +817,10 @@ const {
postRefetchQueries(updatedData?.createEvent),
}));
const { oruga } = useProgrammatic();
const { notification } = useOruga();
onCreateEventMutationDone(async ({ data }) => {
oruga.notification.open({
notification.open({
message: (event.value.draft
? t("The event has been created as a draft")
: t("The event has been published")) as string,
@ -852,7 +863,7 @@ const {
}));
onEditEventMutationDone(() => {
oruga.notification.open({
notification.open({
message: updateEventMessage.value,
variant: "success",
position: "bottom-right",

View file

@ -15,6 +15,7 @@
<o-input
aria-required="true"
required
expanded
v-model="group.name"
id="group-display-name"
/>

View file

@ -27,7 +27,11 @@
>
<form @submit.prevent="updateGroup(buildVariables)" v-if="editableGroup">
<o-field :label="t('Group name')" label-for="group-settings-name">
<o-input v-model="editableGroup.name" id="group-settings-name" />
<o-input
v-model="editableGroup.name"
id="group-settings-name"
expanded
/>
</o-field>
<o-field :label="t('Group short description')">
<Editor
@ -83,16 +87,15 @@
)
}}</small>
</o-radio>
<p class="pl-6">
<p class="pl-6 flex items-center gap-2">
<code>{{ group.url }}</code>
<o-tooltip
v-if="canShowCopyButton"
:label="t('URL copied to clipboard')"
:active="showCopiedTooltip"
always
variant="success"
position="left"
>
/>
<o-button
variant="primary"
icon-right="content-paste"
@ -100,7 +103,6 @@
@click="copyURL"
@keyup.enter="copyURL"
/>
</o-tooltip>
</p>
</div>

View file

@ -668,7 +668,7 @@ import EventMinimalistCard from "@/components/Event/EventMinimalistCard.vue";
import MultiPostListItem from "@/components/Post/MultiPostListItem.vue";
import { Address, addressFullName } from "@/types/address.model";
import InvitationsList from "@/components/Group/InvitationsList.vue";
import addMinutes from "date-fns/addMinutes";
import { addMinutes } from "date-fns";
import { JOIN_GROUP } from "@/graphql/member";
import { MemberRole, Openness, PostVisibility } from "@/types/enums";
import { IMember } from "@/types/actor/member.model";

View file

@ -387,6 +387,7 @@
<o-input
type="textarea"
v-model="noteContent"
expanded
id="newNoteInput"
></o-input>
</o-field>

View file

@ -29,6 +29,7 @@
v-model="editablePost.title"
id="post-title"
dir="auto"
expanded
/>
</o-field>

View file

@ -100,7 +100,7 @@
</div>
<o-dropdown position="bottom-left" aria-role="list">
<template #trigger>
<o-button role="button" icon-right="dots-horizontal">
<o-button icon-right="dots-horizontal">
{{ t("Actions") }}
</o-button>
</template>

View file

@ -71,6 +71,7 @@
ref="resourceRenameInput"
aria-required="true"
v-model="updatedResource.title"
expanded
/>
</o-field>
@ -133,6 +134,7 @@
aria-required="true"
v-model="newResource.title"
id="new-resource-title"
expanded
/>
</o-field>
@ -167,6 +169,7 @@
id="new-resource-url"
type="url"
required
expanded
v-model="newResource.resourceUrl"
@blur="previewResource"
ref="modalNewResourceLinkInput"
@ -187,6 +190,7 @@
aria-required="true"
v-model="newResource.title"
id="new-resource-link-title"
expanded
/>
</o-field>
@ -200,6 +204,7 @@
type="textarea"
v-model="newResource.summary"
id="new-resource-summary"
expanded
/>
</o-field>

View file

@ -60,6 +60,7 @@
type="email"
id="account-email"
v-model="newEmail"
expanded
/>
</o-field>
<p class="help">{{ t("You'll receive a confirmation email.") }}</p>
@ -72,6 +73,7 @@
password-reveal
minlength="6"
v-model="passwordForEmailChange"
expanded
/>
</o-field>
<o-button class="mt-2" variant="primary" nativeType="submit">
@ -117,6 +119,7 @@
minlength="6"
id="account-old-password"
v-model="oldPassword"
expanded
/>
</o-field>
<o-field :label="t('New password')" label-for="account-new-password">
@ -128,6 +131,7 @@
minlength="6"
id="account-new-password"
v-model="newPassword"
expanded
/>
</o-field>
<o-button class="mt-2" variant="primary" nativeType="submit">
@ -243,7 +247,7 @@ import {
} from "../../graphql/user";
import RouteName from "../../router/name";
import { logout, SELECTED_PROVIDERS } from "../../utils/auth";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { useOruga } from "@oruga-ui/oruga-next";
const { t } = useI18n({ useScope: "global" });
@ -341,12 +345,12 @@ const {
DELETE_ACCOUNT
);
const { oruga } = useProgrammatic();
const { notification } = useOruga();
deleteAccountMutationDone(async () => {
console.debug("Deleted account, logging out client...");
await logout(false);
oruga.notification.open({
notification.open({
message: t("Your account has been successfully deleted"),
variant: "success",
position: "bottom-right",

View file

@ -240,10 +240,9 @@
<o-tooltip
:label="$t('URL copied to clipboard')"
:active="showCopiedTooltip.atom"
always
variant="success"
position="left"
>
/>
<o-button
tag="a"
icon-left="rss"
@ -259,14 +258,13 @@
target="_blank"
>{{ $t("RSS/Atom Feed") }}</o-button
>
</o-tooltip>
<o-tooltip
:label="$t('URL copied to clipboard')"
:active="showCopiedTooltip.ics"
always
variant="success"
position="left"
>
/>
<o-button
tag="a"
@click="
@ -282,7 +280,6 @@
target="_blank"
>{{ $t("ICS/WebCal Feed") }}</o-button
>
</o-tooltip>
<o-button
icon-left="refresh"
variant="text"

View file

@ -52,6 +52,7 @@
<o-input
aria-required="true"
required
expanded
id="email"
type="email"
v-model="credentials.email"
@ -63,6 +64,7 @@
aria-required="true"
id="password"
required
expanded
type="password"
password-reveal
v-model="credentials.password"

View file

@ -19,6 +19,7 @@
password-reveal
minlength="6"
v-model="credentials.password"
expanded
/>
</o-field>
<o-field :label="$t('Password (confirmation)')">
@ -29,6 +30,7 @@
password-reveal
minlength="6"
v-model="credentials.passwordConfirmation"
expanded
/>
</o-field>
<button class="button is-primary">

View file

@ -103,6 +103,7 @@
v-model="credentials.email"
@blur="showGravatar = true"
@focus="showGravatar = false"
expanded
/>
</o-field>
@ -120,6 +121,7 @@
password-reveal
minlength="6"
v-model="credentials.password"
expanded
/>
</o-field>

View file

@ -14,6 +14,7 @@
type="email"
id="emailAddress"
v-model="emailValue"
expanded
/>
</o-field>
<p class="flex flex-wrap gap-1 mt-2">

View file

@ -0,0 +1,74 @@
defmodule Mobilizon.Federation.ActivityStream.Converter.AddressTest do
use Mobilizon.DataCase
alias Mobilizon.Addresses.Address
alias Mobilizon.Federation.ActivityStream.Converter.Address, as: AddressConverter
describe "address to AS" do
test "valid address to as" do
data =
AddressConverter.model_to_as(%Address{
country: "France",
locality: "Lyon",
description: "Locaux'Motiv",
postal_code: "69007",
street: "10Bis Rue Jangot"
})
assert is_map(data)
assert data["type"] == "Place"
assert data["name"] == "Locaux'Motiv"
assert data["address"]["type"] == "PostalAddress"
assert data["address"]["addressLocality"] == "Lyon"
assert data["address"]["postalCode"] == "69007"
assert data["address"]["addressCountry"] == "France"
assert data["address"]["streetAddress"] == "10Bis Rue Jangot"
end
end
describe "AS to Address" do
test "basic AS data to model" do
address =
AddressConverter.as_to_model_data(%{
"type" => "Place",
"name" => "Federazione Anarchica Torinese",
"address" => "corso Palermo 46",
"latitude" => nil,
"longitude" => nil
})
assert address["description"] == "Federazione Anarchica Torinese"
assert address["street"] == "corso Palermo 46"
assert address["locality"] == nil
assert address["geom"] == nil
end
test "AS data with PostalAddress to model" do
address =
AddressConverter.as_to_model_data(%{
"type" => "Place",
"name" => "Federazione Anarchica Torinese",
"address" => %{
"streetAddress" => "Corso Palermo, 46",
"addressCountry" => "Italia",
"postalCode" => "10152",
"addressLocality" => "Torino"
},
"latitude" => 45.08281,
"longitude" => 7.69311
})
assert address["description"] == "Federazione Anarchica Torinese"
assert address["street"] == "Corso Palermo, 46"
assert address["locality"] == "Torino"
assert address["postal_code"] == "10152"
assert address["country"] == "Italia"
assert address["geom"] == %Geo.Point{
coordinates: {7.69311, 45.08281},
srid: 4326
}
end
end
end

View file

@ -0,0 +1,188 @@
defmodule Mobilizon.Federation.NodeInfoTest do
use Mobilizon.DataCase
import Mox
alias Mobilizon.Federation.NodeInfo
alias Mobilizon.Service.HTTP.WebfingerClient.Mock, as: WebfingerClientMock
@instance_domain "event-federation.eu"
@nodeinfo_fixture_path "test/fixtures/nodeinfo/wp-event-federation.json"
@nodeinfo_regular_fixture_path "test/fixtures/nodeinfo/regular.json"
@nodeinfo_both_versions_fixture_path "test/fixtures/nodeinfo/both_versions.json"
@nodeinfo_older_versions_fixture_path "test/fixtures/nodeinfo/older_versions.json"
@nodeinfo_data_fixture_path "test/fixtures/nodeinfo/data.json"
@nodeinfo_wp_data_fixture_path "test/fixtures/nodeinfo/wp-data.json"
describe "getting application actor" do
test "from wordpress event federation" do
nodeinfo_data = @nodeinfo_fixture_path |> File.read!() |> Jason.decode!()
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://event-federation.eu/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_data}}
end)
assert "https://event-federation.eu/actor-relay" ==
NodeInfo.application_actor(@instance_domain)
end
test "with no FEP-2677 information" do
nodeinfo_data = @nodeinfo_regular_fixture_path |> File.read!() |> Jason.decode!()
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://event-federation.eu/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_data}}
end)
assert nil == NodeInfo.application_actor(@instance_domain)
end
test "with no result" do
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://event-federation.eu/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 404, body: ""}}
end)
assert nil == NodeInfo.application_actor(@instance_domain)
end
end
describe "getting informations" do
test "with both 2.0 and 2.1 endpoints" do
nodeinfo_end_point_data =
@nodeinfo_both_versions_fixture_path |> File.read!() |> Jason.decode!()
nodeinfo_data = @nodeinfo_data_fixture_path |> File.read!() |> Jason.decode!()
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://mobilizon.fr/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
end)
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://mobilizon.fr/.well-known/nodeinfo/2.1"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_data}}
end)
assert {:ok, data} = NodeInfo.nodeinfo("mobilizon.fr")
assert data["version"] == "2.1"
assert data["software"]["name"] == "Mobilizon"
# Added in 2.1
refute is_nil(data["software"]["repository"])
end
test "with only 2.0 endpoint" do
nodeinfo_end_point_data = @nodeinfo_regular_fixture_path |> File.read!() |> Jason.decode!()
nodeinfo_wp_data = @nodeinfo_wp_data_fixture_path |> File.read!() |> Jason.decode!()
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://event-federation.eu/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
end)
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://event-federation.eu/wp-json/activitypub/1.0/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_wp_data}}
end)
assert {:ok, data} = NodeInfo.nodeinfo("event-federation.eu")
assert data["version"] == "2.0"
assert data["software"]["name"] == "wordpress"
# Added in 2.1
assert is_nil(data["software"]["repository"])
end
test "with no valid endpoint" do
nodeinfo_end_point_data =
@nodeinfo_older_versions_fixture_path |> File.read!() |> Jason.decode!()
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://somewhere.tld/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
end)
assert {:error, :no_node_info_endpoint_found} = NodeInfo.nodeinfo("somewhere.tld")
end
test "with no answer from well-known endpoint" do
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://somewhere.tld/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 404}}
end)
assert {:error, :node_info_meta_http_error} = NodeInfo.nodeinfo("somewhere.tld")
end
test "with no answer from version endpoint" do
nodeinfo_end_point_data =
@nodeinfo_both_versions_fixture_path |> File.read!() |> Jason.decode!()
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://mobilizon.fr/.well-known/nodeinfo"
},
_opts ->
{:ok, %Tesla.Env{status: 200, body: nodeinfo_end_point_data}}
end)
WebfingerClientMock
|> expect(:call, fn
%{
method: :get,
url: "https://mobilizon.fr/.well-known/nodeinfo/2.1"
},
_opts ->
{:ok, %Tesla.Env{status: 404}}
end)
assert {:error, :node_info_endpoint_http_error} = NodeInfo.nodeinfo("mobilizon.fr")
end
end
end

View file

@ -0,0 +1,12 @@
{
"links": [
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
"href": "https://mobilizon.fr/.well-known/nodeinfo/2.0"
},
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
"href": "https://mobilizon.fr/.well-known/nodeinfo/2.1"
}
]
}

29
test/fixtures/nodeinfo/data.json vendored Normal file
View file

@ -0,0 +1,29 @@
{
"version": "2.1",
"protocols": [
"activitypub"
],
"metadata": {
"nodeDescription": "Mobilizon.fr est l'instance Mobilizon de Framasoft.",
"nodeName": "Mobilizon"
},
"usage": {
"users": {
"total": 9204
},
"localComments": 3253,
"localPosts": 7545
},
"services": {
"outbound": [
"atom1.0"
],
"inbound": []
},
"software": {
"name": "Mobilizon",
"version": "4.0.2",
"repository": "https://framagit.org/framasoft/mobilizon"
},
"openRegistrations": true
}

View file

@ -0,0 +1,8 @@
{
"links": [
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/1.1",
"href": "https://mobilizon.fr/.well-known/nodeinfo/1.1"
}
]
}

8
test/fixtures/nodeinfo/regular.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"links": [
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
"href": "https://event-federation.eu/wp-json/activitypub/1.0/nodeinfo"
}
]
}

24
test/fixtures/nodeinfo/wp-data.json vendored Normal file
View file

@ -0,0 +1,24 @@
{
"version": "2.0",
"software": {
"name": "wordpress",
"version": "6.4.2"
},
"usage": {
"users": {
"total": 1,
"activeMonth": 1,
"activeHalfyear": 1
},
"localPosts": 3,
"localComments": 3
},
"openRegistrations": false,
"protocols": [
"activitypub"
],
"services": {
"inbound": [],
"outbound": []
}
}

View file

@ -0,0 +1,12 @@
{
"links": [
{
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
"href": "https://event-federation.eu/wp-json/activitypub/1.0/nodeinfo"
},
{
"rel": "https://www.w3.org/ns/activitystreams#Application",
"href": "https://event-federation.eu/actor-relay"
}
]
}

View file

@ -2,12 +2,16 @@ defmodule Mobilizon.Web.NodeInfoControllerTest do
use Mobilizon.Web.ConnCase
alias Mobilizon.Config
alias Mobilizon.Federation.ActivityPub.Relay
use Mobilizon.Web, :verified_routes
test "Get node info schemas", %{conn: conn} do
conn = get(conn, url(~p"/.well-known/nodeinfo"))
relay = Relay.get_actor()
relay_url = relay.url
assert json_response(conn, 200) == %{
"links" => [
%{
@ -17,6 +21,10 @@ defmodule Mobilizon.Web.NodeInfoControllerTest do
%{
"href" => url(~p"/.well-known/nodeinfo/2.1"),
"rel" => "http://nodeinfo.diaspora.software/ns/schema/2.1"
},
%{
"href" => relay_url,
"rel" => "https://www.w3.org/ns/activitystreams#Application"
}
]
}

View file

@ -18,7 +18,7 @@ exports[`CommentTree > renders a comment tree with comments 1`] = `
</div>
</div>
<div data-v-5d0380ab=\\"\\" class=\\"\\">
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\" iconboth=\\"false\\"></o-button-stub>
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\"></o-button-stub>
</div>
</article>
</form>
@ -56,7 +56,7 @@ exports[`CommentTree > renders an empty comment tree 1`] = `
</div>
</div>
<div data-v-5d0380ab=\\"\\" class=\\"\\">
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\" iconboth=\\"false\\"></o-button-stub>
<o-button-stub data-v-5d0380ab=\\"\\" variant=\\"primary\\" iconleft=\\"send\\" rounded=\\"false\\" outlined=\\"false\\" loading=\\"false\\" expanded=\\"false\\" inverted=\\"false\\" nativetype=\\"submit\\" tag=\\"button\\" disabled=\\"false\\"></o-button-stub>
</div>
</article>
</form>