Merge branch 'potsdamn/main' into v4.x

This commit is contained in:
778a69cd 2024-02-29 18:22:31 +01:00
commit 213171822e
53 changed files with 3793 additions and 563 deletions

2
.gitignore vendored
View file

@ -57,3 +57,5 @@ stats.html
/playwright-report/
.histoire
# Nix out-links
result*

View file

@ -1,3 +1,5 @@
*You can learn about [what we plan to do with this fork](https://framacolibri.org/t/using-mobilizon-for-regional-leftist-subculture-calendar-platforms/18772) in this post in the Mobilizon forum.*
<h1 align="center">
<a href="https://joinmobilizon.org">
<img src="https://lutim.cpy.re/qVYC86G9.png" alt="Mobilizon">
@ -16,6 +18,20 @@ Mobilizon is your federated organization and mobilization platform. Gather peopl
</a>
</p>
## Notes about this fork
The currently deployed `main` branch can be tested at [https://rotes.potsda.mn/](https://rotes.potsda.mn/).
### Building with Nix
For building this locally, you can use Nix (with Flakes enabled):
```
$ nix build git+ssh://git@git.potsda.mn/potsda.mn/mobilizon.git?ref=main#mobilizon
```
The built package is then located in `result/`.
## Introduction
Mobilizon is a tool designed to create platforms for managing communities and events. Its purpose is to help as many people as possible to free themselves from Facebook groups and events, from Meetup, etc.

139
default.nix Normal file
View file

@ -0,0 +1,139 @@
{ lib
, beamPackages
, fetchFromGitHub
, git
, cmake
, nixosTests
, src
, src-config
, mobilizon-js
}:
let
inherit (beamPackages) mixRelease buildMix;
in
mixRelease rec {
pname = "mobilizon";
version = "4.1.0-alpha.1";
inherit src;
# See https://github.com/whitfin/cachex/issues/205
# This circumvents a startup error for now
stripDebug = false;
nativeBuildInputs = [ git cmake ];
mixNixDeps = import ./mix.nix {
inherit beamPackages lib;
overrides = (final: prev:
(lib.mapAttrs
(_: value: value.override {
appConfigPath = src-config;
})
prev) // {
fast_html = prev.fast_html.override {
nativeBuildInputs = [ cmake ];
};
ex_cldr = prev.ex_cldr.overrideAttrs (old: {
# We have to use the GitHub sources, as it otherwise tries to download
# the locales at build time.
src = fetchFromGitHub {
owner = "elixir-cldr";
repo = "cldr";
rev = "v${old.version}";
sha256 = assert old.version == "2.37.5";
"sha256-T5Qvuo+xPwpgBsqHNZYnTCA4loToeBn1LKTMsDcCdYs=";
};
postInstall = ''
cp $src/priv/cldr/locales/* $out/lib/erlang/lib/ex_cldr-${old.version}/priv/cldr/locales/
'';
});
# Upstream issue: https://github.com/bryanjos/geo_postgis/pull/87
geo_postgis = prev.geo_postgis.overrideAttrs (old: {
propagatedBuildInputs = old.propagatedBuildInputs ++ [ final.ecto ];
});
# The remainder are Git dependencies (and their deps) that are not supported by mix2nix currently.
web_push_encryption = buildMix rec {
name = "web_push_encryption";
version = "0.3.1";
src = fetchFromGitHub {
owner = "danhper";
repo = "elixir-web-push-encryption";
rev = "6e143dcde0a2854c4f0d72816b7ecab696432779";
sha256 = "sha256-Da+/28SPZuUQBi8fQj31zmMvhMrYUaQIW4U4E+mRtMg=";
};
beamDeps = with final; [ httpoison jose ];
};
icalendar = buildMix rec {
name = "icalendar";
version = "unstable-2022-04-10";
src = fetchFromGitHub {
owner = "tcitworld";
repo = name;
rev = "1033d922c82a7223db0ec138e2316557b70ff49f";
sha256 = "sha256-N3bJZznNazLewHS4c2B7LP1lgxd1wev+EWVlQ7rOwfU=";
};
beamDeps = with final; [ mix_test_watch ex_doc timex ];
};
rajska = buildMix rec {
name = "rajska";
version = "1.3.3";
src = fetchFromGitHub {
owner = "tcitworld";
repo = name;
rev = "0c036448e261e8be6a512581c592fadf48982d84";
sha256 = "sha256-4pfply1vTAIT2Xvm3kONmrCK05xKfXFvcb8EKoSCXBE=";
};
beamDeps = with final; [ ex_doc credo absinthe excoveralls hammer mock ];
};
exkismet = buildMix rec {
name = "exkismet";
version = "0.0.3";
src = fetchFromGitHub {
owner = "tcitworld";
repo = name;
rev = "8b5485fde00fafbde20f315bec387a77f7358334";
sha256 = "sha256-ttgCWoBKU7VTjZJBhZNtqVF4kN7psBr/qOeR65MbTqw=";
};
beamDeps = with final; [ httpoison ex_doc credo doctor dialyxir ];
};
});
};
preConfigure = ''
export LANG=C.UTF-8 # fix elixir locale warning
'';
# Install the compiled js part
preBuild =
''
cp -a "${mobilizon-js}/_napalm-install/priv/static" ./priv
chmod 770 -R ./priv
'';
postBuild = ''
mix phx.digest --no-deps-check
'';
# Just a hack to reduce path size by 60MB
postInstall =
let
inherit (mixNixDeps) ex_cldr;
in
''
rm -r $out/lib/ex_cldr-${ex_cldr.version}/priv/cldr/locales
ln -s ${ex_cldr.src}/priv/cldr/locales $out/lib/ex_cldr-${ex_cldr.version}/priv/cldr/locales
'';
passthru.elixirPackage = beamPackages.elixir;
meta = with lib; {
description = "Mobilizon is an online tool to help manage your events, your profiles and your groups";
homepage = "https://joinmobilizon.org/";
license = licenses.agpl3Plus;
maintainers = with maintainers; [ minijackson erictapen ];
};
}

98
flake.lock Normal file
View file

@ -0,0 +1,98 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"napalm": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1703102458,
"narHash": "sha256-3pOV731qi34Q2G8e2SqjUXqnftuFrbcq+NdagEZXISo=",
"owner": "nix-community",
"repo": "napalm",
"rev": "edcb26c266ca37c9521f6a97f33234633cbec186",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "napalm",
"type": "github"
}
},
"nix-filter": {
"locked": {
"lastModified": 1705332318,
"narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=",
"owner": "numtide",
"repo": "nix-filter",
"rev": "3449dc925982ad46246cfc36469baf66e1b64f17",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-filter",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1707092692,
"narHash": "sha256-ZbHsm+mGk/izkWtT4xwwqz38fdlwu7nUUKXTOmm4SyE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "faf912b086576fd1a15fca610166c98d47bc667e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"napalm": "napalm",
"nix-filter": "nix-filter",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

249
flake.nix Normal file
View file

@ -0,0 +1,249 @@
{
description = "Mobilizon fork for potsda.mn";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nix-filter.url = "github:numtide/nix-filter";
napalm.url = "github:nix-community/napalm";
napalm.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, nix-filter, napalm }:
let
forAllSystems = f: nixpkgs.lib.genAttrs
[ "x86_64-linux" "aarch64-linux" ]
(system: f system);
nixpkgsFor = forAllSystems (
system:
import nixpkgs { inherit system; }
);
filter = nix-filter.lib;
in
{
packages = forAllSystems (system:
let
pkgs = nixpkgsFor.${system};
# Directories that are neither needed for building the frontend nor the backend.
# For better build caching.
unrelatedDirs = [
"flake.lock"
(filter.matchExt "nix")
"docs"
"docker"
"docker-compose.test.yml"
"docker-compose.yml"
"generate-test-data"
];
in
{
mobilizon = pkgs.callPackage ./. {
src = filter {
root = ./.;
exclude = [
"src"
(filter.matchExt "js")
(filter.matchExt "ts")
(filter.matchExt "json")
"tests"
"scripts"
"public"
] ++ unrelatedDirs;
};
src-config = ./config;
mobilizon-js = self.packages."${system}".mobilizon-frontend;
};
mobilizon-frontend =
let
nodejs = pkgs.nodejs-18_x;
in
napalm.legacyPackages."${system}".buildPackage
(filter {
root = ./.;
exclude = [
"lib"
"config"
"test"
"rel"
"support"
] ++ unrelatedDirs;
})
{
inherit nodejs;
nativeBuildInputs = [ pkgs.imagemagick ];
npmCommands = [ "npm install" "npm run build" ];
# Keep this in sync with the content of ./patches/
customPatchPackages = {
vue-i18n-extract."2.0.7" = pkgs: prev: {
preConfigure = ''
${pkgs.git}/bin/git apply -p3 ${./patches/vue-i18n-extract+2.0.7.patch}
'';
};
};
};
default = self.packages."${system}".mobilizon;
# Update local Mobilizon definition
update =
pkgs.writeShellScriptBin "update" ''
set -eou pipefail
${pkgs.mix2nix}/bin/mix2nix ./mix.lock > mix.nix
'';
});
devShells = forAllSystems (system:
let pkgs = nixpkgsFor.${system};
in {
default =
pkgs.mkShell {
buildInputs = with pkgs; [
elixir
mix2nix
cmake
imagemagick
nodejs-18_x
];
};
});
overlays.default = final: prev: {
inherit (self.packages."${prev.system}") mobilizon;
};
checks = forAllSystems (system: {
inherit (self.packages.${system}) mobilizon update;
nixosTest =
let
pkgsMobilizon = import nixpkgs {
inherit system;
overlays = [ self.overlays.default ];
};
certs = import "${nixpkgs}/nixos/tests/common/acme/server/snakeoil-certs.nix";
test = import ./nixos-test.nix { inherit certs; };
in
pkgsMobilizon.nixosTest test;
});
lib = {
# Patch the logos in the source tree of a mobilizon-frontend package before building.
# Can be used to construct the argument for .overrideAttrs on mobilizon-frontend.
mobilizonLogosOverride = icons:
let
inherit (icons) logo favicon;
in
old: {
postPatch = ''
cp '${logo}' src/assets/logo.svg
magick convert \
-resize x16 \
-gravity center \
-crop 16x16+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
public/img/icons/favicon-16x16.png
magick convert \
-resize x32 \
-gravity center \
-crop 32x32+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
public/img/icons/favicon-32x32.png
magick convert \
-resize x16 \
-gravity center \
-crop 16x16+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
favicon-16x16.ico
magick convert \
-resize x32 \
-gravity center \
-crop 32x32+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
favicon-32x32.ico
magick convert \
-resize x48 \
-gravity center \
-crop 48x48+0+0 \
-flatten \
-colors 256 \
'${favicon}' \
favicon-48x48.ico
magick convert \
favicon-16x16.ico \
favicon-32x32.ico \
favicon-48x48.ico \
public/favicon.ico
rm favicon-16x16.ico favicon-32x32.ico favicon-48x48.ico
cp '${favicon}' public/img/icons/favicon.svg
cp '${favicon}' public/img/icons/safari-pinned-tab.svg
magick convert \
'${favicon}' \
-gravity center \
-extent 630x350 \
public/img/mobilizon_default_card.png
magick convert \
-background '#e08c96' \
'${logo}' \
-resize 366x108 \
public/img/mobilizon_logo.png
'' + nixpkgs.lib.concatMapStrings
({ resize, filename }: ''
magick convert \
-resize x${resize} \
'${favicon}' \
public/img/icons/${filename}
'')
[
{ resize = "180"; filename = "apple-touch-icon.png"; }
{ resize = "180"; filename = "apple-touch-icon-180x180.png"; }
{ resize = "152"; filename = "apple-touch-icon-152x152.png"; }
{ resize = "120"; filename = "apple-touch-icon-120x120.png"; }
{ resize = "76"; filename = "apple-touch-icon-76x76.png"; }
{ resize = "60"; filename = "apple-touch-icon-60x60.png"; }
{ resize = "192"; filename = "android-chrome-192x192.png"; }
{ resize = "512"; filename = "android-chrome-512x512.png"; }
{ resize = "192"; filename = "android-chrome-maskable-192x192.png"; }
{ resize = "512"; filename = "android-chrome-maskable-512x512.png"; }
{ resize = "128"; filename = "badge-128x128.png"; }
{ resize = "144"; filename = "icon-144x144.png"; }
{ resize = "168"; filename = "icon-168x168.png"; }
{ resize = "256"; filename = "icon-256x256.png"; }
{ resize = "48"; filename = "icon-48x48.png"; }
{ resize = "72"; filename = "icon-72x72.png"; }
{ resize = "96"; filename = "icon-96x96.png"; }
{ resize = "144"; filename = "msapplication-icon-144x144.png"; }
{ resize = "150"; filename = "mstile-150x150.png"; }
{ resize = "192"; filename = "android-chrome-192x192.png"; }
{ resize = "512"; filename = "android-chrome-512x512.png"; }
{ resize = "192"; filename = "android-chrome-maskable-192x192.png"; }
{ resize = "512"; filename = "android-chrome-maskable-512x512.png"; }
];
};
};
};
}

View file

@ -20,6 +20,7 @@ defmodule Mobilizon.Config do
registration_email_denylist: list(String.t()),
demo: boolean(),
repository: String.t(),
version: String.t(),
email_from: String.t(),
email_reply_to: String.t(),
federating: boolean(),
@ -160,9 +161,7 @@ defmodule Mobilizon.Config do
end
@spec instance_version :: String.t()
def instance_version do
GitStatus.commit()
end
def instance_version, do: instance_config()[:version]
@spec instance_hostname :: String.t()
def instance_hostname, do: instance_config()[:hostname]

View file

@ -28,126 +28,82 @@ defmodule Mobilizon.Events.Categories do
defp build_in_categories do
[
%{
id: :arts,
label: gettext("Arts")
id: :ausflug,
label: gettext("Ausflug")
},
%{
id: :book_clubs,
label: gettext("Book clubs")
id: :ausstellung,
label: gettext("Ausstellung")
},
%{
id: :business,
label: gettext("Business")
id: :demonstration,
label: gettext("Demonstration")
},
%{
id: :causes,
label: gettext("Causes")
id: :einweihung,
label: gettext("Einweihung")
},
%{
id: :comedy,
label: gettext("Comedy")
id: :filmvorfuehrung,
label: gettext("Filmvorführung")
},
%{
id: :crafts,
label: gettext("Crafts")
id: :fussball,
label: gettext("Fußball")
},
%{
id: :food_drink,
label: gettext("Food & Drink")
id: :gedenken,
label: gettext("Gedenken")
},
%{
id: :health,
label: gettext("Health")
id: :infostand,
label: gettext("Infostand")
},
%{
id: :music,
label: gettext("Music")
id: :kuenstlerisches,
label: gettext("Künstlerisches")
},
%{
id: :auto_boat_air,
label: gettext("Auto, boat and air")
id: :kneipe,
label: gettext("Kneipe")
},
%{
id: :community,
label: gettext("Community")
id: :konzert,
label: gettext("Konzert")
},
%{
id: :family_education,
label: gettext("Family & Education")
id: :kuefa,
label: gettext("Fa")
},
%{
id: :fashion_beauty,
label: gettext("Fashion & Beauty")
},
%{
id: :film_media,
label: gettext("Film & Media")
},
%{
id: :games,
label: gettext("Games")
},
%{
id: :language_culture,
label: gettext("Language & Culture")
},
%{
id: :learning,
label: gettext("Learning")
},
%{
id: :lgbtq,
label: gettext("LGBTQ")
},
%{
id: :movements_politics,
label: gettext("Movements and politics")
},
%{
id: :networking,
label: gettext("Networking")
id: :lesung,
label: gettext("Lesung")
},
%{
id: :party,
label: gettext("Party")
},
%{
id: :performing_visual_arts,
label: gettext("Performing & Visual Arts")
id: :sport,
label: gettext("Sport")
},
%{
id: :pets,
label: gettext("Pets")
id: :theater,
label: gettext("Theater")
},
%{
id: :photography,
label: gettext("Photography")
id: :verhandlung,
label: gettext("Verhandlung")
},
%{
id: :outdoors_adventure,
label: gettext("Outdoors & Adventure")
},
%{
id: :spirituality_religion_beliefs,
label: gettext("Spirituality, Religion & Beliefs")
},
%{
id: :science_tech,
label: gettext("Science & Tech")
},
%{
id: :sports,
label: gettext("Sports")
},
%{
id: :theatre,
label: gettext("Theatre")
id: :workshop,
label: gettext("Workshop")
},
# Legacy default value
%{
id: :meeting,
label: gettext("Meeting")
}
label: gettext("Infoveranstaltung")
},
]
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

@ -18,6 +18,8 @@ defmodule Mobilizon.Web.PageController do
defdelegate my_events(conn, params), to: PageController, as: :index
@spec create_event(Plug.Conn.t(), any) :: Plug.Conn.t()
defdelegate create_event(conn, params), to: PageController, as: :index
@spec calendar(Plug.Conn.t(), any) :: Plug.Conn.t()
defdelegate calendar(conn, params), to: PageController, as: :index
@spec list_events(Plug.Conn.t(), any) :: Plug.Conn.t()
defdelegate list_events(conn, params), to: PageController, as: :index
@spec edit_event(Plug.Conn.t(), any) :: Plug.Conn.t()

View file

@ -77,7 +77,7 @@ defmodule Mobilizon.Web.Plugs.HTTPSecurityPlug do
# unsafe-eval is because of JS issues with regenerator-runtime
@script_src "script-src 'self' 'unsafe-eval' "
@style_src "style-src 'self' "
@font_src "font-src 'self' "
@font_src "font-src 'self' data: "
@spec csp_string(Keyword.t()) :: String.t()
defp csp_string(options) do
@ -117,6 +117,8 @@ defmodule Mobilizon.Web.Plugs.HTTPSecurityPlug do
style_src = [style_src] ++ [get_csp_config(:style_src, options)]
style_src = [style_src] ++ ["'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='"]
font_src = [@font_src] ++ [get_csp_config(:font_src, options)]
frame_src = build_csp_field(:frame_src, options)

View file

@ -120,6 +120,7 @@ defmodule Mobilizon.Web.Router do
get("/@:name", PageController, :actor)
get("/events/me", PageController, :my_events)
get("/events/create", PageController, :create_event)
get("/events/calendar", PageController, :calendar)
get("/events/:uuid", PageController, :event)
get("/comments/:uuid", PageController, :comment)
get("/resource/:uuid", PageController, :resource)
@ -188,6 +189,7 @@ defmodule Mobilizon.Web.Router do
get("/events/create", PageController, :create_event)
get("/events/list", PageController, :list_events)
get("/events/me", PageController, :my_events)
get("/events/calendar", PageController, :calendar)
get("/events/:uuid/edit", PageController, :edit_event)
# This is a hack to ease link generation into emails

View file

@ -15,11 +15,6 @@
document.documentElement.classList.remove('dark')
}
</script>
<%= if root?(assigns) do %>
<link rel="preload" href="/img/shape-1.svg" as="image" />
<link rel="preload" href="/img/shape-2.svg" as="image" />
<link rel="preload" href="/img/shape-3.svg" as="image" />
<% end %>
<%= tags(assigns) || assigns.tags %>
<%= Vite.vite_client() %>
<%= Vite.vite_snippet("src/main.ts") %>

2014
mix.nix Normal file

File diff suppressed because it is too large Load diff

46
nixos-test.nix Normal file
View file

@ -0,0 +1,46 @@
{ certs }:
{ lib, ... }:
let
mobilizonDomain = certs.domain;
port = 41395;
in
{
name = "mobilizon";
meta.maintainers = with lib.maintainers; [ minijackson erictapen ];
nodes.server =
{ pkgs, ... }:
{
services.mobilizon = {
enable = true;
settings = {
":mobilizon" = {
":instance" = {
name = "Test Mobilizon";
hostname = mobilizonDomain;
};
"Mobilizon.Web.Endpoint".http.port = port;
};
};
};
services.postgresql.package = pkgs.postgresql_14;
security.pki.certificateFiles = [ certs.ca.cert ];
services.nginx.virtualHosts."${mobilizonDomain}" = {
enableACME = lib.mkForce false;
sslCertificate = certs.${mobilizonDomain}.cert;
sslCertificateKey = certs.${mobilizonDomain}.key;
};
networking.hosts."::1" = [ mobilizonDomain ];
};
testScript = ''
server.wait_for_unit("mobilizon.service")
server.wait_for_open_port(${toString port})
server.succeed("curl --fail https://${mobilizonDomain}/")
'';
}

188
package-lock.json generated
View file

@ -9,9 +9,13 @@
"version": "4.1.0",
"hasInstallScript": true,
"dependencies": {
"@apollo/client": "^3.3.16",
"@apollo/client": "^3.9.5",
"@framasoft/socket": "^1.0.0",
"@framasoft/socket-apollo-link": "^1.0.0",
"@fullcalendar/core": "^6.1.10",
"@fullcalendar/daygrid": "^6.1.10",
"@fullcalendar/interaction": "^6.1.10",
"@fullcalendar/vue3": "^6.1.10",
"@oruga-ui/oruga-next": "^0.8.2",
"@oruga-ui/theme-oruga": "^0.2.0",
"@sentry/tracing": "^7.1",
@ -57,6 +61,7 @@
"graphql": "^16.8.1",
"graphql-tag": "^2.10.3",
"hammerjs": "^2.0.8",
"ical.js": "^1.5.0",
"intersection-observer": "^0.12.0",
"jwt-decode": "^4.0.0",
"leaflet": "^1.4.0",
@ -121,6 +126,7 @@
"typescript": "~5.3.2",
"vite": "^5.0.12",
"vite-plugin-pwa": "^0.19.0",
"vite-svg-loader": "^4.0.0",
"vitest": "^1.2.2",
"vue-i18n-extract": "^2.0.4",
"vue-router-mock": "^1.0.0"
@ -2667,6 +2673,39 @@
"zen-observable": "^0.10.0"
}
},
"node_modules/@fullcalendar/core": {
"version": "6.1.10",
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.10.tgz",
"integrity": "sha512-oTXGJSAGpCf1oY+CKp5qYjMHkJCPBkJ3SHitl63n8Q6xKeiwQ4EF6Au451euUovREwJpLmD1AyZrCnWmtB9AVg==",
"dependencies": {
"preact": "~10.12.1"
}
},
"node_modules/@fullcalendar/daygrid": {
"version": "6.1.10",
"resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.10.tgz",
"integrity": "sha512-Z4GRm1IyHKgxXFTWGcEI0nTsvYOIkpE0aMt3/o3ER2SZkF+hfwcDFhtj0c9+WhMjXFIWYeoTnA9rUOY7Zl/nxA==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.10"
}
},
"node_modules/@fullcalendar/interaction": {
"version": "6.1.10",
"resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.10.tgz",
"integrity": "sha512-aZRlwCpmDasq2RNeWV0ub20Uevare9Cb6iMlxCacx0fhOC14H28G9d1FsduJIecInL84SPGwt5ItqAYMsWv7zw==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.10"
}
},
"node_modules/@fullcalendar/vue3": {
"version": "6.1.10",
"resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.10.tgz",
"integrity": "sha512-YMYBQx0TlWNuN4G6ra2dkf5cCF5aVi/2zDLGLvLqe2Nk2o7uNbTkrCSG40061OepWQlJv+hYqm1JukLRmyqi4Q==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.10",
"vue": "^3.0.11"
}
},
"node_modules/@graphql-typed-document-node/core": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
@ -3942,6 +3981,15 @@
"node": ">= 10"
}
},
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
"dev": true,
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@ -6071,6 +6119,47 @@
"node": ">=8"
}
},
"node_modules/css-select": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
"dev": true,
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-tree": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"dependencies": {
"mdn-data": "2.0.30",
"source-map-js": "^1.0.1"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
}
},
"node_modules/css-what": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
"dev": true,
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@ -6082,6 +6171,39 @@
"node": ">=4"
}
},
"node_modules/csso": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
"dev": true,
"dependencies": {
"css-tree": "~2.2.0"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/csso/node_modules/css-tree": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
"dev": true,
"dependencies": {
"mdn-data": "2.0.28",
"source-map-js": "^1.0.1"
},
"engines": {
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/csso/node_modules/mdn-data": {
"version": "2.0.28",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
"dev": true
},
"node_modules/cssom": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
@ -8215,6 +8337,11 @@
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/ical.js": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/ical.js/-/ical.js-1.5.0.tgz",
"integrity": "sha512-7ZxMkogUkkaCx810yp0ZGKvq1ZpRgJeornPttpoxe6nYZ3NLesZe1wWMXDdwTkj/b5NtXT+Y16Aakph/ao98ZQ=="
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -9691,6 +9818,12 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/mdn-data": {
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true
},
"node_modules/mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
@ -10734,6 +10867,15 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/preact": {
"version": "10.12.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -12312,6 +12454,40 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/svgo": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.4.tgz",
"integrity": "sha512-T+Xul3JwuJ6VGXKo/p2ndqx1ibxNKnLTvRc1ZTWKCfyKS/GgNjRZcYsK84fxTsy/izr91g/Rwx6fGnVgaFSI5g==",
"dev": true,
"dependencies": {
"@trysound/sax": "0.2.0",
"commander": "^7.2.0",
"css-select": "^5.1.0",
"css-tree": "^2.2.1",
"css-what": "^6.1.0",
"csso": "5.0.5",
"picocolors": "^1.0.0"
},
"bin": {
"svgo": "bin/svgo"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/svgo"
}
},
"node_modules/svgo/node_modules/commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true,
"engines": {
"node": ">= 10"
}
},
"node_modules/symbol-observable": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
@ -13194,6 +13370,16 @@
}
}
},
"node_modules/vite-svg-loader": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-4.0.0.tgz",
"integrity": "sha512-0MMf1yzzSYlV4MGePsLVAOqXsbF5IVxbn4EEzqRnWxTQl8BJg/cfwIzfQNmNQxZp5XXwd4kyRKF1LytuHZTnqA==",
"dev": true,
"dependencies": {
"@vue/compiler-sfc": "^3.2.20",
"svgo": "^3.0.2"
}
},
"node_modules/vitest": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz",

View file

@ -15,9 +15,7 @@
"story:preview": "histoire preview",
"test": "vitest",
"coverage": "vitest run --coverage",
"prepare": "husky",
"pre-commit": "lint-staged",
"postinstall": "patch-package"
"pre-commit": "lint-staged"
},
"lint-staged": {
"**/*.{js,ts,vue}": [
@ -31,9 +29,13 @@
},
"type": "module",
"dependencies": {
"@apollo/client": "^3.3.16",
"@apollo/client": "^3.9.5",
"@framasoft/socket": "^1.0.0",
"@framasoft/socket-apollo-link": "^1.0.0",
"@fullcalendar/core": "^6.1.10",
"@fullcalendar/daygrid": "^6.1.10",
"@fullcalendar/interaction": "^6.1.10",
"@fullcalendar/vue3": "^6.1.10",
"@oruga-ui/oruga-next": "^0.8.2",
"@oruga-ui/theme-oruga": "^0.2.0",
"@sentry/tracing": "^7.1",
@ -79,6 +81,7 @@
"graphql": "^16.8.1",
"graphql-tag": "^2.10.3",
"hammerjs": "^2.0.8",
"ical.js": "^1.5.0",
"intersection-observer": "^0.12.0",
"jwt-decode": "^4.0.0",
"leaflet": "^1.4.0",
@ -143,6 +146,7 @@
"typescript": "~5.3.2",
"vite": "^5.0.12",
"vite-plugin-pwa": "^0.19.0",
"vite-svg-loader": "^4.0.0",
"vitest": "^1.2.2",
"vue-i18n-extract": "^2.0.4",
"vue-router-mock": "^1.0.0"

View file

@ -2269,42 +2269,7 @@ msgstr "LGBTQ"
msgid "Language & Culture"
msgstr "Sprache & Kultur"
#: lib/mobilizon/events/categories.ex:96
#, elixir-autogen, elixir-format
msgid "Learning"
msgstr "Lernen"
#: lib/mobilizon/events/categories.ex:149
#, elixir-autogen, elixir-format
msgid "Meeting"
msgstr "Treffen"
#: lib/mobilizon/events/categories.ex:104
#, elixir-autogen, elixir-format
msgid "Movements and politics"
msgstr "Bewegungen und Politik"
#: lib/mobilizon/events/categories.ex:64
#, elixir-autogen, elixir-format
msgid "Music"
msgstr "Musik"
#: lib/mobilizon/events/categories.ex:108
#, elixir-autogen, elixir-format
msgid "Networking"
msgstr "Netzwerke"
#: lib/mobilizon/events/categories.ex:128
#, elixir-autogen, elixir-format
msgid "Outdoors & Adventure"
msgstr "Natur & Abenteuer"
#: lib/mobilizon/events/categories.ex:112
#, elixir-autogen, elixir-format
msgid "Party"
msgstr "Party"
#: lib/mobilizon/events/categories.ex:116
#: lib/mobilizon/events/categories.ex:100
#, elixir-autogen, elixir-format
msgid "Performing & Visual Arts"
msgstr "Darstellende & bildende Kunst"
@ -2329,16 +2294,63 @@ msgstr "Wissenschaft & Technologie"
msgid "Spirituality, Religion & Beliefs"
msgstr "Glauben, Religion & Spiritualität"
#: lib/mobilizon/events/categories.ex:140
#, elixir-autogen, elixir-format
msgid "Sports"
msgid "Ausflug"
msgstr "Ausflug"
msgid "Ausstellung"
msgstr "Ausstellung"
msgid "Demonstration"
msgstr "Demonstration"
msgid "Einweihung"
msgstr "Einweihung"
msgid "Filmvorführung"
msgstr "Filmvorführung"
msgid "Fußball"
msgstr "Fußball"
msgid "Gedenken"
msgstr "Gedenken"
msgid "Infostand"
msgstr "Infostand"
msgid "Infoveranstaltung"
msgstr "Infoveranstaltung"
msgid "Künstlerisches"
msgstr "Künstlerisches"
msgid "Kneipe"
msgstr "Kneipe"
msgid "Konzert"
msgstr "Konzert"
msgid "KüFa"
msgstr "KüFa"
msgid "Lesung"
msgstr "Lesung"
msgid "Party"
msgstr "Party"
msgid "Sport"
msgstr "Sport"
#: lib/mobilizon/events/categories.ex:144
#, elixir-autogen, elixir-format
msgid "Theatre"
msgid "Theater"
msgstr "Theater"
msgid "Verhandlung"
msgstr "Verhandlung"
msgid "Workshop"
msgstr "Workshop"
#: lib/web/templates/email/participation/event_card.text.eex:9
#, elixir-autogen, elixir-format
msgid "Read more: %{url}"

View file

@ -1936,11 +1936,6 @@ msgstr ""
#: lib/mobilizon/events/categories.ex:112
#, elixir-autogen, elixir-format
msgid "Party"
msgstr ""
#: lib/mobilizon/events/categories.ex:116
#, elixir-autogen, elixir-format
msgid "Performing & Visual Arts"
msgstr ""
@ -1964,15 +1959,62 @@ msgstr ""
msgid "Spirituality, Religion & Beliefs"
msgstr ""
#: lib/mobilizon/events/categories.ex:140
#, elixir-autogen, elixir-format
msgid "Sports"
msgstr ""
msgid "Ausflug"
msgstr "Excursion"
#: lib/mobilizon/events/categories.ex:144
#, elixir-autogen, elixir-format
msgid "Theatre"
msgstr ""
msgid "Ausstellung"
msgstr "Exhibition"
msgid "Demonstration"
msgstr "Demonstration"
msgid "Einweihung"
msgstr "Inauguration"
msgid "Filmvorführung"
msgstr "Film screening"
msgid "Fußball"
msgstr "Football"
msgid "Gedenken"
msgstr "Remembrance"
msgid "Infostand"
msgstr "Infostand"
msgid "Infoveranstaltung"
msgstr "Info event"
msgid "Künstlerisches"
msgstr "Art related"
msgid "Kneipe"
msgstr "Bar"
msgid "Konzert"
msgstr "Concert"
msgid "KüFa"
msgstr "Kitchen for all"
msgid "Lesung"
msgstr "Reading"
msgid "Party"
msgstr "Party"
msgid "Sport"
msgstr "Sport"
msgid "Theater"
msgstr "Theater"
msgid "Verhandlung"
msgstr "Trial"
msgid "Workshop"
msgstr "Workshop"
#: lib/web/templates/email/participation/event_card.text.eex:9
#, elixir-autogen, elixir-format, fuzzy

View file

@ -1,10 +0,0 @@
<!--?xml version="1.0" standalone="no"?-->
<svg id="sw-js-blob-svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="sw-gradient" x1="0" x2="1" y1="1" y2="0">
<stop id="stop1" stop-color="rgba(255, 231.287, 78.545, 0.3)" offset="0%"></stop>
<stop id="stop2" stop-color="rgba(254.848, 165.324, 149.009, 0.25)" offset="100%"></stop>
</linearGradient>
</defs>
<path fill="url(#sw-gradient)" d="M18.2,-13.5C23.5,-7.8,27.8,-0.2,27.7,8.7C27.5,17.6,22.9,27.8,14.1,33.9C5.3,39.9,-7.8,41.6,-17.7,36.8C-27.6,32,-34.2,20.7,-37.1,8.4C-39.9,-3.9,-39,-17.2,-32.2,-23.2C-25.4,-29.3,-12.7,-28.3,-3.1,-25.8C6.4,-23.3,12.8,-19.3,18.2,-13.5Z" width="100%" height="100%" transform="translate(50 50)" style="transition: all 0.3s ease 0s;" stroke-width="0" stroke="url(#sw-gradient)"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1,015 B

View file

@ -1,10 +0,0 @@
<!--?xml version="1.0" standalone="no"?-->
<svg id="sw-js-blob-svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="sw-gradient" x1="0" x2="1" y1="1" y2="0">
<stop id="stop1" stop-color="rgba(181.058, 255, 167.816, 0.2)" offset="0%"></stop>
<stop id="stop2" stop-color="rgba(149.009, 254.848, 251.263, 0.25)" offset="100%"></stop>
</linearGradient>
</defs>
<path fill="url(#sw-gradient)" d="M20.2,-14.3C28.2,-6,38.3,2.5,37.6,9.8C36.9,17.1,25.5,23.1,15.5,25.2C5.6,27.3,-2.9,25.4,-11.2,21.9C-19.6,18.4,-27.9,13.3,-30.8,5.6C-33.7,-2.1,-31.2,-12.5,-25.2,-20.4C-19.1,-28.3,-9.6,-33.7,-1.8,-32.3C6.1,-30.9,12.1,-22.7,20.2,-14.3Z" width="100%" height="100%" transform="translate(50 50)" style="transition: all 0.3s ease 0s;" stroke-width="0" stroke="url(#sw-gradient)"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1,016 B

View file

@ -1,10 +0,0 @@
<!--?xml version="1.0" standalone="no"?-->
<svg id="sw-js-blob-svg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="sw-gradient" x1="0" x2="1" y1="1" y2="0">
<stop id="stop1" stop-color="rgba(172.198, 167.816, 255, 0.2)" offset="0%"></stop>
<stop id="stop2" stop-color="rgba(236.8, 149.009, 254.848, 0.25)" offset="100%"></stop>
</linearGradient>
</defs>
<path fill="url(#sw-gradient)" d="M25.3,-21.5C29.4,-15.2,26.8,-4.8,23.6,3.8C20.4,12.5,16.5,19.4,10.2,23.2C3.9,27,-4.8,27.6,-12.6,24.5C-20.3,21.4,-27,14.6,-30.1,5.6C-33.2,-3.4,-32.6,-14.4,-26.9,-21.1C-21.3,-27.8,-10.7,-30.1,0,-30.1C10.7,-30.1,21.3,-27.8,25.3,-21.5Z" width="100%" height="100%" transform="translate(50 50)" style="transition: all 0.3s ease 0s;" stroke-width="0" stroke="url(#sw-gradient)"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1,013 B

View file

@ -337,6 +337,11 @@ onBeforeUnmount(() => {
}
}
.w-full .mbz-card {
width: 20rem;
max-width: 95%;
}
.vue-skip-to {
z-index: 40;
}

32
src/assets/logo.svg Normal file
View file

@ -0,0 +1,32 @@
<svg
class="bg-white dark:bg-zinc-900 dark:fill-white"
:class="{ 'bg-gray-900': invert }"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 248.16 46.78"
>
<title>Mobilizon Logo</title>
<g data-name="header">
<path
d="M0 45.82l3.18-40.8a29.88 29.88 0 015.07-.36 27.74 27.74 0 014.95.36l4.86 17.16a92.19 92.19 0 012.34 10.08h.36a92.19 92.19 0 012.34-10.08L28 5.02a29.23 29.23 0 015-.36 29.23 29.23 0 015 .36l3.18 40.8a13.61 13.61 0 01-3.63.42 23.41 23.41 0 01-3.63-.24l-1.2-19.92q-.36-5.52-.48-12.84h-.44l-7.32 26.51a25.62 25.62 0 01-4 .3 23.36 23.36 0 01-3.84-.3L9.36 13.24H9q-.3 8.94-.48 12.84L7.26 46a22.47 22.47 0 01-3.6.24A13.75 13.75 0 010 45.82zM74 31.06q0 8-4.26 12.3a12.21 12.21 0 01-9 3.42 12.21 12.21 0 01-9-3.42q-4.26-4.26-4.26-12.3t4.24-12.31a12.21 12.21 0 019-3.42 12.21 12.21 0 019 3.42Q74 23.02 74 31.06zM60.75 20.98q-5.67 0-5.67 10.08t5.67 10.08q5.67 0 5.67-10.08t-5.67-10.08zM103.2 19.75q2.7 4.11 2.7 11.28T102 42.31a13.18 13.18 0 01-10 4.11 31.41 31.41 0 01-11.34-2V2.2l.4-.45h2.76A4 4 0 0187 2.83a5.38 5.38 0 01.93 3.57v11.94a12.08 12.08 0 017.56-2.7 8.71 8.71 0 017.71 4.11zm-9.72 2a7.28 7.28 0 00-5.58 2.82v16a15 15 0 004.08.54 5.25 5.25 0 004.68-2.67q1.68-2.67 1.68-7.59 0-9.03-4.86-9.1zM121 22v23.94a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3V24.75q0-3.24-2.7-3.24h-.72a9.32 9.32 0 01-.3-2.58 10.7 10.7 0 01.3-2.7 39.63 39.63 0 014.38-.24h1a5.19 5.19 0 014 1.62A6.27 6.27 0 01121 22z"
/>
<path
d="M119.82.84a7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.93 7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.93z"
fill="currentColor"
/>
<path
d="M139.08 40.42h2a10.23 10.23 0 01.6 3.18 9.24 9.24 0 01-.18 2.1 38.47 38.47 0 01-5.64.54q-6.48 0-6.48-7v-37l.36-.42h2.88a3.94 3.94 0 013.12 1.05 5.52 5.52 0 01.9 3.57v31.31q-.02 2.67 2.44 2.67zM155.94 22v23.94a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3V24.75q0-3.24-2.7-3.24h-.72a9.32 9.32 0 01-.3-2.58 10.7 10.7 0 01.3-2.7 39.63 39.63 0 014.38-.24h1a5.19 5.19 0 014.05 1.62 6.27 6.27 0 011.43 4.39z"
/>
<path
d="M154.8 2.84a7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.93 7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.93z"
fill="currentColor"
/>
<path
d="M163.08 39.22l8.76-11.82q1.32-1.8 4.8-5.7l-.18-.3a63.09 63.09 0 01-7.74.42H163a9.79 9.79 0 01-.24-2.34 15.8 15.8 0 01.42-3.3h20.4a16.31 16.31 0 011 4.26 4.1 4.1 0 01-.78 2.34L175 34.66a64.65 64.65 0 01-4.56 5.7l.18.24q3.12-.3 5.22-.3h2.58a15.35 15.35 0 006.12-.9 9.4 9.4 0 01.72 3.12q0 3.42-4.32 3.42h-18a14.27 14.27 0 01-.9-3.93 5.08 5.08 0 011.04-2.79zM215.88 31.06q0 8-4.26 12.3a13.63 13.63 0 01-18.06 0q-4.26-4.26-4.26-12.3t4.26-12.31a13.63 13.63 0 0118.06 0q4.26 4.27 4.26 12.31zm-13.29-10.08q-5.67 0-5.67 10.08t5.67 10.08q5.67 0 5.67-10.08t-5.67-10.08zM247 25.84v13.32a11 11 0 001.2 5.64 7 7 0 01-4.41 1.56q-2.43 0-3.33-1.14a5.69 5.69 0 01-.9-3.54V27.4a7.74 7.74 0 00-.72-3.87 2.78 2.78 0 00-2.58-1.17 8.62 8.62 0 00-6.3 3v20.58a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3v-29.7l.42-.36h2.76q3.42 0 4.08 3.6 4.38-3.84 8.73-3.84t6.42 2.82a12.17 12.17 0 012.07 7.38z"
/>
<path
d="M57.26 10.75a7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.84 7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.84zM198.26 10.75a7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.84 7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.84z"
fill="currentColor"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -10,7 +10,7 @@
>
<MapMarker />
<span v-if="physicalAddress.locality">
{{ physicalAddress.locality }}
{{ physicalAddress.description }}, {{ physicalAddress.locality }}
</span>
<span v-else>
{{ physicalAddress.description }}

View file

@ -4,7 +4,11 @@
:class="{ small }"
:style="`--small: ${smallStyle}`"
>
<div class="datetime-container-header" />
<div class="datetime-container-header">
<time :datetime="dateObj.toISOString()" class="weekday">{{
weekday
}}</time>
</div>
<div class="datetime-container-content">
<time :datetime="dateObj.toISOString()" class="day block font-semibold">{{
day
@ -12,12 +16,12 @@
<time
:datetime="dateObj.toISOString()"
class="month font-semibold block uppercase py-1 px-0"
>{{ month }}</time
>
>{{ month }}</time>
</div>
</div>
</template>
<script lang="ts" setup>
import { localeShortWeekDayNames } from "@/utils/datetime";
import { computed } from "vue";
const props = withDefaults(
@ -38,6 +42,10 @@ const day = computed<string>(() =>
dateObj.value.toLocaleString(undefined, { day: "numeric" })
);
const weekday = computed<string>(() =>
dateObj.value.toLocaleString(undefined, { weekday: "short" })
);
const smallStyle = computed<string>(() => (props.small ? "1.2" : "2"));
</script>
@ -51,6 +59,13 @@ div.datetime-container {
height: calc(10px * var(--small));
background: #f3425f;
}
.datetime-container-header .weekday
{
font-size: calc(9px * var(--small));
font-weight: bold;
vertical-align: top;
line-height: calc(9px * var(--small));
}
.datetime-container-content {
height: calc(30px * var(--small));
}

View file

@ -3,7 +3,7 @@
class="mbz-card snap-center dark:bg-mbz-purple"
:class="{
'sm:flex sm:items-start': mode === 'row',
'sm:max-w-xs w-[18rem] shrink-0 flex flex-col': mode === 'column',
'w-auto sm:w-[18rem] shrink-0 flex flex-col': mode === 'column',
}"
:to="to"
:isInternal="isInternal"
@ -58,6 +58,16 @@
:date="event.beginsOn.toString()"
/>
</div>
<div
class="-mt-3 h-0 flex mb-3 ltr:ml-0 rtl:mr-0 items-end self-end"
:class="{ 'sm:hidden': mode === 'row' }"
>
<start-time-icon
:small="true"
v-if="!mergedOptions.hideDate"
:date="event.beginsOn.toString()"
/>
</div>
<span
class="text-gray-700 dark:text-white font-semibold hidden"
:class="{ 'sm:block': mode === 'row' }"
@ -161,6 +171,7 @@ import {
organizerAvatarUrl,
} from "@/types/event.model";
import DateCalendarIcon from "@/components/Event/DateCalendarIcon.vue";
import StartTimeIcon from "@/components/Event/StartTimeIcon.vue";
import LazyImageWrapper from "@/components/Image/LazyImageWrapper.vue";
import { EventStatus } from "@/types/enums";
import RouteName from "../../router/name";

View file

@ -21,9 +21,14 @@
/>
<o-autocomplete
:data="addressData"
:required="isRequired"
v-model="queryTextWithDefault"
:placeholder="placeholderWithDefault"
:formatter="(elem: IAddress) => addressFullName(elem)"
:formatter="
(elem?: IAddress) => {
if (elem) addressFullName(elem);
}
"
:debounce="debounceDelay"
@input="asyncData"
:icon="canShowLocateMeButton ? null : 'map-marker'"
@ -64,7 +69,7 @@
</template>
</o-autocomplete>
<o-button
:disabled="!queryTextWithDefault"
:disabled="!selected"
@click="resetAddress"
class="reset-area !h-auto"
icon-left="close"
@ -227,6 +232,7 @@ const props = withDefaults(
resultType?: AddressSearchType;
defaultCoords?: string;
allowManualDetails?: boolean;
isRequired?: boolean;
}>(),
{
defaultCoords: "0;0",
@ -235,6 +241,7 @@ const props = withDefaults(
hideMap: false,
hideSelected: false,
allowManualDetails: false,
isRequired: false,
}
);
@ -500,9 +507,7 @@ const fieldErrors = computed(() => {
</script>
<style lang="scss">
.autocomplete {
.dropdown-menu {
z-index: 2000;
}
z-index: 20;
.dropdown-item.is-disabled {
opacity: 1 !important;

View file

@ -0,0 +1,49 @@
<template>
<div
class="starttime-container flex flex-col rounded-lg text-center justify-center overflow-hidden items-stretch bg-white dark:bg-gray-700 text-violet-3 dark:text-white"
:class="{ small }"
:style="`--small: ${smallStyle}`"
>
<div class="starttime-container-content font-semibold">
<Clock class="clock-icon"/><time :datetime="dateObj.toISOString()">{{
time
}}</time>
</div>
</div>
</template>
<script lang="ts" setup>
import { localeShortWeekDayNames } from "@/utils/datetime";
import { computed } from "vue";
import Clock from "vue-material-design-icons/ClockTimeTenOutline.vue";
const props = withDefaults(
defineProps<{
date: string;
small?: boolean;
}>(),
{ small: false }
);
const dateObj = computed<Date>(() => new Date(props.date));
const time = computed<string>(() =>
dateObj.value.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" })
);
const smallStyle = computed<string>(() => (props.small ? "0.9" : "2"));
</script>
<style lang="scss" scoped>
div.starttime-container {
width: auto;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.2);
padding: 0.25rem 0.25rem;
font-size: calc(1rem * var(--small));
}
.clock-icon {
vertical-align: middle;
padding-right: 0.2rem;
display: inline-block;
}
</style>

View file

@ -0,0 +1,228 @@
<template>
<FullCalendar
ref="calendarRef"
:options="calendarOptions"
class="agenda-view"
/>
<div v-if="listOfEventsByDate.date" class="my-4">
<b v-text="formatDateString(listOfEventsByDate.date)" />
<div v-if="listOfEventsByDate.events.length > 0">
<div
v-for="(event, index) in listOfEventsByDate.events"
v-bind:key="index"
>
<div class="scroll-ml-6 snap-center shrink-0 my-4">
<EventCard :event="event.event.extendedProps.event" />
</div>
</div>
</div>
<EmptyContent v-else icon="calendar" :inline="true">
<span>
{{ t("No events found") }}
</span>
</EmptyContent>
</div>
</template>
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import { locale } from "@/utils/i18n";
import { computed, ref } from "vue";
import { useLazyQuery } from "@vue/apollo-composable";
import { IEvent } from "@/types/event.model";
import { Paginate } from "@/types/paginate";
import { SEARCH_CALENDAR_EVENTS } from "@/graphql/search";
import FullCalendar from "@fullcalendar/vue3";
import { EventSegment } from "@fullcalendar/core";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import {
formatDateISOStringWithoutTime,
formatDateString,
} from "@/filters/datetime";
import EventCard from "../Event/EventCard.vue";
import EmptyContent from "../Utils/EmptyContent.vue";
const { t } = useI18n({ useScope: "global" });
const calendarRef = ref();
const lastSelectedDate = ref<string | undefined>(new Date().toISOString());
const listOfEventsByDate = ref<{ events: EventSegment[]; date?: string }>({
events: [],
date: undefined,
});
const showEventsByDate = (dateStr: string) => {
dateStr = formatDateISOStringWithoutTime(dateStr);
const moreLinkElement = document.querySelectorAll(
`td[data-date='${dateStr}'] a.fc-more-link`
)[0] as undefined | HTMLElement;
if (moreLinkElement) {
moreLinkElement.click();
} else {
listOfEventsByDate.value = {
events: [],
date: dateStr,
};
}
calendarRef.value.getApi().select(dateStr);
};
if (window.location.hash.length) {
lastSelectedDate.value = formatDateISOStringWithoutTime(
window.location.hash.replace("#_", "")
);
} else {
lastSelectedDate.value = formatDateISOStringWithoutTime(
new Date().toISOString()
);
}
const { load: searchEventsLoad, refetch: searchEventsRefetch } = useLazyQuery<{
searchEvents: Paginate<IEvent>;
}>(SEARCH_CALENDAR_EVENTS);
const calendarOptions = computed((): object => {
return {
plugins: [dayGridPlugin, interactionPlugin],
initialView: "dayGridMonth",
initialDate: lastSelectedDate.value,
events: async (
info: { start: Date; end: Date; startStr: string; endStr: string },
successCallback: (arg: object[]) => unknown,
failureCallback: (err: string) => unknown
) => {
const queryVars = {
limit: 999,
beginsOn: info.start,
endsOn: info.end,
};
const result =
(await searchEventsLoad(undefined, queryVars)) ||
(await searchEventsRefetch(queryVars))?.data;
if (!result) {
failureCallback("failed to fetch calendar events");
return;
}
successCallback(
(result.searchEvents.elements ?? []).map((event: IEvent) => {
return {
id: event.id,
title: event.title,
start: event.beginsOn,
end: event.endsOn,
startStr: event.beginsOn,
endStr: event.endsOn,
url: event.url,
extendedProps: {
event: event,
},
};
})
);
},
nextDayThreshold: "09:00:00",
dayMaxEventRows: 0,
moreLinkClassNames: "bg-mbz-yellow dark:bg-mbz-purple dark:text-white",
moreLinkContent: (arg: { num: number; text: string }) => {
return "+" + arg.num.toString();
},
contentHeight: "auto",
eventClassNames: "line-clamp-3 bg-mbz-yellow dark:bg-mbz-purple",
headerToolbar: {
left: "prev,next,customTodayButton",
center: "",
right: "title",
},
locale: locale,
firstDay: 1,
buttonText: {
today: t("Today"),
month: t("Month"),
week: t("Week"),
day: t("Day"),
list: t("List"),
},
customButtons: {
customTodayButton: {
text: t("Today"),
click: () => {
calendarRef.value.getApi().today();
lastSelectedDate.value = formatDateISOStringWithoutTime(
new Date().toISOString()
);
},
},
},
dateClick: (info: { dateStr: string }) => {
showEventsByDate(info.dateStr);
},
moreLinkClick: (info: {
date: Date;
allSegs: EventSegment[];
hiddenSegs: EventSegment[];
jsEvent: object;
}) => {
listOfEventsByDate.value = {
events: info.allSegs,
date: info.date.toISOString(),
};
if (info.allSegs.length) {
window.location.hash =
"_" + formatDateISOStringWithoutTime(info.date.toISOString());
}
return "none";
},
moreLinkDidMount: (arg: { el: Element }) => {
if (
lastSelectedDate.value &&
arg.el.closest(`td[data-date='${lastSelectedDate.value}']`)
) {
showEventsByDate(lastSelectedDate.value);
lastSelectedDate.value = undefined;
}
},
};
});
</script>
<style>
.agenda-view .fc-button {
font-size: 0.8rem !important;
}
.agenda-view .fc-toolbar-title {
font-size: 1rem !important;
}
.agenda-view .fc-daygrid-day-events {
min-height: 1.1rem !important;
margin-bottom: 0.2rem !important;
margin-left: 0.1rem !important;
}
.agenda-view .fc-more-link {
pointer-events: none !important;
}
.clock-icon {
display: inline-block;
vertical-align: middle;
}
.time {
font-size: 0.95rem !important;
}
</style>

View file

@ -0,0 +1,104 @@
<template>
<FullCalendar ref="calendarRef" :options="calendarOptions">
<template v-slot:eventContent="arg">
<span
class="text-violet-3 dark:text-white font-bold m-2"
:title="arg.event.title"
>
{{ arg.event.title }}
</span>
</template>
</FullCalendar>
</template>
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import { locale } from "@/utils/i18n";
import { computed, ref } from "vue";
import { useLazyQuery } from "@vue/apollo-composable";
import { IEvent } from "@/types/event.model";
import { Paginate } from "@/types/paginate";
import { SEARCH_CALENDAR_EVENTS } from "@/graphql/search";
import FullCalendar from "@fullcalendar/vue3";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
const calendarRef = ref();
const { t } = useI18n({ useScope: "global" });
const { load: searchEventsLoad, refetch: searchEventsRefetch } = useLazyQuery<{
searchEvents: Paginate<IEvent>;
}>(SEARCH_CALENDAR_EVENTS);
const calendarOptions = computed((): object => {
return {
plugins: [dayGridPlugin, interactionPlugin],
initialView: "dayGridMonth",
events: async (
info: { start: Date; end: Date; startStr: string; endStr: string },
successCallback: (arg: object[]) => unknown,
failureCallback: (err: string) => unknown
) => {
const queryVars = {
limit: 999,
beginsOn: info.start,
endsOn: info.end,
};
const result =
(await searchEventsLoad(undefined, queryVars)) ||
(await searchEventsRefetch(queryVars))?.data;
if (!result) {
failureCallback("failed to fetch calendar events");
return;
}
successCallback(
(result.searchEvents.elements ?? []).map((event: IEvent) => {
return {
id: event.id,
title: event.title,
start: event.beginsOn,
end: event.endsOn,
startStr: event.beginsOn,
endStr: event.endsOn,
url: `/events/${event.uuid}`,
extendedProps: {
event: event,
},
};
})
);
},
nextDayThreshold: "09:00:00",
dayMaxEventRows: 5,
moreLinkClassNames: "bg-mbz-yellow dark:bg-mbz-purple dark:text-white p-2",
moreLinkContent: (arg: { num: number; text: string }) => {
return "+" + arg.num.toString();
},
eventClassNames: "line-clamp-3 bg-mbz-yellow dark:bg-mbz-purple",
headerToolbar: {
left: "prev,next,today",
center: "title",
right: "dayGridWeek,dayGridMonth", // user can switch between the two
},
locale: locale,
firstDay: 1,
buttonText: {
today: t("Today"),
month: t("Month"),
week: t("Week"),
day: t("Day"),
list: t("List"),
},
};
});
</script>
<style>
.fc-popover-header {
color: black !important;
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<form
id="search-anchor"
class="container mx-auto my-3 flex flex-wrap flex-col sm:flex-row items-stretch gap-2 text-center items-center justify-center dark:text-slate-100"
class="container mx-auto my-3 flex flex-wrap flex-row items-stretch gap-2 text-center items-center justify-center dark:text-slate-100"
role="search"
@submit.prevent="submit"
>
@ -10,7 +10,7 @@
}}</label>
<o-input
v-model="search"
:placeholder="t('Keyword, event title, group name, etc.')"
:placeholder="t('Search')"
id="search_field_input"
autofocus
autocapitalize="off"
@ -19,31 +19,18 @@
maxlength="1024"
expanded
/>
<full-address-auto-complete
:resultType="AddressSearchType.ADMINISTRATIVE"
v-model="location"
:hide-map="true"
:hide-selected="true"
:default-text="locationDefaultText"
labelClass="sr-only"
:placeholder="t('e.g. Nantes, Berlin, Cork, …')"
/>
<o-button native-type="submit" icon-left="magnify">
<template v-if="search">{{ t("Go!") }}</template>
<template v-else>{{ t("Explore!") }}</template>
</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")
);

View file

@ -1,18 +1,6 @@
<template>
<section class="container mx-auto px-2 my-3">
<h1 class="dark:text-white font-bold">
{{ config.slogan ?? t("Gather ⋅ Organize ⋅ Mobilize") }}
</h1>
<i18n-t
keypath="Join {instance}, a Mobilizon instance"
tag="p"
class="dark:text-white"
>
<template #instance>
<b>{{ config.name }}</b>
</template>
</i18n-t>
<p class="dark:text-white mb-2">{{ config.description }}</p>
<p class="dark:text-white mb-2">{{ config.slogan }}</p>
<!-- We don't invite to find other instances yet -->
<!-- <p v-if="!config.registrationsOpen">
{{ t("This instance isn't opened to registrations, but you can register on other instances.") }}
@ -27,12 +15,6 @@
>
<!-- We don't invite to find other instances yet -->
<!-- <o-button v-else variant="link" tag="a" href="https://joinmastodon.org">{{ t('Find an instance') }}</o-button> -->
<router-link
:to="{ name: RouteName.ABOUT }"
class="py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-violet-title focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
>
{{ t("Learn more about {instance}", { instance: config.name }) }}
</router-link>
</div>
</section>
</template>

View file

@ -13,7 +13,7 @@
<!-- Show the real image on the top and fade in after loading -->
<img
ref="image"
class="transition-opacity duration-500 rounded-lg object-cover mx-auto h-full"
class="transition-opacity duration-500 rounded-lg object-cover h-full w-full"
:class="imageOpacity"
alt=""
src=""

View file

@ -19,31 +19,29 @@
</div>
<slot name="subtitle" />
</div>
<div class="" v-show="showScrollLeftButton">
<!-- <div class="hidden sm:block" v-show="showScrollLeftButton">
<button
@click="scrollLeft"
class="absolute inset-y-0 my-auto z-10 rounded-full bg-white dark:bg-transparent w-10 h-10 border border-shadowColor -left-5 ml-2"
>
<span class="">&lt;</span>
</button>
</div>
</div> -->
<div class="overflow-hidden">
<div
class="relative w-full snap-x snap-always snap-mandatory overflow-x-auto flex pb-6 gap-x-5 gap-y-8 p-1"
ref="scrollContainer"
@scroll="scrollHandler"
class="multi-card-event"
>
<slot name="content" />
</div>
</div>
<div class="" v-show="showScrollRightButton">
<!-- <div class="hidden sm:block" v-show="showScrollRightButton">
<button
@click="scrollRight"
class="absolute inset-y-0 my-auto z-10 rounded-full bg-white dark:bg-transparent w-10 h-10 border border-shadowColor -right-5 mr-2"
>
<span class="">&gt;</span>
</button>
</div>
</div> -->
</div>
</template>
@ -63,56 +61,68 @@ const emit = defineEmits(["doGeoLoc"]);
const { t } = useI18n({ useScope: "global" });
const showScrollRightButton = ref(false);
const showScrollLeftButton = ref(false);
// const showScrollRightButton = ref(true);
// const showScrollLeftButton = ref(false);
const scrollContainer = ref<any>();
// const scrollContainer = ref<any>();
const scrollHandler = () => {
if (scrollContainer.value) {
showScrollRightButton.value =
scrollContainer.value.scrollLeft <
scrollContainer.value.scrollWidth - scrollContainer.value.clientWidth;
showScrollLeftButton.value = scrollContainer.value.scrollLeft > 0;
}
};
// const scrollHandler = () => {
// if (scrollContainer.value) {
// showScrollRightButton.value =
// scrollContainer.value.scrollLeft <
// scrollContainer.value.scrollWidth - scrollContainer.value.clientWidth;
// showScrollLeftButton.value = scrollContainer.value.scrollLeft > 0;
// }
// };
const doScroll = (e: Event, left: number) => {
e.preventDefault();
if (scrollContainer.value) {
scrollContainer.value.scrollBy({
left,
behavior: "smooth",
});
}
};
// const doScroll = (e: Event, left: number) => {
// e.preventDefault();
// if (scrollContainer.value) {
// scrollContainer.value.scrollBy({
// left,
// behavior: "smooth",
// });
// }
// };
const scrollLeft = (e: Event) => {
doScroll(e, -300);
};
// const scrollLeft = (e: Event) => {
// doScroll(e, -300);
// };
const scrollRight = (e: Event) => {
doScroll(e, 300);
};
// const scrollRight = (e: Event) => {
// doScroll(e, 300);
// };
const scrollHorizontalToVertical = (evt: WheelEvent) => {
evt.deltaY > 0 ? doScroll(evt, 300) : doScroll(evt, -300);
};
// const scrollHorizontalToVertical = (evt: WheelEvent) => {
// evt.deltaY > 0 ? doScroll(evt, 300) : doScroll(evt, -300);
// };
onMounted(async () => {
// Make sure everything is mounted properly
setTimeout(() => {
scrollHandler();
}, 1500);
scrollContainer.value.addEventListener("wheel", scrollHorizontalToVertical);
});
// onMounted(async () => {
// scrollContainer.value.addEventListener("wheel", scrollHorizontalToVertical);
// });
onUnmounted(() => {
if (scrollContainer.value) {
scrollContainer.value.removeEventListener(
"wheel",
scrollHorizontalToVertical
);
}
});
// onUnmounted(() => {
// if (scrollContainer.value) {
// scrollContainer.value.removeEventListener(
// "wheel",
// scrollHorizontalToVertical
// );
// }
// });
</script>
<style lang="scss" scoped>
.multi-card-event {
display: grid;
grid-auto-rows: 1fr;
grid-column-gap: 20px;
grid-row-gap: 30px;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
.event-card {
height: 100%;
display: flex;
flex-direction: column;
}
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<router-link
:to="to"
class="mbz-card flex flex-col items-center dark:border-gray-700 shadow-md md:flex-col snap-center shrink-0 first:pl-8 w-[18rem] dark:bg-mbz-purple"
class="mbz-card flex flex-col items-center dark:border-gray-700 shadow-md md:flex-col snap-center shrink-0 first:pl-8 w-auto sm:w-[18rem] dark:bg-mbz-purple"
>
<div class="relative w-full group">
<img

View file

@ -0,0 +1,79 @@
<template>
<close-content
class="container mx-auto px-2"
v-show="loadingEvents || (events && events.total > 0)"
:suggestGeoloc="false"
v-on="attrs"
>
<template #title>
{{ t("Upcoming events") }}
</template>
<template #subtitle>
<i18n-t
class="text-slate-700 dark:text-slate-300"
tag="p"
keypath="On {instance} and other federated instances"
>
<template #instance>
<b>{{ instanceName }}</b>
</template>
</i18n-t>
</template>
<template #content>
<skeleton-event-result
v-for="i in 6"
class="scroll-ml-6 snap-center shrink-0 w-[18rem] my-4"
:key="i"
v-show="loadingEvents"
/>
<event-card
v-for="event in events.elements"
:event="event"
:key="event.uuid"
/>
<!--<more-content
:to="{
name: RouteName.SEARCH,
query: {
contentType: 'EVENTS',
},
}"
>
{{ t("View more events") }}
</more-content>-->
</template>
</close-content>
</template>
<script lang="ts" setup>
import MoreContent from "./MoreContent.vue";
import CloseContent from "./CloseContent.vue";
import { computed, useAttrs } from "vue";
import { IEvent } from "@/types/event.model";
import { useQuery } from "@vue/apollo-composable";
import EventCard from "../Event/EventCard.vue";
import { Paginate } from "@/types/paginate";
import SkeletonEventResult from "../Event/SkeletonEventResult.vue";
import { EventSortField, SortDirection } from "@/types/enums";
import { FETCH_EVENTS } from "@/graphql/event";
import { useI18n } from "vue-i18n";
import RouteName from "@/router/name";
defineProps<{
instanceName: string;
}>();
const { t } = useI18n({ useScope: "global" });
const attrs = useAttrs();
const { result: resultEvents, loading: loadingEvents } = useQuery<{
events: Paginate<IEvent>;
}>(FETCH_EVENTS, {
orderBy: EventSortField.BEGINS_ON,
direction: SortDirection.ASC,
limit: 99
});
const events = computed(
() => resultEvents.value?.events ?? { total: 0, elements: [] }
);
</script>

View file

@ -1,38 +1,13 @@
<template>
<svg
class="bg-white dark:bg-zinc-900 dark:fill-white"
:class="{ 'bg-gray-900': invert }"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 248.16 46.78"
>
<title>Mobilizon Logo</title>
<g data-name="header">
<path
d="M0 45.82l3.18-40.8a29.88 29.88 0 015.07-.36 27.74 27.74 0 014.95.36l4.86 17.16a92.19 92.19 0 012.34 10.08h.36a92.19 92.19 0 012.34-10.08L28 5.02a29.23 29.23 0 015-.36 29.23 29.23 0 015 .36l3.18 40.8a13.61 13.61 0 01-3.63.42 23.41 23.41 0 01-3.63-.24l-1.2-19.92q-.36-5.52-.48-12.84h-.44l-7.32 26.51a25.62 25.62 0 01-4 .3 23.36 23.36 0 01-3.84-.3L9.36 13.24H9q-.3 8.94-.48 12.84L7.26 46a22.47 22.47 0 01-3.6.24A13.75 13.75 0 010 45.82zM74 31.06q0 8-4.26 12.3a12.21 12.21 0 01-9 3.42 12.21 12.21 0 01-9-3.42q-4.26-4.26-4.26-12.3t4.24-12.31a12.21 12.21 0 019-3.42 12.21 12.21 0 019 3.42Q74 23.02 74 31.06zM60.75 20.98q-5.67 0-5.67 10.08t5.67 10.08q5.67 0 5.67-10.08t-5.67-10.08zM103.2 19.75q2.7 4.11 2.7 11.28T102 42.31a13.18 13.18 0 01-10 4.11 31.41 31.41 0 01-11.34-2V2.2l.4-.45h2.76A4 4 0 0187 2.83a5.38 5.38 0 01.93 3.57v11.94a12.08 12.08 0 017.56-2.7 8.71 8.71 0 017.71 4.11zm-9.72 2a7.28 7.28 0 00-5.58 2.82v16a15 15 0 004.08.54 5.25 5.25 0 004.68-2.67q1.68-2.67 1.68-7.59 0-9.03-4.86-9.1zM121 22v23.94a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3V24.75q0-3.24-2.7-3.24h-.72a9.32 9.32 0 01-.3-2.58 10.7 10.7 0 01.3-2.7 39.63 39.63 0 014.38-.24h1a5.19 5.19 0 014 1.62A6.27 6.27 0 01121 22z"
/>
<path
d="M119.82.84a7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.93 7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.93z"
fill="currentColor"
/>
<path
d="M139.08 40.42h2a10.23 10.23 0 01.6 3.18 9.24 9.24 0 01-.18 2.1 38.47 38.47 0 01-5.64.54q-6.48 0-6.48-7v-37l.36-.42h2.88a3.94 3.94 0 013.12 1.05 5.52 5.52 0 01.9 3.57v31.31q-.02 2.67 2.44 2.67zM155.94 22v23.94a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3V24.75q0-3.24-2.7-3.24h-.72a9.32 9.32 0 01-.3-2.58 10.7 10.7 0 01.3-2.7 39.63 39.63 0 014.38-.24h1a5.19 5.19 0 014.05 1.62 6.27 6.27 0 011.43 4.39z"
/>
<path
d="M154.8 2.84a7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.93 7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.93z"
fill="currentColor"
/>
<path
d="M163.08 39.22l8.76-11.82q1.32-1.8 4.8-5.7l-.18-.3a63.09 63.09 0 01-7.74.42H163a9.79 9.79 0 01-.24-2.34 15.8 15.8 0 01.42-3.3h20.4a16.31 16.31 0 011 4.26 4.1 4.1 0 01-.78 2.34L175 34.66a64.65 64.65 0 01-4.56 5.7l.18.24q3.12-.3 5.22-.3h2.58a15.35 15.35 0 006.12-.9 9.4 9.4 0 01.72 3.12q0 3.42-4.32 3.42h-18a14.27 14.27 0 01-.9-3.93 5.08 5.08 0 011.04-2.79zM215.88 31.06q0 8-4.26 12.3a13.63 13.63 0 01-18.06 0q-4.26-4.26-4.26-12.3t4.26-12.31a13.63 13.63 0 0118.06 0q4.26 4.27 4.26 12.31zm-13.29-10.08q-5.67 0-5.67 10.08t5.67 10.08q5.67 0 5.67-10.08t-5.67-10.08zM247 25.84v13.32a11 11 0 001.2 5.64 7 7 0 01-4.41 1.56q-2.43 0-3.33-1.14a5.69 5.69 0 01-.9-3.54V27.4a7.74 7.74 0 00-.72-3.87 2.78 2.78 0 00-2.58-1.17 8.62 8.62 0 00-6.3 3v20.58a20.85 20.85 0 01-3.66.3 23 23 0 01-3.78-.3v-29.7l.42-.36h2.76q3.42 0 4.08 3.6 4.38-3.84 8.73-3.84t6.42 2.82a12.17 12.17 0 012.07 7.38z"
/>
<path
d="M57.26 10.75a7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.84 7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.84zM198.26 10.75a7.37 7.37 0 01-.6-3 7.37 7.37 0 01.6-3 8.09 8.09 0 013.87-.84 7.05 7.05 0 013.69.84 7.37 7.37 0 01.6 3 7.37 7.37 0 01-.6 3 7.46 7.46 0 01-3.87.84 6.49 6.49 0 01-3.69-.84z"
fill="currentColor"
/>
</g>
</svg>
<figure>
<svgLogo/>
</figure>
</template>
<script lang="ts" setup>
import svgLogo from "../assets/logo.svg?component";
withDefaults(
defineProps<{
invert?: boolean;

View file

@ -13,25 +13,9 @@
>
<MobilizonLogo class="w-40" />
</router-link>
<div
class="flex items-center md:order-2 ml-auto gap-2"
v-if="currentActor?.id"
>
<router-link
:to="{ name: RouteName.CONVERSATION_LIST }"
class="flex sm:mr-3 text-sm md:mr-0 relative"
id="conversations-menu-button"
aria-expanded="false"
>
<span class="sr-only">{{ t("Open conversations") }}</span>
<Inbox :size="32" />
<span
v-show="unreadConversationsCount > 0"
class="absolute bottom-0.5 -left-2 bg-primary rounded-full inline-block h-3 w-3 mx-2"
>
</span>
</router-link>
<o-dropdown position="bottom-left">
<div class="flex items-center md:order-2 ml-auto" v-if="currentActor?.id">
<o-dropdown position="bottom-right">
<template #trigger>
<button
type="button"
@ -181,34 +165,55 @@
<ul
class="flex flex-col md:flex-row md:space-x-8 mt-2 md:mt-0 md:font-lightbold"
>
<li v-if="currentActor?.id">
<search-fields
v-if="showMobileMenu"
class="m-auto w-auto"
v-model:search="search"
v-model:location="location"
/>
<li class="m-auto">
<router-link
:to="{ name: RouteName.EVENT_CALENDAR }"
class="block py-2 pr-4 pl-3 text-zinc-700 border-b border-gray-100 hover:bg-zinc-50 md:hover:bg-transparent md:border-0 md:hover:text-mbz-purple-700 md:p-0 dark:text-zinc-400 md:dark:hover:text-white dark:hover:bg-zinc-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
>{{ t("Calendar") }}</router-link
>
</li>
<li class="m-auto" v-if="currentActor?.id">
<router-link
:to="{ name: RouteName.MY_EVENTS }"
class="block py-2 pr-4 pl-3 text-zinc-700 border-b border-gray-100 hover:bg-zinc-50 md:hover:bg-transparent md:border-0 md:hover:text-mbz-purple-700 md:p-0 dark:text-zinc-400 md:dark:hover:text-white dark:hover:bg-zinc-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
>{{ t("My events") }}</router-link
>
</li>
<li v-if="currentActor?.id">
<li class="m-auto" v-if="currentActor?.id">
<router-link
:to="{ name: RouteName.MY_GROUPS }"
class="block py-2 pr-4 pl-3 text-zinc-700 border-b border-gray-100 hover:bg-zinc-50 md:hover:bg-transparent md:border-0 md:hover:text-mbz-purple-700 md:p-0 dark:text-zinc-400 md:dark:hover:text-white dark:hover:bg-zinc-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
>{{ t("My groups") }}</router-link
>
</li>
<li v-if="!currentActor?.id">
<li class="m-auto" v-if="!currentActor?.id">
<router-link
:to="{ name: RouteName.LOGIN }"
class="block py-2 pr-4 pl-3 text-zinc-700 border-b border-gray-100 hover:bg-zinc-50 md:hover:bg-transparent md:border-0 md:hover:text-mbz-purple-700 md:p-0 dark:text-zinc-400 md:dark:hover:text-white dark:hover:bg-zinc-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
>{{ t("Login") }}</router-link
>
</li>
<li v-if="!currentActor?.id && canRegister">
<li class="m-auto" v-if="!currentActor?.id && canRegister">
<router-link
:to="{ name: RouteName.REGISTER }"
class="block py-2 pr-4 pl-3 text-zinc-700 border-b border-gray-100 hover:bg-zinc-50 md:hover:bg-transparent md:border-0 md:hover:text-mbz-purple-700 md:p-0 dark:text-zinc-400 md:dark:hover:text-white dark:hover:bg-zinc-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
>{{ t("Register") }}</router-link
>
</li>
<search-fields
v-if="!showMobileMenu"
class="m-auto w-auto"
v-model:search="search"
v-model:location="location"
/>
</ul>
</div>
</div>
@ -219,28 +224,23 @@
import MobilizonLogo from "@/components/MobilizonLogo.vue";
import { ICurrentUserRole } from "@/types/enums";
import { logout } from "../utils/auth";
import { IPerson, displayName } from "../types/actor";
import { displayName } from "../types/actor";
import RouteName from "../router/name";
import { computed, onMounted, ref, watch } from "vue";
import { computed, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Inbox from "vue-material-design-icons/Inbox.vue";
import { useCurrentUserClient } from "@/composition/apollo/user";
import {
useCurrentActorClient,
useCurrentUserIdentities,
} from "@/composition/apollo/actor";
import { useLazyQuery, useMutation } from "@vue/apollo-composable";
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 { useOruga } from "@oruga-ui/oruga-next";
import {
UNREAD_ACTOR_CONVERSATIONS,
UNREAD_ACTOR_CONVERSATIONS_SUBSCRIPTION,
} from "@/graphql/user";
import { ICurrentUser } from "@/types/current-user.model";
import SearchFields from "@/components/Home/SearchFields.vue";
const { currentUser } = useCurrentUserClient();
const { currentActor } = useCurrentActorClient();
@ -262,60 +262,8 @@ const canRegister = computed(() => {
const { t } = useI18n({ useScope: "global" });
const unreadConversationsCount = computed(
() =>
unreadActorConversationsResult.value?.loggedUser.defaultActor
?.unreadConversationsCount ?? 0
);
const {
result: unreadActorConversationsResult,
load: loadUnreadConversations,
subscribeToMore,
} = useLazyQuery<{
loggedUser: Pick<ICurrentUser, "id" | "defaultActor">;
}>(UNREAD_ACTOR_CONVERSATIONS);
watch(currentActor, async (currentActorValue, previousActorValue) => {
if (
currentActorValue?.id &&
currentActorValue.preferredUsername !==
previousActorValue?.preferredUsername
) {
await loadUnreadConversations();
subscribeToMore<
{ personId: string },
{ personUnreadConversationsCount: number }
>({
document: UNREAD_ACTOR_CONVERSATIONS_SUBSCRIPTION,
variables: {
personId: currentActor.value?.id as string,
},
updateQuery: (previousResult, { subscriptionData }) => {
console.debug(
"Updating actor unread conversations count query after subscribe to more update",
subscriptionData?.data?.personUnreadConversationsCount
);
return {
...previousResult,
loggedUser: {
id: previousResult.loggedUser.id,
defaultActor: {
...previousResult.loggedUser.defaultActor,
unreadConversationsCount:
subscriptionData?.data?.personUnreadConversationsCount ??
previousResult.loggedUser.defaultActor
?.unreadConversationsCount,
} as IPerson, // no idea why,
},
};
},
});
}
});
onMounted(() => {});
const location = ref(null);
const search = ref("");
watch(identities, () => {
// If we don't have any identities, the user has validated their account,

View file

@ -1,21 +1,8 @@
<template>
<footer
class="bg-violet-2 color-secondary flex flex-col items-center py-2 px-3"
class="bg-zinc-900 color-secondary flex flex-col items-center py-2 px-3"
ref="footer"
>
<picture class="flex max-w-xl">
<source
:srcset="`/img/pics/footer_${random}-1024w.webp 1x, /img/pics/footer_${random}-1920w.webp 2x`"
type="image/webp"
/>
<img
:src="`/img/pics/footer_${random}-1024w.webp`"
alt=""
width="1024"
height="428"
loading="lazy"
/>
</picture>
<ul
class="inline-flex flex-wrap justify-around gap-3 text-lg text-white underline decoration-yellow-1"
>

View file

@ -4,6 +4,10 @@ function parseDateTime(value: string): Date {
return new Date(value);
}
function formatDateISOStringWithoutTime(value: string): string {
return parseDateTime(value).toISOString().split("T")[0];
}
function formatDateString(value: string): string {
return parseDateTime(value).toLocaleString(locale(), {
weekday: "long",
@ -76,4 +80,9 @@ function formatDateTimeString(
const locale = () => i18n.global.locale.replace("_", "-");
export { formatDateString, formatTimeString, formatDateTimeString };
export {
formatDateISOStringWithoutTime,
formatDateString,
formatTimeString,
formatDateTimeString,
};

View file

@ -201,6 +201,56 @@ export const SEARCH_EVENTS = gql`
${ACTOR_FRAGMENT}
`;
export const SEARCH_CALENDAR_EVENTS = gql`
query SearchEvents(
$beginsOn: DateTime
$endsOn: DateTime
$eventPage: Int
$limit: Int
) {
searchEvents(
beginsOn: $beginsOn
endsOn: $endsOn
page: $eventPage
limit: $limit
) {
total
elements {
id
title
uuid
beginsOn
endsOn
picture {
id
url
}
status
tags {
...TagFragment
}
physicalAddress {
...AdressFragment
}
organizerActor {
...ActorFragment
}
attributedTo {
...ActorFragment
}
options {
...EventOptions
}
__typename
}
}
}
${EVENT_OPTIONS_FRAGMENT}
${TAG_FRAGMENT}
${ADDRESS_FRAGMENT}
${ACTOR_FRAGMENT}
`;
export const SEARCH_GROUPS = gql`
query SearchGroups(
$location: String

View file

@ -54,7 +54,7 @@
"Access group members": "Gruppenmitglieder ansehen",
"Access group todo-lists": "Gruppen-To-do-Liste ansehen",
"Access organized events": "Organisierte Veranstaltungen ansehen",
"Access participations": "Teilnehmer ansehen",
"Access participations": "Teilnehmer:innen ansehen",
"Accessibility": "Barrierefreiheit",
"Accessible only by link": "Erreichbar nur über einen Link",
"Accessible only to members": "Nur für Mitglieder zugänglich",
@ -67,7 +67,7 @@
"Activated": "Aktiviert",
"Active": "Aktiv",
"Activity": "Ereignisse",
"Actor": "Akteur*in",
"Actor": "Akteur:in",
"Adapt to system theme": "An das Thema des Systems anpassen",
"Add": "Hinzufügen",
"Add / Remove…": "Hinzufügen/Entfernen …",
@ -83,17 +83,17 @@
"Add some tags": "Schlagworte ergänzen",
"Add to my calendar": "Zu meinem Kalender hinzufügen",
"Additional comments": "Weitere Kommentare",
"Admin": "Administrator",
"Admin": "Administrator:in",
"Admin dashboard": "Übersichtsseite der Administration",
"Admin settings": "Admineinstellungen",
"Admin settings successfully saved.": "Admineinstellungen erfolgreich gespeichert.",
"Administration": "Administration",
"Administrator": "Administrator",
"Administrator": "Administrator:in",
"All": "Alle",
"All activities": "Alle Ereignisse",
"All good, let's continue!": "Das passt, weiter gehts!",
"All the places have already been taken": "Alle Plätze sind schon vergeben",
"Allow all comments from users with accounts": "Erlaube alle Kommentare von angemeldeten Benutzern",
"Allow all comments from users with accounts": "Erlaube alle Kommentare von angemeldeten Benutzer:innen",
"Allow registrations": "Erlaube Registrierungen",
"An URL to an external ticketing platform": "Eine Webadresse zu einer externen Ticketplattform",
"An error has occured while refreshing the page.": "Es trat ein Fehler auf, während die Seite neu geladen wurde.",
@ -106,27 +106,27 @@
"An event I'm organizing has a new pending participation": "Eine Veranstaltung die ich organisiere, hat eine ausstehende Teilnahmeanfrage",
"An event from one of my groups has been published": "Eine Veranstaltung einer meiner Gruppen wurde veröffentlicht",
"An event from one of my groups has been updated or deleted": "Eine Veranstaltung einer meiner Gruppen wurde aktualisiert oder gelöscht",
"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.": "Als Instanz bezeichnen wir eine Installation der Mobilizon-Software auf einem Server. Eine Instanz kann von jedem mithilfe der {mobilizon_software} oder anderer kompatibler Software betrieben werden. Der Name dieser Instanz lautet „{instance_name}“. Diese Instanz ist Teil des „Fediverse“, einem Netzwerk aus vielen verbundenen Instanzen, die alle miteinander kommunizieren können. Nutzer von verschiedenen Instanzen können so genau wie beim E-Mail-System miteinander kommunizieren. Auch wenn Sie Ihr Konto bei einer anderen Instanzen registriert haben.",
"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.": "Als Instanz bezeichnen wir eine Installation der Mobilizon-Software auf einem Server. Eine Instanz kann von jedem mithilfe der {mobilizon_software} oder anderer kompatibler Software betrieben werden. Der Name dieser Instanz lautet „{instance_name}“. Diese Instanz ist Teil des „Fediverse“, einem Netzwerk aus vielen verbundenen Instanzen, die alle miteinander kommunizieren können. Nutzer:innen von verschiedenen Instanzen können so genau wie beim E-Mail-System miteinander kommunizieren. Auch wenn Sie Ihr Konto bei einer anderen Instanzen registriert haben.",
"And {number} comments": "Und {number} Kommentare",
"Announcements and mentions notifications are always sent straight away.": "Benachrichtigungen zu Ankündigungen und Erwähnungen werden immer sofort verschickt.",
"Anonymous participant": "Anonymer Teilnehmer",
"Anonymous participants will be asked to confirm their participation through e-mail.": "Anonyme Teilnehmer werden gebeten, ihre Teilnahme per E-Mail zu bestätigen.",
"Anonymous participant": "Anonyme Teilnehmer:in",
"Anonymous participants will be asked to confirm their participation through e-mail.": "Anonyme Teilnehmer:innen werden gebeten, ihre Teilnahme per E-Mail zu bestätigen.",
"Anonymous participations": "Anonyme Teilnahme",
"Any category": "Jede Kategorie",
"Any day": "Egal wann",
"Any distance": "Beliebige Entfernung",
"Any type": "Jeder Typ",
"Anyone can join freely": "Jeder kann frei beitreten",
"Anyone can request being a member, but an administrator needs to approve the membership.": "Jeder kann einen Antrag auf Mitgliedschaft stellen, aber ein Administrator muss die Mitgliedschaft genehmigen.",
"Anyone wanting to be a member from your group will be able to from your group page.": "Jeder, der ein Mitglied Ihrer Gruppe werden möchte, kann dies von Ihrer Gruppenseite aus tun.",
"Anyone can request being a member, but an administrator needs to approve the membership.": "Jeder kann einen Antrag auf Mitgliedschaft stellen, aber eine Administrator:in muss die Mitgliedschaft genehmigen.",
"Anyone wanting to be a member from your group will be able to from your group page.": "Jede, der/die ein Mitglied Ihrer Gruppe werden möchte, kann dies von Ihrer Gruppenseite aus tun.",
"Application": "Anwendung",
"Apply filters": "Filter anwenden",
"Approve member": "Mitglied zulassen",
"Apps": "Apps",
"Are you really sure you want to delete your whole account? You'll lose everything. Identities, settings, events created, messages and participations will be gone forever.": "Sind Sie sicher, dass Sie Ihr Benutzerkonto komplett löschen möchten? Sie verlieren dadurch alles: Identitäten, Einstellungen, erstellte Veranstaltungen, Nachrichten und Veranstaltungsteilnahmen. Alles wird nach Bestätigung gelöscht.",
"Are you really sure you want to delete your whole account? You'll lose everything. Identities, settings, events created, messages and participations will be gone forever.": "Sind Sie sicher, dass Sie Ihr Benutzer:innenkonto komplett löschen möchten? Sie verlieren dadurch alles: Identitäten, Einstellungen, erstellte Veranstaltungen, Nachrichten und Veranstaltungsteilnahmen. Alles wird nach Bestätigung gelöscht.",
"Are you sure you want to <b>completely delete</b> this group? All members - including remote ones - will be notified and removed from the group, and <b>all of the group data (events, posts, discussions, todos…) will be irretrievably destroyed</b>.": "Möchten Sie diese Gruppe wirklich <b>löschen</b>? Alle Mitglieder einschließlich externe werden benachrichtigt und aus der Gruppe entfernt. Es werden <b>alle Gruppendaten (Veranstaltungen, Beiträge, Diskussionen, Aufgaben, …) irreversibel gelöscht</b>.",
"Are you sure you want to <b>delete</b> this comment? This action cannot be undone.": "Sind Sie sicher, dass Sie diesen Kommentar <b>löschen</b> wollen? Diese Aktion kann nicht rückgängig gemacht werden.",
"Are you sure you want to <b>delete</b> this event? This action cannot be undone. You may want to engage the discussion with the event creator or edit its event instead.": "Sind Sie sicher, diese Veranstaltung <b>löschen</b> zu wollen? Dies kann nicht rückgängig gemacht werden. Sie könnten sich stattdessen mit dem Veranstalter austauschen oder die Veranstaltung bearbeiten.",
"Are you sure you want to <b>delete</b> this event? This action cannot be undone. You may want to engage the discussion with the event creator or edit its event instead.": "Sind Sie sicher, diese Veranstaltung <b>löschen</b> zu wollen? Dies kann nicht rückgängig gemacht werden. Sie könnten sich stattdessen mit dem Veranstalter:in austauschen oder die Veranstaltung bearbeiten.",
"Are you sure you want to <b>suspend</b> this group? All members - including remote ones - will be notified and removed from the group, and <b>all of the group data (events, posts, discussions, todos…) will be irretrievably destroyed</b>.": "Möchten Sie diese Gruppe wirklich <b>auflösen</b>? Alle Mitglieder einschließlich externe werden benachrichtigt und aus der Gruppe entfernt. Es werden <b>alle Gruppendaten (Veranstaltungen, Beiträge, Diskussionen, Aufgaben, …) irreversibel gelöscht</b>.",
"Are you sure you want to <b>suspend</b> this group? As this group originates from instance {instance}, this will only remove local members and delete the local data, as well as rejecting all the future data.": "Möchten Sie diese Gruppe wirklich <b>auflösen</b>? Da diese Gruppe von einer anderen Instanz ({instance}) stammt, werden nur lokale Mitglieder und Daten entfernt und zukünftige Daten abgelehnt.",
"Are you sure you want to cancel the event creation? You'll lose all modifications.": "Sind Sie sich sicher, dass Sie das Erstellen der Veranstaltung abbrechen möchten? Alle Änderungen werden verloren gehen.",
@ -137,7 +137,7 @@
"Are you sure you want to delete this post? This action cannot be reverted.": "Bist du sicher, dass du diesen Beitrag löschen willst? Das kann nicht rückgängig gemacht werden.",
"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.": "Sind Sie sicher, dass Sie die Gruppe {groupName} verlassen möchten? Sie verlieren den Zugang zu den privaten Inhalten dieser Gruppe. Diese Aktion kann nicht rückgängig gemacht werden.",
"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.": "Da der Veranstalter sich entschieden hat, die Teilnahmeanfragen manuell zu validieren, wird Ihre Teilnahme erst dann wirklich bestätigt, wenn Sie eine E-Mail erhalten, in der die Annahme bestätigt wird.",
"Ask your instance admin to {enable_feature}.": "Bitten Sie Ihren Instanzadministrator um {enable_feature}.",
"Ask your instance admin to {enable_feature}.": "Bitten Sie Ihre Instanzadministrator:in um {enable_feature}.",
"Assigned to": "Zugewiesen an",
"Atom feed for events and posts": "Atom-Feed mit Veranstaltungen und Beiträgen",
"Attending": "Teilnahme",
@ -168,6 +168,7 @@
"By transit": "Mit öffentlichen Verkehrsmitteln",
"By {group}": "Von {group}",
"By {username}": "Von {username}",
"Calendar": "Kalender",
"Can be an email or a link, or just plain text.": "Dies kann eine E-Mail-Adresse oder ein Link sein. Oder einfach ein Freitext.",
"Cancel": "Abbrechen",
"Cancel anonymous participation": "Anonyme Teilnahme stornieren",
@ -192,8 +193,8 @@
"Change role": "Rolle ändern",
"Change the filters.": "Filter ändern.",
"Change timezone": "Zeitzone ändern",
"Change user email": "Benutzer-E-Mail-Adresse ändern",
"Change user role": "Benutzerrolle ändern",
"Change user email": "Benutzer:innen- E-Mail-Adresse ändern",
"Change user role": "Benutzer:innen-Rolle ändern",
"Check your inbox (and your junk mail folder).": "Prüfen Sie Ihren Posteingang (und den Spamordner).",
"Choose the source of the instance's Privacy Policy": "Wählen Sie die Quelle der Instanz-Datenschutzrichtlinie",
"Choose the source of the instance's Terms": "Wählen Sie die Quelle der Instanz-Bedingungen",
@ -201,8 +202,8 @@
"Clear": "Leeren",
"Clear address field": "Adressfeld leeren",
"Clear date filter field": "Datumsfilterfeld löschen",
"Clear participation data for all events": "Übersichtliche Teilnehmerdaten für alle Veranstaltungen",
"Clear participation data for this event": "Übersichtliche Teilnehmerdaten für diese Veranstaltung",
"Clear participation data for all events": "Übersichtliche Teilnehmer:innen-daten für alle Veranstaltungen",
"Clear participation data for this event": "Übersichtliche Teilnehmer:innen-daten für diese Veranstaltung",
"Clear timezone field": "Zeitzonenfeld leeren",
"Click for more information": "Klicken Sie hier für mehr Informationen",
"Click to upload": "Zum Hochladen hier klicken",
@ -220,7 +221,7 @@
"Confirm my participation": "Meine Teilnahme bestätigen",
"Confirm my particpation": "Bestätige meine Teilnahme",
"Confirm participation": "Teilnahme bestätigen",
"Confirm user": "Benutzer bestätigen",
"Confirm user": "Benutzer:in bestätigen",
"Confirmed": "Bestätigt",
"Confirmed at": "Bestätigt um",
"Confirmed: Will happen": "Bestätigt: Wird stattfinden",
@ -313,11 +314,11 @@
"Discussions list": "Liste der Diskussionen",
"Display name": "Anzeigename",
"Display participation price": "Teilnahmegebühr anzeigen",
"Displayed nickname": "Angezeigter Nutzername",
"Displayed nickname": "Angezeigter Nutzer:innen-Name",
"Displayed on homepage and meta tags. Describe what Mobilizon is and what makes this instance special in a single paragraph.": "Wird auf der Startseite und in den Meta-Tags angezeigt. Beschreiben Sie in einem Absatz was Mobilizon ist und was diese Instanz besonders macht.",
"Distance": "Entfernung",
"Do not receive any mail": "Keine E-Mails erhalten",
"Do you really want to suspend this account? All of the user's profiles will be deleted.": "Wollen Sie dieses Konto wirklich sperren? Alle Profile des Benutzers werden gelöscht.",
"Do you really want to suspend this account? All of the user's profiles will be deleted.": "Wollen Sie dieses Konto wirklich sperren? Alle Profile der Benutzer:in werden gelöscht.",
"Do you wish to {create_event} or {explore_events}?": "Möchten Sie ein {create_event} oder {explore_events}?",
"Do you wish to {create_group} or {explore_groups}?": "Möchten Sie eine {create_group} oder {explore_groups}?",
"Does the event needs to be confirmed later or is it cancelled?": "Muss die Veranstaltung später noch bestätigt werden oder wurde sie abgesagt?",
@ -329,7 +330,7 @@
"Edit": "Bearbeiten",
"Edit post": "Beitrag bearbeiten",
"Edit profile {profile}": "Profil {profile} bearbeiten",
"Edit user email": "Benutzer-E-Mail-Adresse bearbeiten",
"Edit user email": "Benutzer:innen-E-Mail-Adresse bearbeiten",
"Edited {ago}": "Editiert {ago}",
"Edited {relative_time} ago": "Vor {relative_time} bearbeitet",
"Eg: Stockholm, Dance, Chess…": "z. B.: Berlin, Tanzen, Schach …",
@ -414,7 +415,7 @@
"Follow a new instance": "Neuen Instanz folgen",
"Follow instance": "Der Instanz folgen",
"Follow request pending approval": "Ihre Folgeanfrage wartet auf Bestätigung",
"Follow requests will be approved by a group moderator": "Folgeanfragen werden von einem Gruppenmoderator genehmigt",
"Follow requests will be approved by a group moderator": "Folgeanfragen werden von einer Gruppenmoderator:in genehmigt",
"Follow status": "Status verfolgen",
"Followed": "Gefolgt von",
"Followed, pending response": "Gefolgt, Antwort steht noch aus",
@ -478,7 +479,7 @@
"Hide filters": "Filter verstecken",
"Hide replies": "Antworten ausblenden",
"Home": "Home",
"Home to {number} users": "Zuhause von {number} Nutzern",
"Home to {number} users": "Zuhause von {number} Nutzer:innen",
"Homepage": "Website",
"Hourly email summary": "Stündliche E-Mail-Zusammenfassungen",
"I agree to the {instanceRules} and {termsOfService}": "Ich stimme den {instanceRules} und den {termsOfService} zu",
@ -500,12 +501,12 @@
"Identity {displayName} created": "Identität {displayName} erstellt",
"Identity {displayName} deleted": "Identität {displayName} gelöscht",
"Identity {displayName} updated": "Identität {displayName} aktualisiert",
"If allowed by organizer": "Wenn vom Organisator erlaubt",
"If allowed by organizer": "Wenn von Organisator:in erlaubt",
"If an account with this email exists, we just sent another confirmation email to {email}": "Falls ein Konto mit dieser E-Mail-Adresse existiert, senden wir eine neue Bestätigungs-E-Mail an {email}",
"If this identity is the only administrator of some groups, you need to delete them before being able to delete this identity.": "Falls diese Identität der einzige Administrator einer oder mehrerer Gruppen sein sollte, ist zunächst die Gruppe zu löschen, bevor diese Identität gelöscht werden kann.",
"If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:": "Wenn Sie nach Ihrer föderierten Identität gefragt werden, setzt sich dieser aus Ihrem Benutzernamen und Ihrer Instanz zusammen. Die föderierte Identität für Ihr erstes Profil lautet zum Beispiel:",
"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.": "Wenn Sie sich für die manuelle Validierung von Teilnehmern entschieden haben, sendet Ihnen Mobilizon eine E-Mail, um Sie über neue zu bearbeitende Teilnahmen zu informieren. Sie können die Häufigkeit dieser Benachrichtigungen unten auswählen.",
"If you want, you may send a message to the event organizer here.": "Wenn Sie möchten, können Sie dem Organisator eine Nachricht senden.",
"If this identity is the only administrator of some groups, you need to delete them before being able to delete this identity.": "Falls diese Identität die einzige Administrator:in einer oder mehrerer Gruppen sein sollte, ist zunächst die Gruppe zu löschen, bevor diese Identität gelöscht werden kann.",
"If you are being asked for your federated indentity, it's composed of your username and your instance. For instance, the federated identity for your first profile is:": "Wenn Sie nach Ihrer föderierten Identität gefragt werden, setzt sich dieser aus Ihrem Benutzer:innen-Namen und Ihrer Instanz zusammen. Die föderierte Identität für Ihr erstes Profil lautet zum Beispiel:",
"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.": "Wenn Sie sich für die manuelle Validierung von Teilnehmer:innen entschieden haben, sendet Ihnen Mobilizon eine E-Mail, um Sie über neue zu bearbeitende Teilnahmen zu informieren. Sie können die Häufigkeit dieser Benachrichtigungen unten auswählen.",
"If you want, you may send a message to the event organizer here.": "Wenn Sie möchten, können Sie der Organisator:in eine Nachricht senden.",
"Ignore": "Ignorieren",
"Illustration picture for “{category}” by {author} on {source} ({license})": "Illustration für „{category}“ von {author} auf {source} ({license})",
"In person": "Persönlich",
@ -525,7 +526,7 @@
"Instance Terms": "Instanz-Regeln",
"Instance Terms Source": "Herkunft der Instanz-Regeln",
"Instance Terms URL": "URL der Instanz-Regeln",
"Instance administrator": "Administrator der Instanz",
"Instance administrator": "Administrator:in der Instanz",
"Instance configuration": "Einstellungen der Instanz",
"Instance feeds": "Instanz-Feeds",
"Instance languages": "Sprache der Instanz",
@ -592,11 +593,11 @@
"Login on Mobilizon!": "Bei Mobilizon anmelden!",
"Login on {instance}": "Anmelden auf {instance}",
"Login status": "Anmeldestatus",
"Main languages you/your moderators speak": "Hauptsprache Ihres Moderators",
"Main languages you/your moderators speak": "Hauptsprache Ihrer Moderator:in",
"Make sure that all words are spelled correctly.": "Achte darauf, dass alle Wörter richtig geschrieben sind.",
"Manage group members": "Gruppenmitglieder verwalten",
"Manage group memberships": "Gruppenmitgliedschaften verwalten",
"Manage participations": "Teilnehmer verwalten",
"Manage participations": "Teilnehmer:innen verwalten",
"Manually approve new followers": "Neue Follower manuell genehmigen",
"Manually invite new members": "Manuelles Einladen neuer Mitglieder",
"Map": "Karte",
@ -626,7 +627,7 @@
"Moderation": "Moderation",
"Moderation log": "Moderationsprotokoll",
"Moderation logs": "Moderationsprotokoll",
"Moderator": "Moderator",
"Moderator": "Moderator:in",
"Modify all of your account's data": "Alle Daten Ihres Kontos bearbeiten",
"More options": "Mehr Optionen",
"Most recently published": "Zuletzt veröffentlicht",
@ -639,7 +640,7 @@
"My federated identity ends in {domain}": "Meine föderierte Identität ended in {domain}",
"My groups": "Meine Gruppen",
"My identities": "Meine Identitäten",
"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.": "VORSICHT! Die Standardbedingungen wurden nicht von einem Anwalt geprüft und stellen daher wahrscheinlich keinen vollständigen Schutz für Administratoren in allen erdenklichen Fällen dar. Sie sind zudem nicht auf die lokalen Gesetzgebungen der Länder angepasst. Falls Sie sich unsicher sind, konsultieren Sie einen Rechtsanwalt.",
"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.": "VORSICHT! Die Standardbedingungen wurden nicht von einer Anwält:in geprüft und stellen daher wahrscheinlich keinen vollständigen Schutz für Administrator:innen in allen erdenklichen Fällen dar. Sie sind zudem nicht auf die lokalen Gesetzgebungen der Länder angepasst. Falls Sie sich unsicher sind, konsultieren Sie eine Rechtsanwält:in.",
"Name": "Name",
"Navigated to {pageTitle}": "Zu {pageTitle} navigiert",
"Never used": "Nie verwendet",
@ -690,8 +691,8 @@
"No open reports yet": "Bisher keine ausstehenden Berichte",
"No organized events found": "Keine erstellte Veranstaltung gefunden",
"No organized events listed": "Keine erstellten Veranstaltungen gelistet",
"No participant matches the filters": "Kein Teilnehmer entspricht den Filterkriterien",
"No participant to approve|Approve participant|Approve {number} participants": "Keine Teilnahme zu bestätigen|Bestätige Teilnehmer|Bestätige {number} Teilnehmer",
"No participant matches the filters": "Keine Teilnehmer:in entspricht den Filterkriterien",
"No participant to approve|Approve participant|Approve {number} participants": "Keine Teilnahme zu bestätigen|Bestätige Teilnehmer:in|Bestätige {number} Teilnehmer:innen",
"No participant to reject|Reject participant|Reject {number} participants": "Keine Teilnahme abzulehnen|Lehne Teilnahme ab|Lehne {number} Teilnahmen ab",
"No participations listed": "Keine Teilnahmen gelistet",
"No posts found": "Keine Beiträge gefunden",
@ -707,8 +708,8 @@
"No results found": "Keine Ergebnisse gefunden",
"No results found for {search}": "Keine Ergebnisse gefunden für {search}",
"No rules defined yet.": "Noch keine Regeln definiert.",
"No user matches the filter": "Kein Benutzer entspricht dem Filterkriterium",
"No user matches the filters": "Kein Benutzer entspricht den Filterkriterien",
"No user matches the filter": "Keine Benutzer:in entspricht dem Filterkriterium",
"No user matches the filters": "Keine Benutzer:in entspricht den Filterkriterien",
"None": "Keine",
"Not accessible with a wheelchair": "Für Rollstühle nicht barrierefrei",
"Not approved": "Nicht freigegeben",
@ -719,8 +720,8 @@
"Notification settings": "Benachrichtigungseinstellungen",
"Notifications": "Benachrichtigungen",
"Notifications for manually approved participations to an event": "Benachrichtigungen bei manuell bestätigten Teilnahmen an einer Veranstlatung",
"Notify participants": "Teilnehmer benachrichtigen",
"Notify the user of the change": "Benachrichtige den Benutzer über die Änderung",
"Notify participants": "Teilnehmer:innen benachrichtigen",
"Notify the user of the change": "Benachrichtige die Benutzer:in über die Änderung",
"Now, create your first profile:": "Erstellen Sie jetzt Ihr erstes Profil:",
"Number of members": "Anzahl der Mitglieder",
"Number of places": "Anzahl der Plätze",
@ -743,12 +744,12 @@
"Only accessible to members of the group": "Nur für Gruppenmitglieder sichtbar",
"Only alphanumeric lowercased characters and underscores are supported.": "Es werden nur alphanumerische Kleinbuchstaben und Unterstriche unterstützt.",
"Only group members can access discussions": "Nur Gruppenmitglieder können die Diskussion anzeigen",
"Only group moderators can create, edit and delete events.": "Nur Gruppen-Moderatoren können Veranstaltungen erstellen, bearbeiten und löschen.",
"Only group moderators can create, edit and delete posts.": "Nur Gruppenmoderatoren können Beiträge erstellen, editieren oder löschen.",
"Only registered users may fetch remote events from their URL.": "Nur registrierte Benutzer können Remote-Veranstaltungen über ihre URL abrufen.",
"Only group moderators can create, edit and delete events.": "Nur Gruppenmoderatoren:innen können Veranstaltungen erstellen, bearbeiten und löschen.",
"Only group moderators can create, edit and delete posts.": "Nur Gruppenmoderatoren:innen können Beiträge erstellen, editieren oder löschen.",
"Only registered users may fetch remote events from their URL.": "Nur registrierte Benutzer:innen können Remote-Veranstaltungen über ihre URL abrufen.",
"Open": "Offen",
"Open a topic on our forum": "Thema in unserem Forum eröffnen",
"Open an issue on our bug tracker (advanced users)": "Meldung in unserem Fehlermeldesystem (Bug-Tracker für erfahrene Benutzer) eröffnen",
"Open an issue on our bug tracker (advanced users)": "Meldung in unserem Fehlermeldesystem (Bug-Tracker für erfahrene Benutzer:innen) eröffnen",
"Open main menu": "Öffne Hauptmenü",
"Open user menu": "Öffne Benutzermenü",
"Opened reports": "Geöffnete Meldungen",
@ -758,33 +759,33 @@
"Organized by": "Organisiert von",
"Organized by {name}": "Organisiert von {name}",
"Organized events": "Organisierte Veranstaltungen",
"Organizer": "Organisator",
"Organizer notifications": "Benachrichtigungen für Organisatoren",
"Organizers": "Organisator",
"Organizer": "Organisator:in",
"Organizer notifications": "Benachrichtigungen für Organisator:innen",
"Organizers": "Organisator:innnen",
"Other": "Andere",
"Other actions": "Weitere Aktionen",
"Other notification options:": "Andere Benachrichtigungs-Optionen:",
"Other software may also support this.": "Andere Software unterstützt dies möglicherweise auch.",
"Other users with the same IP address": "Andere Benutzer mit der gleichen IP-Adresse",
"Other users with the same email domain": "Andere Benutzer mit der gleichen E-Mail-Domain",
"Otherwise this identity will just be removed from the group administrators.": "Andernfalls wird diese Identität aus der Liste der Gruppenadministratoren entfernt.",
"Other users with the same IP address": "Andere Benutzer:innen mit der gleichen IP-Adresse",
"Other users with the same email domain": "Andere Benutzer:innen mit der gleichen E-Mail-Domain",
"Otherwise this identity will just be removed from the group administrators.": "Andernfalls wird diese Identität aus der Liste der Gruppenadministrator:innen entfernt.",
"Owncast": "Owncast",
"Page": "Seite",
"Page limited to my group (asks for auth)": "Seite ist auf meine Gruppe beschränkt (nach Authentifizierung fragen)",
"Page not found": "Seite nicht gefunden",
"Parent folder": "Übergeordneter Ordner",
"Partially accessible with a wheelchair": "Teilweise barrierefrei für Rollstühle",
"Participant": "Teilnehmer",
"Participants": "Teilnehmer",
"Participant": "Teilnehmer:in",
"Participants": "Teilnehmer:innen",
"Participate": "Teilnehmen",
"Participate using your email address": "Nehmen Sie mit Ihrer E-Mail-Adresse teil",
"Participation approval": "Genehmigung der Teilnahme",
"Participation confirmation": "Teilnahmebestätigung",
"Participation notifications": "Benachrichtigungen für Teilnehmer",
"Participation notifications": "Benachrichtigungen für Teilnehmer:innen",
"Participation requested!": "Teilnahme angefragt!",
"Participation with account": "Teilnahme mit Konto",
"Participation without account": "Teilnahme ohne Konto",
"Participations": "Teilnehmer",
"Participations": "Teilnehmer:innen",
"Password": "Passwort",
"Password (confirmation)": "Passwort (Bestätigung)",
"Password reset": "Zurücksetzen des Passworts",
@ -800,11 +801,11 @@
"Pick an instance": "Instanz wählen",
"Please add as many details as possible to help identify the problem.": "Bitte geben Sie uns so viele Details wie möglich, die uns helfen könnten, das Problem zu analysieren.",
"Please check your spam folder if you didn't receive the email.": "Bitte sehen Sie auch in Ihrem Spam-Ordner nach, wenn Sie keine E-Mail erhalten haben.",
"Please contact this instance's Mobilizon admin if you think this is a mistake.": "Bitte kontaktieren Sie den Administrator dieser Mobilizon-Instanz, wenn Sie denken, dass dies ein Fehler ist.",
"Please contact this instance's Mobilizon admin if you think this is a mistake.": "Bitte kontaktieren Sie die Administrator:in dieser Mobilizon-Instanz, wenn Sie denken, dass dies ein Fehler ist.",
"Please do not use it in any real way.": "Bitte nicht in der Praxis anwenden.",
"Please enter your password to confirm this action.": "Bitte geben Sie zur Bestätigung des Vorgangs Ihr Passwort ein.",
"Please make sure the address is correct and that the page hasn't been moved.": "Bitte stellen Sie sicher, dass die Adresse korrekt ist und die Seite nicht verschoben wurde.",
"Please read the {fullRules} published by {instance}'s administrators.": "Bitte lesen Sie die {fullRules}, veröffentlicht von den Administratoren von {instance}.",
"Please read the {fullRules} published by {instance}'s administrators.": "Bitte lesen Sie die {fullRules}, veröffentlicht von den Administrator:innen von {instance}.",
"Popular groups close to you": "Beliebte Gruppen in Ihrer Nähe",
"Popular groups nearby {position}": "Beliebte Gruppen in der Nähe von {position}",
"Post": "Beitrag",
@ -847,7 +848,7 @@
"Publish group posts": "Gruppenbeiträge veröffentlichen",
"Published by {name}": "Veröffentlicht von {name}",
"Published events with <b>{comments}</b> comments and <b>{participations}</b> confirmed participations": "Veröffentlichte Veranstaltungen mit <b>{comments}</b> Kommentaren und <b>{participations}</b> bestätigten Teilnahmen",
"Published events with {comments} comments and {participations} confirmed participations": "Veröffentlichte Veranstaltung mit {comments} Kommentaren und {participations} bestätigten Teilnehmenden",
"Published events with {comments} comments and {participations} confirmed participations": "Veröffentlichte Veranstaltung mit {comments} Kommentaren und {participations} bestätigten Teilnehmer:innen",
"Push": "Push",
"Quote": "Zitat",
"RSS/Atom Feed": "RSS/Atom-Feed",
@ -866,7 +867,7 @@
"Register": "Registrieren",
"Register an account on {instanceName}!": "Erstelle einen Konto auf {instanceName}!",
"Register on this instance": "Auf dieser Instanz registrieren",
"Registration is allowed, anyone can register.": "Registrierung erlaubt, jeder kann teilnehmen.",
"Registration is allowed, anyone can register.": "Registrierung erlaubt, jede/r kann teilnehmen.",
"Registration is closed.": "Registrierung geschlossen.",
"Registration is currently closed.": "Registrierungen sind aktuell geschlossen.",
"Registrations": "Registrierungen",
@ -1006,11 +1007,11 @@
"The URL where the event live can be watched again after it has ended": "Die URL, unter der die Veranstaltung nach ihrem Ende noch einmal angesehen werden kann",
"The Zoom video teleconference URL": "Der Zoom Videokonferenz Link",
"The account's email address was changed. Check your emails to verify it.": "Die E-Mail-Adresse des Kontos wurde geändert. Bitte prüfen Sie Ihre E-Mails für eine Bestätigung.",
"The actual number of participants may differ, as this event is hosted on another instance.": "Die tatsächliche Zahl der Teilnehmer kann variieren, da diese Veranstaltung auf einer anderen Instanz ausgerichtet wird.",
"The actual number of participants may differ, as this event is hosted on another instance.": "Die tatsächliche Zahl der Teilnehmer:innen kann variieren, da diese Veranstaltung auf einer anderen Instanz ausgerichtet wird.",
"The calc will be created on {service}": "Das Tabellendokument wird auf {service} angelegt",
"The content came from another server. Transfer an anonymous copy of the report?": "Der Inhalt kam von einem anderen Server. Möchten Sie eine anonyme Kopie der Meldung übertragen?",
"The draft event has been updated": "Der Entwurf wurde aktualisiert",
"The event has a sign language interpreter": "Für die Veranstaltung gibt es Dolmetschen in Gebärdensprache",
"The event has a sign language interpreter": "Für die Veranstaltung gibt es Dolmetscher:innen für Gebärdensprache",
"The event has been created as a draft": "Diese Veranstaltung wurde als Entwurf erstellt",
"The event has been published": "Die Veranstaltung wurde veröffentlicht",
"The event has been updated": "Die Veranstaltung wurde aktualisiert",
@ -1019,9 +1020,9 @@
"The event is fully online": "Die Veranstaltung findet online statt",
"The event live video contains subtitles": "Der Livestream der Veranstaltung enthält Untertitel",
"The event live video does not contain subtitles": "Der Livestream der Veranstaltung enthält keine Untertitel",
"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?": "Der Veranstalter hat sich dafür entschieden, Teilnahmeanfragen manuell zu überprüfen. Möchten Sie eine kurze Nachricht hinterlassen, in der Sie erklären, warum Sie an der Veranstaltung teilnehmen möchten?",
"The event organizer didn't add any description.": "Der Organisator hat keine Beschreibung hinzugefügt.",
"The event organizer manually approves participations. Since you've chosen to participate without an account, please explain why you want to participate to this event.": "Der Organisator möchte Teilnahmen manuell bestätigen. Wenn Sie ohne Konto teilnehmen möchten, erklären Sie bitte, warum Sie an der Veranstaltung interessiert sind.",
"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?": "Die Veranstalter:in hat sich dafür entschieden, Teilnahmeanfragen manuell zu überprüfen. Möchten Sie eine kurze Nachricht hinterlassen, in der Sie erklären, warum Sie an der Veranstaltung teilnehmen möchten?",
"The event organizer didn't add any description.": "Die Organisator:in hat keine Beschreibung hinzugefügt.",
"The event organizer manually approves participations. Since you've chosen to participate without an account, please explain why you want to participate to this event.": "Die Organisator:in möchte Teilnahmen manuell bestätigen. Wenn Sie ohne Konto teilnehmen möchten, erklären Sie bitte, warum Sie an der Veranstaltung interessiert sind.",
"The event title will be ellipsed.": "Der Titel der Veranstaltung wird verkürzt dargestellt.",
"The event will show as attributed to this group.": "Die Veranstaltung wird als dieser Gruppe zugehörig angezeigt.",
"The event will show as attributed to this profile.": "Die Veranstaltung wird als Ihrem Profil zugewiesen angezeigt.",
@ -1031,7 +1032,7 @@
"The event {event} was updated by {profile}.": "Die Veranstaltung {event} wurde von {profile} aktualisiert.",
"The events you created are not shown here.": "Die Veranstaltung, die Sie erstellt haben ist hier nicht gelistet.",
"The geolocation prompt was denied.": "Die Abfrage der Geolokalisierung wurde verweigert.",
"The group can now be joined by anyone, but new members need to be approved by an administrator.": "Der Gruppe kann nun jeder beitreten, aber neue Mitglieder müssen von einem Administrator genehmigt werden.",
"The group can now be joined by anyone, but new members need to be approved by an administrator.": "Der Gruppe kann nun jede/r beitreten, aber neue Mitglieder müssen von einer Administrator:in genehmigt werden.",
"The group can now be joined by anyone.": "Der Gruppe können nun alle beitreten.",
"The group can now only be joined with an invite.": "Der Gruppe kann nun ausschließlich durch Einladung beigetreten werden.",
"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.": "Diese Gruppe wird öffentlich in Suchergebnissen sichtbar sein und könnte im Bereich „Entdecken“ auftauchen. Nur öffentliche Informationen werden auf der Gruppenseite angezeigt.",
@ -1039,12 +1040,12 @@
"The group's banner was changed.": "Das Gruppenbanner wurde geändert.",
"The group's physical address was changed.": "Die physische Adresse der Gruppe wurde geändert.",
"The group's short description was changed.": "Die Kurzbeschreibung der Gruppe wurde geändert.",
"The instance administrator is the person or entity that runs this Mobilizon instance.": "Der Administrator der Instanz ist die Person oder Organisation, die diese Mobilizon-Instanz betreibt.",
"The instance administrator is the person or entity that runs this Mobilizon instance.": "Die Administrator:in der Instanz ist die Person oder Organisation, die diese Mobilizon-Instanz betreibt.",
"The member was approved": "Das Mitglied wurde zugelassen",
"The member was removed from the group {group}": "Das Mitglied wurde aus der Gruppe {group} entfernt",
"The membership request from {profile} was rejected": "Der Mitgliedsantrag von {profile} wurde abgelehnt",
"The only way for your group to get new members is if an admininistrator invites them.": "Die einzige Möglichkeit für Ihre Gruppe, neue Mitglieder zu bekommen, ist, wenn ein Administrator Sie einlädt.",
"The organiser has chosen to close comments.": "Der Veranstalter hat beschlossen, die Kommentare zu schließen.",
"The only way for your group to get new members is if an admininistrator invites them.": "Die einzige Möglichkeit für Ihre Gruppe, neue Mitglieder zu bekommen, ist, wenn eine Administrator:in Sie einlädt.",
"The organiser has chosen to close comments.": "Die Veranstalter:in hat beschlossen, die Kommentare zu schließen.",
"The pad will be created on {service}": "Das Textdokument wird auf {service} angelegt",
"The page you're looking for doesn't exist.": "Die Seite, nach der Sie suchen existiert nicht.",
"The password was successfully changed": "Das Passwort wurde erfolgreich geändert",
@ -1052,22 +1053,22 @@
"The post {post} was deleted by {profile}.": "Der Beitrag {post} wurde von {profile} gelöscht.",
"The post {post} was updated by {profile}.": "Der Beitrag {post} wurde von {profile} aktualisiert.",
"The report contents (eventual comments and event) and the reported profile details will be transmitted to Akismet.": "Die Inhalte der Meldung (Kommentare und Veranstaltungen) und Details des gemeldeten Profils werden an Akismet übermittelt.",
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "Die Meldung wird an die Moderatoren Ihrer Instanz gesendet. Sie können unten erläutern, warum Sie diesen Inhalt melden.",
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "Die Meldung wird an die Moderator:innen Ihrer Instanz gesendet. Sie können unten erläutern, warum Sie diesen Inhalt melden.",
"The selected picture is too heavy. You need to select a file smaller than {size}.": "Die ausgewählte Bilddatei ist zu groß. Die Datei darf höchstens {size} groß sein.",
"The technical details of the error can help developers solve the problem more easily. Please add them to your feedback.": "Die technischen Details des Fehlers können den Entwicklern helfen, das Problem einfacher zu lösen. Bitte fügen Sie diese der Rückmeldung hinzu.",
"The user has been disabled": "Der Benutzer wurde deaktiviert",
"The technical details of the error can help developers solve the problem more easily. Please add them to your feedback.": "Die technischen Details des Fehlers können den Entwickler:innen helfen, das Problem einfacher zu lösen. Bitte fügen Sie diese der Rückmeldung hinzu.",
"The user has been disabled": "Die Benutzer:in wurde deaktiviert",
"The videoconference will be created on {service}": "Die Videokonferenz wird auf {service} angelegt",
"The {default_privacy_policy} will be used. They will be translated in the user's language.": "Die {default_privacy_policy} wird verwendet. Sie wird in die Sprache des Nutzers übersetzt.",
"The {default_terms} will be used. They will be translated in the user's language.": "Die {default_terms} werden verwendet. Sie werden in die Sprache der Nutzer übersetzt.",
"The {default_privacy_policy} will be used. They will be translated in the user's language.": "Die {default_privacy_policy} wird verwendet. Sie wird in die Sprache der Nutzer:in übersetzt.",
"The {default_terms} will be used. They will be translated in the user's language.": "Die {default_terms} werden verwendet. Sie werden in die Sprache der Nutzer:innen übersetzt.",
"Theme": "Thema",
"There are {participants} participants.": "Es gibt {participants} Teilnehmer.",
"There are {participants} participants.": "Es gibt {participants} Teilnehmer:innen.",
"There is no activity yet. Start doing some things to see activity appear here.": "Es gibt noch keine Ereignisse. Mache Dinge damit hier Ereignisse auftauchen.",
"There will be no way to recover your data.": "Es gibt keinen Weg Ihre Daten wiederherszustellen.",
"There's no discussions yet": "Es gibt noch keine Diskussion",
"These events may interest you": "Diese Veranstaltungen könnten Sie interessieren",
"These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "Diese Feeds enthalten Daten aller Veranstaltungen, die mit einem ihrer Profile erstellt wurden, oder an denen eins ihrer Profile teilnimmt. Sie sollten diese nicht weitergeben. Feeds einzelner Profile finden Sie auf der jeweiligen Profil-Einstellungsseite.",
"These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.": "Diese Feeds enthalten Daten aller Veranstaltungen, die mit diesem Profil erstellt wurden, oder an denen es teilnimmt. Sie sollten diese nicht weitergeben. Feeds aller ihrer Profile finden Sie in ihren Benachrichtigungseinstellungen.",
"This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.": "Diese Mobilizon-Instanz und der Organisator akzeptieren anonyme Teilnahmen, aber eine Bestätigung per E-Mail ist erforderlich.",
"This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.": "Diese Mobilizon-Instanz und die Organisator:in akzeptieren anonyme Teilnahmen, aber eine Bestätigung per E-Mail ist erforderlich.",
"This URL doesn't seem to be valid": "Diese Webadresse scheint ungültig",
"This URL is not supported": "Diese URL wird nicht unterstützt",
"This application will be able to access all of your informations and post content. Make sure you only approve applications you trust.": "Diese Anwendung wird Zugriff auf all Ihre Daten und Beitragsinhalte erhalten. Genehmigen Sie nur Anwendungen, denen Sie wirklich vertrauen.",
@ -1094,8 +1095,8 @@
"This instance, <b>{instanceName} ({domain})</b>, hosts your profile, so remember its name.": "Diese Instanz, <b>{instanceName} ({domain})</b>, hostet Ihr Profil, merken Sie sich also ihren Namen.",
"This instance, {instanceName}, hosts your profile, so remember its name.": "Diese Instanz, {instanceName}, beherbergt Ihr Profil, also merken Sie sich ihren Namen.",
"This is a demonstration site to test Mobilizon.": "Dies ist eine Demonstrationsseite, um Mobilizon zu testen.",
"This is like your federated username (<code>{username}</code>) for groups. It will allow the group to be found on the federation, and is guaranteed to be unique.": "Dies ist wie Ihr föderierter Benutzername (<code>{username}</code>), aber für Gruppen. Damit kann die Gruppe auch auf anderen Instanzen gefunden werden und ist garantiert eindeutig.",
"This is like your federated username ({username}) for groups. It will allow the group to be found on the federation, and is guaranteed to be unique.": "Dies ist wie Ihr föderierter Benutzername, aber für ({username}) Gruppen. Er erlaubt das finden Ihrer Gruppe in der Förderation und ist garantiert einzigartig.",
"This is like your federated username (<code>{username}</code>) for groups. It will allow the group to be found on the federation, and is guaranteed to be unique.": "Dies ist wie Ihr föderierter Benutzer:innen-Name (<code>{username}</code>), aber für Gruppen. Damit kann die Gruppe auch auf anderen Instanzen gefunden werden und ist garantiert eindeutig.",
"This is like your federated username ({username}) for groups. It will allow the group to be found on the federation, and is guaranteed to be unique.": "Dies ist wie Ihr föderierter Benutzer:innen-Name, aber für ({username}) Gruppen. Er erlaubt das finden Ihrer Gruppe in der Förderation und ist garantiert einzigartig.",
"This month": "Diesen Monat",
"This post is accessible only for members. You have access to it for moderation purposes only because you are an instance moderator.": "Dieser Beitrag ist nur für Mitglieder verfügbar. Du hast Zugriff nur für Moderationszwecke, denn du bist Moderator:in dieser Instanz.",
"This post is accessible only through it's link. Be careful where you post this link.": "Dieser Beitrag ist nur über dessen Link zugänglich. Seien Sie vorsichtig, wo Sie diesen Link posten.",
@ -1103,8 +1104,8 @@
"This profile is located on this instance, so you need to {access_the_corresponding_account} to suspend it.": "Dieses Profil befindet sich auf dieser Instanz, daher müssen Sie {access_the_corresponding_account} zugreifen, um es zu sperren.",
"This profile was not found": "Dieses Profil wurde nicht gefunden",
"This setting will be used to display the website and send you emails in the correct language.": "Diese Einstellung wird verwendet, um die Website anzuzeigen und Ihnen E-Mails in der richtigen Sprache zu senden.",
"This user doesn't have any profiles": "Dieser Benutzer hat keine Profile",
"This user was not found": "Dieser Benutzer wurde nicht gefunden",
"This user doesn't have any profiles": "Diese Benutzer:in hat keine Profile",
"This user was not found": "Diese Benutzer:in wurde nicht gefunden",
"This website isn't moderated and the data that you enter will be automatically destroyed every day at 00:01 (Paris timezone).": "Diese Website wird nicht moderiert und die Daten, die Sie eingeben, werden jeden Tag um 00:01 Uhr (Pariser Zeitzone) automatisch gelöscht.",
"This week": "Diese Woche",
"This weekend": "Dieses Wochenende",
@ -1116,7 +1117,7 @@
"Title": "Titel",
"To activate more notifications, head over to the notification settings.": "Um mehr Benachrichtigungen zu aktivieren, sehen Sie in den Benachrichtigungs-Einstellungen vorbei.",
"To confirm, type your event title \"{eventTitle}\"": "Geben Sie zur Bestätigung Ihren Veranstaltungstitel „{eventTitle}“ ein",
"To confirm, type your identity username \"{preferredUsername}\"": "Geben Sie zur Bestätigung den Nutzernamen Ihrer Identität „{preferredUsername}“ ein",
"To confirm, type your identity username \"{preferredUsername}\"": "Geben Sie zur Bestätigung den Nutzer:innen-Namen Ihrer Identität „{preferredUsername}“ ein",
"To create and manage multiples identities from a same account": "So erstellen und verwalten Sie mehrere Identitäten über ein und dasselbe Konto",
"To create and manage your events": "So erstellen und verwalten Sie Ihre Veranstaltungen",
"To create or join an group and start organizing with other people": "So erstellen Sie eine Gruppe oder treten ihr bei und beginnen, sich mit anderen Personen zu organisieren",
@ -1148,7 +1149,7 @@
"Underline": "Unterstreichen",
"Undo": "Rückgängig macheb",
"Unfollow": "Nicht mehr folgen",
"Unfortunately, your participation request was rejected by the organizers.": "Leider wurde Ihre Teilnahmeanfrage vom Organisator abgelehnt.",
"Unfortunately, your participation request was rejected by the organizers.": "Leider wurde Ihre Teilnahmeanfrage von der Organisator:in abgelehnt.",
"Unknown": "Unbekannt",
"Unknown actor": "Unbekannter Akteur",
"Unknown error.": "Unbekannter Fehler.",
@ -1177,10 +1178,10 @@
"Uploaded media size": "Größe der hochgeladenen Medien",
"Uploaded media total size": "Gesamtgröße der hochgeladenen Medien",
"Use my location": "Nutzte meinen Standort",
"User": "Nutzer",
"User": "Nutzer:in",
"User settings": "Nutzereinstellungen",
"Username": "Nutzername",
"Users": "Nutzer",
"Username": "Nutzer:innen-Name",
"Users": "Nutzer:innen",
"Validating account": "Konto bestätigen",
"Validating email": "E-Mail-Adresse bestätigen",
"Video Conference": "Videokonferenz",
@ -1227,7 +1228,7 @@
"What can I do to help?": "Was kann ich tun, wenn ich helfen möchte?",
"What happened?": "Was ist passiert?",
"Wheelchair accessibility": "Barrierefreiheit für Rollstühle",
"When a moderator from the group creates an event and attributes it to the group, it will show up here.": "Wenn ein Moderator der Gruppe ein Ereignis erstellt und es der Gruppe zuordnet, wird es hier angezeigt.",
"When a moderator from the group creates an event and attributes it to the group, it will show up here.": "Wenn eine Moderator:in der Gruppe ein Ereignis erstellt und es der Gruppe zuordnet, wird es hier angezeigt.",
"When the event is private, you'll need to share the link around.": "Wenn die Veranstaltung privat ist, müssen Sie den Link weitergeben.",
"When the post is private, you'll need to share the link around.": "Wenn der Beitrag privat ist, müssen Sie den Link weitergeben.",
"Whether smoking is prohibited during the event": "Während der Veranstaltung wird nicht geraucht",
@ -1240,7 +1241,7 @@
"Who published {number} events": "Die {number} Veranstaltungen angelegt haben",
"Why create an account?": "Warum ein Konto erstellen?",
"Will allow to display and manage your participation status on the event page when using this device. Uncheck if you're using a public device.": "Ermöglicht die Anzeige und Verwaltung Ihres Teilnahmestatus auf der Veranstaltungsseite, wenn Sie dieses Gerät verwenden. Deaktivieren Sie diese Option, wenn Sie ein öffentliches Gerät verwenden.",
"With the most participants": "Mit den meisten Teilnehmenden",
"With the most participants": "Mit den meisten Teilnehmer:innen",
"Within {number} kilometers of {place}": "|Innerhalb eines Kilometers von {place}|Innerhalb von {number} Kilometer von {place}",
"Write a new comment": "Schreibe einen neuen Kommentar",
"Write a new message": "Schreibe eine neue Nachricht",
@ -1252,7 +1253,7 @@
"You added the member {member}.": "Sie haben das Mitglied {member} hinzugefügt.",
"You approved {member}'s membership.": "Sie haben die Mitgliedschaft von {member} genehmigt.",
"You archived the discussion {discussion}.": "Sie haben die Diskussion {discussion} archiviert.",
"You are not an administrator for this group.": "Sie sind kein Administrator dieser Gruppe.",
"You are not an administrator for this group.": "Sie sind keine Administrator:in dieser Gruppe.",
"You are not part of any group.": "Sie sind kein Teil einer Gruppe.",
"You are offline": "Sie sind offline",
"You are participating in this event anonymously": "Sie nehmen anonym an dieser Veranstaltung teil",
@ -1276,7 +1277,7 @@
"You deleted the post {post}.": "Sie haben den Beitrag {post} gelöscht.",
"You deleted the resource {resource}.": "Sie haben die Ressource {resource} gelöscht.",
"You demoted the member {member} to an unknown role.": "Sie haben {member} zu einer unbekannten Rolle zurückgestuft.",
"You demoted {member} to moderator.": "Sie haben {member} zum Moderator zurückgestuft.",
"You demoted {member} to moderator.": "Sie haben {member} zur Moderator:in zurückgestuft.",
"You demoted {member} to simple member.": "Sie haben {member} zu einem einfachen Mitglied zurückgestuft.",
"You didn't create or join any event yet.": "Sie haben keine Veranstaltung erstellt oder nehmen an einer teil.",
"You don't follow any instances yet.": "Sie folgen noch keinen Instanzen.",
@ -1303,25 +1304,25 @@
"You need to login.": "Sie müssen sich einloggen.",
"You posted a comment on the event {event}.": "Sie haben die Veranstaltung {event} kommentiert.",
"You promoted the member {member} to an unknown role.": "Sie haben {member} einer unbekannten Rolle zugewiesen.",
"You promoted {member} to administrator.": "Sie haben {member} zum Administrator befördert.",
"You promoted {member} to moderator.": "Sie haben {member} zum Moderator befördert.",
"You promoted {member} to administrator.": "Sie haben {member} zur Administrator:in befördert.",
"You promoted {member} to moderator.": "Sie haben {member} zur Moderator:in befördert.",
"You rejected {member}'s membership request.": "Sie haben den Mitgliedsantrag von {member} abgelehnt.",
"You renamed the discussion from {old_discussion} to {discussion}.": "Sie haben die Diskussion {old_discussion} in {discussion} umbenannt.",
"You renamed the folder from {old_resource_title} to {resource}.": "Sie haben den Ordner {old_resource_title} in {resource} umbenannt.",
"You renamed the resource from {old_resource_title} to {resource}.": "Sie haben die Ressource {old_resource_title} in {resource} umbenannt.",
"You replied to a comment on the event {event}.": "Sie haben auf ein Kommentar in der Veranstaltung {event} geantwortet.",
"You replied to the discussion {discussion}.": "Sie haben auf die Diskussion {discussion} geantwortet.",
"You requested to join the group.": "Sie haben die angefragt der Gruppe beizutreten.",
"You requested to join the group.": "Sie haben angefragt, der Gruppe beizutreten.",
"You updated the event {event}.": "Sie haben die Veranstaltung {event} aktualisiert.",
"You updated the group {group}.": "Sie haben die Gruppe {group} aktualisiert.",
"You updated the member {member}.": "Sie haben {member} aktualisiert.",
"You updated the post {post}.": "Sie haben den Beitrag {post} aktualisiert.",
"You were demoted to an unknown role by {profile}.": "Sie wurden von {profile} in eine unbekannte Rolle zurückgestuft.",
"You were demoted to moderator by {profile}.": "Sie wurden von {profile} zum Moderator zurückgestuft.",
"You were demoted to moderator by {profile}.": "Sie wurden von {profile} zur Moderator:in zurückgestuft.",
"You were demoted to simple member by {profile}.": "Sie wurden von {profile} zum einfachen Mitglied zurückgestuft.",
"You were promoted to administrator by {profile}.": "Sie wurden von {profile} zum Administrator befördert.",
"You were promoted to administrator by {profile}.": "Sie wurden von {profile} zur Administrator:in befördert.",
"You were promoted to an unknown role by {profile}.": "Sie wurden von {profile} zu einer unbekannten Rolle befördert.",
"You were promoted to moderator by {profile}.": "Sie wurden von {profile} zum Moderator befördert.",
"You were promoted to moderator by {profile}.": "Sie wurden von {profile} zur Moderator:in befördert.",
"You will be able to add an avatar and set other options in your account settings.": "In Ihren Kontoeinstellungen können Sie einen Avatar hinzufügen und weitere Optionen festlegen.",
"You will be redirected to the original instance": "Sie werden auf die ursprüngliche Instanz weitergeleitet",
"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.": "Hier finden Sie alle Veranstaltungen, die Sie erstellt haben, an denen Sie teilnehmen, und von Gruppen, denen Sie folgen oder bei denen Sie Mitglied sind.",
@ -1355,8 +1356,8 @@
"Your participation request is being validated": "Ihre Teilnahme wird überprüft",
"Your participation status has been changed": "Ihr Teilnahmestatus hat sich geändert",
"Your participation status is saved only on this device and will be deleted one month after the event's passed.": "Ihr Teilnahmestatus wird nur auf diesem Gerät gespeichert und wird einen Monat nach Ablauf der Veranstaltung wieder gelöscht.",
"Your participation still has to be approved by the organisers.": "Ihre Teilnahme muss noch von den Organisatoren genehmigt werden.",
"Your participation will be validated once you click the confirmation link into the email, and after the organizer manually validates your participation.": "Ihre Teilnahme wird bestätigt, sobald Sie auf den Bestätigungslink in der E-Mail klicken und nachdem der Veranstalter Ihre Teilnahme manuell bestätigt hat.",
"Your participation still has to be approved by the organisers.": "Ihre Teilnahme muss noch von den Organisator:innen genehmigt werden.",
"Your participation will be validated once you click the confirmation link into the email, and after the organizer manually validates your participation.": "Ihre Teilnahme wird bestätigt, sobald Sie auf den Bestätigungslink in der E-Mail klicken und nachdem die Veranstalter:in Ihre Teilnahme manuell bestätigt hat.",
"Your participation will be validated once you click the confirmation link into the email.": "Ihre Teilnahme wird bestätigt, sobald Sie den Bestätigungslink in der E-Mail anklicken.",
"Your position was not available.": "Ihre Position konnte nicht abgerufen werden.",
"Your profile will be shown as contact.": "Ihr Profil wird als Kontakt angezeigt.",
@ -1395,7 +1396,7 @@
"iCal Feed": "iCal-Feed",
"instance rules": "Instanz-Regeln",
"mobilizon-instance.tld": "mobilizon-instanz.tld",
"more than 1360 contributors": "mehr als 1360 Unterstützern",
"more than 1360 contributors": "mehr als 1360 Unterstützer:innen",
"multitude of interconnected Mobilizon websites": "Vielzahl miteinander verbundener Mobilizon-Websites",
"new{'@'}email.com": "neu{'@'}email.com",
"profile@instance": "profil@instanz",
@ -1439,7 +1440,7 @@
"{moderator} deleted an event named \"{title}\"": "{moderator} hat eine Veranstaltung namens „{title}“ gelöscht",
"{moderator} has deleted a comment from {author}": "{moderator} hat einen Kommentar von {author} gelöscht",
"{moderator} has deleted a comment from {author} under the event {event}": "{moderator} hat einen Kommentar von {author} unter der Veranstaltung {event} gelöscht",
"{moderator} has deleted user {user}": "{moderator} hat den Nutzer {user} gelöscht",
"{moderator} has deleted user {user}": "{moderator} hat die Nutzer:in {user} gelöscht",
"{moderator} has done an unknown action": "{moderator} hat eine unbekannte Handlung vorgenommen",
"{moderator} has unsuspended group {profile}": "{moderator} hat die Sperrung der Gruppe {profile} aufgehoben",
"{moderator} has unsuspended profile {profile}": "{moderator} hat das Profil {profil} gesperrt",
@ -1454,7 +1455,7 @@
"{number} members": "{number} Mitglieder",
"{number} memberships": "{number} Mitgliedschaften",
"{number} organized events": "Keine organisierten Veranstaltungen|Eine organisierte Veranstaltung|{number} organisierte Veranstaltungen",
"{number} participations": "Keine Teilnehmer|Ein Teilnehmer|{number} Teilnehmer",
"{number} participations": "Keine Teilnehmer:in|Eine Teilnehmer:in|{number} Teilnehmer:innen",
"{number} posts": "Keine Beiträge |Ein Beitrag|{number} Beiträge",
"{number} seats left": "{number} Plätze übrig",
"{old_group_name} was renamed to {group}.": "{old_group_name} wurde in {group} umbenannt.",
@ -1470,7 +1471,7 @@
"{profile} deleted the folder {resource}.": "{profile} hat den Ordner {resource} gelöscht.",
"{profile} deleted the resource {resource}.": "{profile} hat die Ressource {resource} gelöscht.",
"{profile} demoted {member} to an unknown role.": "{profile} hat {member} zu einer unbekannten Rolle zurückgestuft.",
"{profile} demoted {member} to moderator.": "{profile} hat {member} zum Moderator zurückgestuft.",
"{profile} demoted {member} to moderator.": "{profile} hat {member} zur Moderator:in zurückgestuft.",
"{profile} demoted {member} to simple member.": "{profile} hat {member} zu einem einfachen Mitglied zurückgestuft.",
"{profile} excluded member {member}.": "{profile} hat {member} ausgeschlossen.",
"{profile} moved the folder {resource} into {new_path}.": "{profile} hat den Ordner {resource} nach {new_path} verschoben.",
@ -1478,9 +1479,9 @@
"{profile} moved the resource {resource} into {new_path}.": "{profile} hat die Ressource {resource} nach {new_path} verschoben.",
"{profile} moved the resource {resource} to the root folder.": "{profile} hat die Ressource {resource} in das Wurzelverzeichnis verschoben.",
"{profile} posted a comment on the event {event}.": "{profile} hat die Veranstaltung {event} kommentiert.",
"{profile} promoted {member} to administrator.": "{profile} hat {member} zum Administrator befördert.",
"{profile} promoted {member} to administrator.": "{profile} hat {member} zur Administrator:in befördert.",
"{profile} promoted {member} to an unknown role.": "{profile} hat {member} zu einer unbekannten Rolle befördert.",
"{profile} promoted {member} to moderator.": "{profile} hat {member} zum Moderator befördert.",
"{profile} promoted {member} to moderator.": "{profile} hat {member} zur Moderator:in befördert.",
"{profile} quit the group.": "{profile} hat die Gruppe verlassen.",
"{profile} rejected {member}'s membership request.": "{profile} hat den Mitgliedsantrag von {member} abgelehnt.",
"{profile} renamed the discussion from {old_discussion} to {discussion}.": "{profile} hat die Diskussion {old_discussion} in {discussion} umbenannt.",

View file

@ -35,6 +35,7 @@
"Back to previous page": "Back to previous page",
"Before you can login, you need to click on the link inside it to validate your account.": "Before you can login, you need to click on the link inside it to validate your account.",
"By {username}": "By {username}",
"Calendar": "Calendar",
"Cancel anonymous participation": "Cancel anonymous participation",
"Cancel creation": "Cancel creation",
"Cancel edition": "Cancel edition",
@ -1646,4 +1647,4 @@
"You need to enter a text": "You need to enter a text",
"Error while adding tag: {error}": "Error while adding tag: {error}",
"From this instance only": "From this instance only"
}
}

View file

@ -7,9 +7,11 @@ const participations = () => import("@/views/Event/ParticipantsView.vue");
const editEvent = () => import("@/views/Event/EditView.vue");
const event = () => import("@/views/Event/EventView.vue");
const myEvents = () => import("@/views/Event/MyEventsView.vue");
const eventCalendar = () => import("@/views/Event/CalendarView.vue");
export enum EventRouteName {
EVENT_LIST = "EventList",
EVENT_CALENDAR = "EventCalendar",
CREATE_EVENT = "CreateEvent",
MY_EVENTS = "MyEvents",
EDIT_EVENT = "EditEvent",
@ -26,6 +28,14 @@ export enum EventRouteName {
}
export const eventRoutes: RouteRecordRaw[] = [
{
path: "/events/calendar",
name: EventRouteName.EVENT_CALENDAR,
component: eventCalendar,
meta: {
requiredAuth: false,
},
},
{
path: "/events/create",
name: EventRouteName.CREATE_EVENT,

View file

@ -48,7 +48,7 @@ export class Address implements IAddress {
geom?: string = "";
timezone?: string = "";
timezone?: string;
constructor(hash?: IAddress) {
if (!hash) return;

View file

@ -0,0 +1,21 @@
<template>
<div class="container mx-auto px-1 mb-6">
<h1 v-if="!isMobile">
{{ t("Calendar") }}
</h1>
<div class="p-2">
<EventsCalendar v-if="!isMobile" />
<EventsAgenda v-else />
</div>
</div>
</template>
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
import EventsAgenda from "@/components/FullCalendar/EventsAgenda.vue";
import EventsCalendar from "@/components/FullCalendar/EventsCalendar.vue";
const { t } = useI18n({ useScope: "global" });
const isMobile = window.innerWidth < 760;
</script>

View file

@ -18,6 +18,13 @@
/>
</div>
<div class="start-time-icon-wrapper relative" v-if="event?.beginsOn">
<start-time-icon
:date="event.beginsOn.toString()"
class="absolute right-3 -top-16"
/>
</div>
<section class="intro px-2 pt-4" dir="auto">
<div class="flex flex-wrap gap-2 justify-end">
<div class="flex-1 min-w-[300px]">
@ -289,6 +296,7 @@ import {
usernameWithDomain,
} from "@/types/actor";
import DateCalendarIcon from "@/components/Event/DateCalendarIcon.vue";
import StartTimeIcon from "@/components/Event/StartTimeIcon.vue";
import SkeletonDateCalendarIcon from "@/components/Event/SkeletonDateCalendarIcon.vue";
import Earth from "vue-material-design-icons/Earth.vue";
import Link from "vue-material-design-icons/Link.vue";

View file

@ -85,6 +85,7 @@
<full-address-auto-complete
:label="$t('Group address')"
v-model="group.physicalAddress"
:is-required="false"
/>
<div class="field">

View file

@ -162,6 +162,7 @@
v-model="currentAddress"
:allowManualDetails="true"
:hideMap="true"
:is-required="false"
/>
<div class="flex flex-wrap gap-2 my-2">

View file

@ -21,7 +21,8 @@
<div class="flex self-center h-0 mt-4 items-end">
<figure class="" v-if="group.avatar">
<img
class="rounded-full border h-32 w-32"
class="rounded-full border h-32 w-32 group-pfp"
:style="`background-image: url(${group.avatar.url})`"
:src="group.avatar.url"
alt=""
width="128"
@ -1209,6 +1210,16 @@ watch(isCurrentActorAGroupMember, () => {
</script>
<style lang="scss" scoped>
@use "@/styles/_mixins" as *;
.group-pfp {
object-fit: contain;
background-size: cover;
background-repeat: no-repeat;
background-blend-mode: darken;
background-color: rgba(0, 0, 0, 0.8);
background-position: center;
}
div.container {
.block-container {
display: flex;

View file

@ -1,34 +1,9 @@
<template>
<!-- <o-loading v-model:active="$apollo.loading" /> -->
<!-- Nice looking SVGs -->
<section class="mt-5 sm:mt-24">
<div class="-z-10 overflow-hidden">
<img
alt=""
src="/img/shape-1.svg"
class="-z-10 absolute left-[2%] top-36"
width="300"
/>
<img
alt=""
src="/img/shape-2.svg"
class="-z-10 absolute left-[50%] top-[5%] -translate-x-2/4 opacity-60"
width="800"
/>
<img
alt=""
src="/img/shape-3.svg"
class="-z-10 absolute top-0 right-36"
width="200"
/>
</div>
</section>
<!-- Unlogged introduction -->
<unlogged-introduction :config="config" v-if="config && !isLoggedIn" />
<!-- Search fields -->
<search-fields v-model:search="search" v-model:location="location" />
<!-- Categories preview -->
<categories-preview />
<!-- Categories preview
<categories-preview /> -->
<!-- Welcome back -->
<section
class="container mx-auto"
@ -135,17 +110,18 @@
>
</span>
</section>
<!-- Recent events -->
<!-- Recent events
<CloseEvents
@doGeoLoc="performGeoLocation()"
:userLocation="userLocation"
:doingGeoloc="doingGeoloc"
/>
<CloseGroups :userLocation="userLocation" @doGeoLoc="performGeoLocation()" />
<OnlineEvents />
<LastEvents v-if="instanceName" :instanceName="instanceName" />
<CloseGroups :userLocation="userLocation" @doGeoLoc="performGeoLocation()" /> -->
<!--OnlineEvents /-->
<UpcomingEvents v-if="instanceName" :instanceName="instanceName" />
<!-- <LastEvents v-if="instanceName" :instanceName="instanceName" /> -->
<!-- Unlogged content section -->
<picture v-if="!currentUser?.isLoggedIn">
<!-- <picture v-if="!currentUser?.isLoggedIn">
<source
media="(max-width: 799px)"
:srcset="`/img/pics/homepage-480w.webp`"
@ -177,7 +153,7 @@
alt=""
loading="lazy"
/>
</picture>
</picture> -->
<presentation v-if="!currentUser?.isLoggedIn" />
</template>
@ -196,6 +172,7 @@ import { IEvent } from "../types/event.model";
import CloseEvents from "@/components/Local/CloseEvents.vue";
import CloseGroups from "@/components/Local/CloseGroups.vue";
import LastEvents from "@/components/Local/LastEvents.vue";
import UpcomingEvents from "@/components/Local/UpcomingEvents.vue";
import OnlineEvents from "@/components/Local/OnlineEvents.vue";
import {
computed,
@ -214,7 +191,6 @@ import {
UPDATE_CURRENT_USER_LOCATION_CLIENT,
} from "@/graphql/location";
import { LocationType } from "@/types/user-location.model";
import Presentation from "@/components/Home/MobilizonPresentation.vue";
import CategoriesPreview from "@/components/Home/CategoriesPreview.vue";
import UnloggedIntroduction from "@/components/Home/UnloggedIntroduction.vue";
import SearchFields from "@/components/Home/SearchFields.vue";

View file

@ -2,20 +2,6 @@
<section class="container mx-auto py-4 is-max-desktop max-w-2xl">
<div class="">
<div class="">
<picture>
<source
:srcset="`/img/pics/error-480w.webp 1x, /img/pics/error-1024w.webp 2x`"
type="image/webp"
/>
<img
:src="`/img/pics/error-480w.webp`"
alt=""
width="2616"
height="1698"
loading="lazy"
/>
</picture>
<h1 class="text-4xl mb-3">
{{ $t("The page you're looking for doesn't exist.") }}
</h1>

View file

@ -3,6 +3,7 @@ import { defineConfig } from "vite";
import path from "path";
import { VitePWA } from "vite-plugin-pwa";
import { visualizer } from "rollup-plugin-visualizer";
import svgLoader from "vite-svg-loader";
export default defineConfig(({ command }) => {
const isDev = command !== "build";
@ -17,7 +18,7 @@ export default defineConfig(({ command }) => {
const isStory = Boolean(process.env.HISTOIRE);
const plugins = [vue(), visualizer()];
const plugins = [vue(), svgLoader(), visualizer()];
if (!isStory) {
plugins.push(
@ -83,6 +84,9 @@ export default defineConfig(({ command }) => {
return {
plugins,
build,
server: {
host: isDev ? "0.0.0.0" : "localhost",
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),