Merge branch 'fixes' into 'main'

Various fixes

Closes #1279, #900 et #822

See merge request framasoft/mobilizon!1399
This commit is contained in:
Thomas Citharel 2023-05-30 07:59:23 +00:00
commit 4643f00d9b
33 changed files with 1368 additions and 1028 deletions

4
.husky/pre-commit Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
cd js
yarn run lint-staged

View file

@ -14,7 +14,14 @@
"story:build": "histoire build", "story:build": "histoire build",
"story:preview": "histoire preview", "story:preview": "histoire preview",
"test": "vitest", "test": "vitest",
"coverage": "vitest run --coverage" "coverage": "vitest run --coverage",
"prepare": "cd ../ && husky install"
},
"lint-staged": {
"**/*.{js,ts,vue}": [
"eslint --fix",
"prettier --write"
]
}, },
"dependencies": { "dependencies": {
"@absinthe/socket": "^0.2.1", "@absinthe/socket": "^0.2.1",
@ -116,7 +123,9 @@
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.3.0",
"flush-promises": "^1.0.2", "flush-promises": "^1.0.2",
"histoire": "^0.16.1", "histoire": "^0.16.1",
"husky": "^8.0.3",
"jsdom": "^22.0.0", "jsdom": "^22.0.0",
"lint-staged": "^13.2.2",
"mock-apollo-client": "^1.1.0", "mock-apollo-client": "^1.1.0",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"prettier-eslint": "^15.0.1", "prettier-eslint": "^15.0.1",
@ -124,7 +133,7 @@
"sass": "^1.34.1", "sass": "^1.34.1",
"typescript": "~5.0.0", "typescript": "~5.0.0",
"vite": "^4.0.4", "vite": "^4.0.4",
"vite-plugin-pwa": "^0.14.1", "vite-plugin-pwa": "^0.15.1",
"vitest": "^0.31.0", "vitest": "^0.31.0",
"vue-i18n-extract": "^2.0.4" "vue-i18n-extract": "^2.0.4"
} }

View file

@ -42,13 +42,13 @@ body {
@apply bg-transparent text-black dark:text-white font-semibold py-2 px-4 border border-mbz-bluegreen dark:border-violet-3; @apply bg-transparent text-black dark:text-white font-semibold py-2 px-4 border border-mbz-bluegreen dark:border-violet-3;
} }
.btn-outlined-success { .btn-outlined-success {
@apply border-mbz-success; @apply border-mbz-success hover:bg-mbz-success;
} }
.btn-outlined-warning { .btn-outlined-warning {
@apply bg-transparent border dark:text-white hover:dark:text-slate-900 hover:bg-mbz-warning border-mbz-warning; @apply bg-transparent border dark:text-white hover:dark:text-slate-900 hover:bg-mbz-warning border-mbz-warning;
} }
.btn-outlined-danger { .btn-outlined-danger {
@apply border-mbz-danger; @apply border-mbz-danger hover:bg-mbz-danger;
} }
.btn-outlined-text { .btn-outlined-text {
@apply bg-transparent hover:text-slate-900; @apply bg-transparent hover:text-slate-900;

View file

@ -1,10 +1,10 @@
<template> <template>
<router-link <router-link
class="flex gap-1 w-full items-center p-2 border-b-stone-200 border-b" class="flex gap-1 w-full items-center p-2 border-b-stone-200 border-b bg-white dark:bg-transparent"
dir="auto" dir="auto"
:to="{ :to="{
name: RouteName.DISCUSSION, name: RouteName.DISCUSSION,
params: { slug: discussion.slug, id: discussion.id }, params: { slug: discussion.slug },
}" }"
> >
<div class=""> <div class="">

View file

@ -157,7 +157,10 @@
</footer> </footer>
</form> </form>
</o-collapse> </o-collapse>
<div class="map" v-if="!hideMap"> <div
class="map"
v-if="!hideMap && !disabled && (selected.geom || detailsAddress)"
>
<map-leaflet <map-leaflet
:coords="selected.geom ?? defaultCoords" :coords="selected.geom ?? defaultCoords"
:marker="mapMarkerValue" :marker="mapMarkerValue"

View file

@ -1,6 +1,6 @@
<template> <template>
<group-section <group-section
:title="t('Public page')" :title="t('Announcements')"
icon="bullhorn" icon="bullhorn"
:privateSection="false" :privateSection="false"
:route="{ :route="{

View file

@ -8,7 +8,10 @@
}" }"
> >
<template #default> <template #default>
<div v-if="group?.resources?.elements?.length ?? 0 > 0" class="p-1"> <div
v-if="group?.resources?.elements?.length ?? 0 > 0"
class="p-1 bg-white dark:bg-transparent"
>
<div <div
v-for="resource in group?.resources?.elements ?? []" v-for="resource in group?.resources?.elements ?? []"
:key="resource.id" :key="resource.id"

View file

@ -87,7 +87,7 @@ import { useMutation } from "@vue/apollo-composable";
import { AUTORIZE_APPLICATION } from "@/graphql/application"; import { AUTORIZE_APPLICATION } from "@/graphql/application";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
import { IApplication } from "@/types/application.model"; import { IApplication } from "@/types/application.model";
import { scope } from "./scopes"; import { scope as oAuthScopes } from "./scopes";
import AlertCircle from "vue-material-design-icons/AlertCircle.vue"; import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
@ -104,7 +104,7 @@ const isOpen = ref<number>(-1);
const collapses = computed(() => const collapses = computed(() =>
(props.scope ?? "") (props.scope ?? "")
.split(" ") .split(" ")
.map((localScope) => scope[localScope]) .map((localScope) => oAuthScopes[localScope])
.filter((localScope) => localScope) .filter((localScope) => localScope)
); );

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="posts-wrapper"> <div class="posts-wrapper grid gap-4">
<post-list-item <post-list-item
v-for="post in posts" v-for="post in posts"
:key="post.id" :key="post.id"
@ -22,8 +22,6 @@ withDefaults(
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.posts-wrapper { .posts-wrapper {
display: grid;
grid-gap: 20px;
grid-template: 1fr; grid-template: 1fr;
} }
</style> </style>

View file

@ -18,7 +18,7 @@
</h3> </h3>
<p class="flex gap-2"> <p class="flex gap-2">
<Clock /> <Clock />
<span dir="auto" class="" v-if="isBeforeLastWeek">{{ <span dir="auto" class="" v-if="publishedAt && isBeforeLastWeek">{{
formatDateTimeString( formatDateTimeString(
publishedAt.toString(), publishedAt.toString(),
undefined, undefined,
@ -26,7 +26,7 @@
"short" "short"
) )
}}</span> }}</span>
<span v-else>{{ <span v-else-if="publishedAt">{{
formatDistanceToNow(publishedAt, { formatDistanceToNow(publishedAt, {
locale: dateFnsLocale, locale: dateFnsLocale,
addSuffix: true, addSuffix: true,
@ -47,6 +47,24 @@
</template> </template>
</i18n-t> </i18n-t>
</p> </p>
<p
v-if="post.visibility === PostVisibility.UNLISTED"
class="flex gap-2 items-center"
>
<Link :size="16" />
{{ t("Accessible only by link") }}
</p>
<p
v-else-if="post.visibility === PostVisibility.PRIVATE"
class="flex gap-2 items-center"
>
<Lock :size="16" />
{{
t("Accessible only to members", {
group: post.attributedTo?.name,
})
}}
</p>
</div> </div>
</router-link> </router-link>
</template> </template>
@ -61,7 +79,11 @@ import { formatDateTimeString } from "@/filters/datetime";
import Tag from "vue-material-design-icons/Tag.vue"; import Tag from "vue-material-design-icons/Tag.vue";
import AccountEdit from "vue-material-design-icons/AccountEdit.vue"; import AccountEdit from "vue-material-design-icons/AccountEdit.vue";
import Clock from "vue-material-design-icons/Clock.vue"; import Clock from "vue-material-design-icons/Clock.vue";
import Lock from "vue-material-design-icons/Lock.vue";
import Link from "vue-material-design-icons/Link.vue";
import MbzTag from "@/components/TagElement.vue"; import MbzTag from "@/components/TagElement.vue";
import { PostVisibility } from "@/types/enums";
import { useI18n } from "vue-i18n";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -71,34 +93,27 @@ const props = withDefaults(
{ isCurrentActorMember: false } { isCurrentActorMember: false }
); );
const { t } = useI18n({ useScope: "global" });
const dateFnsLocale = inject<Locale>("dateFnsLocale"); const dateFnsLocale = inject<Locale>("dateFnsLocale");
const postTags = computed(() => (props.post.tags ?? []).slice(0, 3)); const postTags = computed(() => (props.post.tags ?? []).slice(0, 3));
const publishedAt = computed((): Date => { const publishedAt = computed((): Date | undefined => {
return new Date((props.post.publishAt ?? props.post.insertedAt) as Date); const date = props.post.publishAt ?? props.post.insertedAt;
if (!date) return undefined;
return new Date(date);
}); });
const isBeforeLastWeek = computed((): boolean => { const isBeforeLastWeek = computed((): boolean => {
return isBefore(publishedAt.value, subWeeks(new Date(), 1)); return (
publishedAt.value !== undefined &&
isBefore(publishedAt.value, subWeeks(new Date(), 1))
);
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "@/styles/_mixins" as *; @use "@/styles/_mixins" as *;
// .post-minimalist-card-wrapper {
// display: grid;
// grid-gap: 5px 10px;
// grid-template-areas: "preview" "body";
// text-decoration: none;
// // width: 100%;
// // color: initial;
// // @include desktop {
// grid-template-columns: 200px 3fr;
// grid-template-areas: "preview body";
// // }
.title-info-wrapper { .title-info-wrapper {
.post-minimalist-title { .post-minimalist-title {
font-size: 18px; font-size: 18px;

View file

@ -7,7 +7,7 @@
> >
<o-notification <o-notification
variant="warning" variant="warning"
v-if="post.visibility !== PostVisibility.PUBLIC" v-if="post.visibility === PostVisibility.UNLISTED"
:closable="false" :closable="false"
> >
{{ {{

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="resource-wrapper" dir="auto"> <div class="resource-wrapper bg-white dark:bg-transparent" dir="auto">
<router-link <router-link
:to="{ :to="{
name: RouteName.RESOURCE_FOLDER, name: RouteName.RESOURCE_FOLDER,

View file

@ -1,7 +1,12 @@
<template> <template>
<div class="flex flex-1 items-center w-full" dir="auto"> <div
class="flex flex-1 items-center w-full bg-white dark:bg-transparent"
dir="auto"
>
<a :href="resource.resourceUrl" target="_blank"> <a :href="resource.resourceUrl" target="_blank">
<div class="preview text-mbz-purple dark:text-mbz-purple-300"> <div
class="min-w-fit relative flex items-center justify-center text-mbz-purple dark:text-mbz-purple-300"
>
<div <div
v-if=" v-if="
resource.type && resource.type &&
@ -14,10 +19,12 @@
customSize="48" customSize="48"
/> />
</div> </div>
<div <img
class="preview-image"
v-else-if="resource.metadata && resource.metadata.imageRemoteUrl" v-else-if="resource.metadata && resource.metadata.imageRemoteUrl"
:style="`background-image: url(${resource.metadata.imageRemoteUrl})`" :src="resource.metadata.imageRemoteUrl"
alt=""
height="48"
width="48"
/> />
<div class="preview-type" v-else> <div class="preview-type" v-else>
<Link :size="48" /> <Link :size="48" />
@ -99,25 +106,6 @@ a {
overflow: hidden; overflow: hidden;
flex: 1; flex: 1;
.preview {
flex: 0 0 50px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
.preview-image {
border-radius: 4px 0 0 4px;
display: block;
margin: 0;
width: 100%;
height: 100%;
object-fit: cover;
background-size: cover;
background-position: 50%;
}
}
.body { .body {
img.favicon { img.favicon {
display: inline-block; display: inline-block;

View file

@ -34,7 +34,7 @@
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
type position = type positionValues =
| "top-right" | "top-right"
| "top" | "top"
| "top-left" | "top-left"
@ -49,7 +49,7 @@ const props = withDefaults(
onAction?: () => any; onAction?: () => any;
cancelText?: string | null; cancelText?: string | null;
variant?: string; variant?: string;
position?: position; position?: positionValues;
pauseOnHover?: boolean; pauseOnHover?: boolean;
indefinite?: boolean; indefinite?: boolean;
}>(), }>(),

View file

@ -430,7 +430,6 @@
"Text": "Text", "Text": "Text",
"Upcoming events": "Upcoming events", "Upcoming events": "Upcoming events",
"Resources": "Resources", "Resources": "Resources",
"Public page": "Public page",
"Discussions": "Discussions", "Discussions": "Discussions",
"No public upcoming events": "No public upcoming events", "No public upcoming events": "No public upcoming events",
"Latest posts": "Latest posts", "Latest posts": "Latest posts",
@ -1564,5 +1563,6 @@
"This application will be allowed to publish and manage events, post and manage comments, participate to events, manage all of your groups, including group events, resources, posts and discussions. It will also be allowed to manage your account and profile settings.": "This application will be allowed to publish and manage events, post and manage comments, participate to events, manage all of your groups, including group events, resources, posts and discussions. It will also be allowed to manage your account and profile settings.", "This application will be allowed to publish and manage events, post and manage comments, participate to events, manage all of your groups, including group events, resources, posts and discussions. It will also be allowed to manage your account and profile settings.": "This application will be allowed to publish and manage events, post and manage comments, participate to events, manage all of your groups, including group events, resources, posts and discussions. It will also be allowed to manage your account and profile settings.",
"No apps authorized yet": "No apps authorized yet", "No apps authorized yet": "No apps authorized yet",
"You have been logged-out": "You have been logged-out", "You have been logged-out": "You have been logged-out",
"An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events on your behalf, automatically and remotely.": "An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events on your behalf, automatically and remotely." "An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events on your behalf, automatically and remotely.": "An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events on your behalf, automatically and remotely.",
"Announcements": "Announcements"
} }

View file

@ -1560,5 +1560,6 @@
"This application will be allowed to publish and manage events, post and manage comments, participate to events, manage all of your groups, including group events, resources, posts and discussions. It will also be allowed to manage your account and profile settings.": "Cette application sera autorisée à publier et à gérer des événements, à publier et à gérer des commentaires, à participer à des événements, à gérer tous vos groupes, y compris les événements de groupe, les ressources, les messages et les discussions. Elle pourra également gérer les paramètres de votre compte et de votre profil.", "This application will be allowed to publish and manage events, post and manage comments, participate to events, manage all of your groups, including group events, resources, posts and discussions. It will also be allowed to manage your account and profile settings.": "Cette application sera autorisée à publier et à gérer des événements, à publier et à gérer des commentaires, à participer à des événements, à gérer tous vos groupes, y compris les événements de groupe, les ressources, les messages et les discussions. Elle pourra également gérer les paramètres de votre compte et de votre profil.",
"No apps authorized yet": "Aucune application autorisée pour le moment", "No apps authorized yet": "Aucune application autorisée pour le moment",
"You have been logged-out": "Vous avez été déconnecté·e", "You have been logged-out": "Vous avez été déconnecté·e",
"An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events on your behalf, automatically and remotely.": "Une « interface de programmation dapplication » ou « API » est un protocole de communication qui permet aux composants logiciels de communiquer entre eux. L'API Mobilizon, par exemple, peut permettre à des outils logiciels tiers de communiquer avec les instances Mobilizon pour effectuer certaines actions, telles que la publication d'événements en votre nom, automatiquement et à distance." "An “application programming interface” or “API” is a communication protocol that allows software components to communicate with each other. The Mobilizon API, for example, can allow third-party software tools to communicate with Mobilizon instances to carry out certain actions, such as posting events on your behalf, automatically and remotely.": "Une « interface de programmation dapplication » ou « API » est un protocole de communication qui permet aux composants logiciels de communiquer entre eux. L'API Mobilizon, par exemple, peut permettre à des outils logiciels tiers de communiquer avec les instances Mobilizon pour effectuer certaines actions, telles que la publication d'événements en votre nom, automatiquement et à distance.",
"Announcements": "Annonces"
} }

View file

@ -215,7 +215,7 @@ import { IPerson, displayName } from "@/types/actor";
import PictureUpload from "@/components/PictureUpload.vue"; import PictureUpload from "@/components/PictureUpload.vue";
import { MOBILIZON_INSTANCE_HOST } from "@/api/_entrypoint"; import { MOBILIZON_INSTANCE_HOST } from "@/api/_entrypoint";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
import { buildFileVariable } from "@/utils/image"; import { buildFileFromIMedia, buildFileVariable } from "@/utils/image";
import { changeIdentity } from "@/utils/identity"; import { changeIdentity } from "@/utils/identity";
import { import {
CREATE_FEED_TOKEN_ACTOR, CREATE_FEED_TOKEN_ACTOR,
@ -237,6 +237,7 @@ import { Dialog } from "@/plugins/dialog";
import { Notifier } from "@/plugins/notifier"; import { Notifier } from "@/plugins/notifier";
import { AbsintheGraphQLErrors } from "@/types/errors.model"; import { AbsintheGraphQLErrors } from "@/types/errors.model";
import { ICurrentUser } from "@/types/current-user.model"; import { ICurrentUser } from "@/types/current-user.model";
import { useHead } from "@vueuse/head";
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
const router = useRouter(); const router = useRouter();
@ -245,7 +246,11 @@ const props = defineProps<{ isUpdate: boolean; identityName?: string }>();
const { currentActor } = useCurrentActorClient(); const { currentActor } = useCurrentActorClient();
const { result: personResult, onError: onPersonError } = useQuery<{ const {
result: personResult,
onError: onPersonError,
onResult: onPersonResult,
} = useQuery<{
fetchPerson: IPerson; fetchPerson: IPerson;
}>( }>(
FETCH_PERSON, FETCH_PERSON,
@ -257,6 +262,10 @@ const { result: personResult, onError: onPersonError } = useQuery<{
}) })
); );
onPersonResult(async ({ data }) => {
avatarFile.value = await buildFileFromIMedia(data?.fetchPerson?.avatar);
});
onPersonError((err) => handleErrors(err as unknown as AbsintheGraphQLErrors)); onPersonError((err) => handleErrors(err as unknown as AbsintheGraphQLErrors));
const person = computed(() => personResult.value?.fetchPerson); const person = computed(() => personResult.value?.fetchPerson);
@ -285,24 +294,6 @@ watch(person, () => {
const avatarMaxSize = useAvatarMaxSize(); const avatarMaxSize = useAvatarMaxSize();
// error({ graphQLErrors }) {
// this.handleErrors(graphQLErrors);
// },
// metaInfo() {
// // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// // @ts-ignore
// const { isUpdate, identityName } = this;
// let title = t("Create a new profile") as string;
// if (isUpdate) {
// title = t("Edit profile {profile}", {
// profile: identityName,
// }) as string;
// }
// return {
// title,
// };
// },
const errors = ref<string[]>([]); const errors = ref<string[]>([]);
const avatarFile = ref<File | null>(null); const avatarFile = ref<File | null>(null);
const showCopiedTooltip = reactive({ ics: false, atom: false }); const showCopiedTooltip = reactive({ ics: false, atom: false });
@ -764,4 +755,16 @@ const breadcrumbsLinks = computed(
const updateUsername = (value: string) => { const updateUsername = (value: string) => {
identity.value.preferredUsername = convertToUsername(value); identity.value.preferredUsername = convertToUsername(value);
}; };
useHead({
title: computed(() => {
let title = t("Create a new profile") as string;
if (isUpdate.value) {
title = t("Edit profile {profile}", {
profile: identityName.value,
}) as string;
}
return title;
}),
});
</script> </script>

View file

@ -102,7 +102,7 @@ import RouteName from "@/router/name";
import EmptyContent from "@/components/Utils/EmptyContent.vue"; import EmptyContent from "@/components/Utils/EmptyContent.vue";
import { useQuery } from "@vue/apollo-composable"; import { useQuery } from "@vue/apollo-composable";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { computed, ref } from "vue"; import { computed } from "vue";
import { useHead } from "@vueuse/head"; import { useHead } from "@vueuse/head";
import { import {
useRouteQuery, useRouteQuery,

View file

@ -483,6 +483,7 @@
<span class="" v-if="event.draft === true"> <span class="" v-if="event.draft === true">
<o-button <o-button
variant="primary" variant="primary"
class="!text-black hover:!text-white"
outlined outlined
@click="createOrUpdateDraft" @click="createOrUpdateDraft"
:disabled="saving" :disabled="saving"

View file

@ -196,7 +196,7 @@ import RouteName from "@/router/name";
import { buildFileFromIMedia } from "@/utils/image"; import { buildFileFromIMedia } from "@/utils/image";
import { useAvatarMaxSize, useBannerMaxSize } from "@/composition/config"; import { useAvatarMaxSize, useBannerMaxSize } from "@/composition/config";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { computed, ref, watch, defineAsyncComponent, inject } from "vue"; import { computed, ref, defineAsyncComponent, inject } from "vue";
import { useGroup, useUpdateGroup } from "@/composition/apollo/group"; import { useGroup, useUpdateGroup } from "@/composition/apollo/group";
import { import {
useCurrentActorClient, useCurrentActorClient,
@ -262,38 +262,17 @@ const copyURL = async (): Promise<void> => {
}, 2000); }, 2000);
}; };
onGroupResult(({ data }) => { onGroupResult(async ({ data }) => {
if (!data) return;
editableGroup.value = data.group; editableGroup.value = data.group;
}); try {
avatarFile.value = await buildFileFromIMedia(editableGroup.value?.avatar);
watch( bannerFile.value = await buildFileFromIMedia(editableGroup.value?.banner);
group, } catch (e) {
async (newGroup: IGroup | undefined, oldGroup: IGroup | undefined) => { // Catch errors while building media
console.debug("watching group"); console.error(e);
if (!newGroup) return;
try {
if (
oldGroup?.avatar !== undefined &&
oldGroup?.avatar !== newGroup?.avatar
) {
avatarFile.value = await buildFileFromIMedia(newGroup?.avatar);
}
if (
oldGroup?.banner !== undefined &&
oldGroup?.banner !== newGroup?.banner
) {
bannerFile.value = await buildFileFromIMedia(newGroup?.banner);
}
} catch (e) {
// Catch errors while building media
console.error(e);
}
editableGroup.value = { ...newGroup };
},
{
immediate: true,
} }
); });
const buildVariables = computed(() => { const buildVariables = computed(() => {
let avatarObj = {}; let avatarObj = {};

View file

@ -78,13 +78,16 @@
}) })
}} }}
</span> </span>
<span v-if="post.visibility === PostVisibility.UNLISTED" class=""> <span
<o-icon icon="link" size="small" /> v-if="post.visibility === PostVisibility.UNLISTED"
class="flex gap-2 items-center"
>
<Link :size="16" />
{{ t("Accessible only by link") }} {{ t("Accessible only by link") }}
</span> </span>
<span <span
v-else-if="post.visibility === PostVisibility.PRIVATE" v-else-if="post.visibility === PostVisibility.PRIVATE"
class="" class="flex gap-2 items-center"
> >
<Lock :size="16" /> <Lock :size="16" />
{{ {{
@ -275,6 +278,7 @@ import Pencil from "vue-material-design-icons/Pencil.vue";
import Delete from "vue-material-design-icons/Delete.vue"; import Delete from "vue-material-design-icons/Delete.vue";
import Share from "vue-material-design-icons/Share.vue"; import Share from "vue-material-design-icons/Share.vue";
import Flag from "vue-material-design-icons/Flag.vue"; import Flag from "vue-material-design-icons/Flag.vue";
import Link from "vue-material-design-icons/Link.vue";
import { Dialog } from "@/plugins/dialog"; import { Dialog } from "@/plugins/dialog";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { Notifier } from "@/plugins/notifier"; import { Notifier } from "@/plugins/notifier";

View file

@ -4,7 +4,7 @@ import { createRouter, createWebHistory, Router } from "vue-router";
import { routes } from "@/router"; import { routes } from "@/router";
import { CommentModeration, EventJoinOptions } from "@/types/enums"; import { CommentModeration, EventJoinOptions } from "@/types/enums";
import { InMemoryCache } from "@apollo/client/cache"; import { InMemoryCache } from "@apollo/client/cache";
import { beforeEach, describe, expect, vi, it } from "vitest"; import { beforeEach, describe, expect, it } from "vitest";
import Oruga from "@oruga-ui/oruga-next"; import Oruga from "@oruga-ui/oruga-next";
import FloatingVue from "floating-vue"; import FloatingVue from "floating-vue";
@ -34,12 +34,7 @@ const eventData = {
describe("ParticipationSection", () => { describe("ParticipationSection", () => {
let wrapper: VueWrapper; let wrapper: VueWrapper;
const generateWrapper = ( const generateWrapper = (customProps: Record<string, unknown> = {}) => {
customProps: Record<string, unknown> = {},
baseData: Record<string, unknown> = {}
) => {
const cache = new InMemoryCache({ addTypename: false });
wrapper = mount(ParticipationSection, { wrapper = mount(ParticipationSection, {
stubs: { stubs: {
ParticipationButton: true, ParticipationButton: true,

View file

@ -1,6 +1,6 @@
import { config, mount } from "@vue/test-utils"; import { mount } from "@vue/test-utils";
import PostListItem from "@/components/Post/PostListItem.vue"; import PostListItem from "@/components/Post/PostListItem.vue";
import { vi, beforeEach, describe, it, expect } from "vitest"; import { beforeEach, describe, it, expect } from "vitest";
import { enUS } from "date-fns/locale"; import { enUS } from "date-fns/locale";
import { routes } from "@/router"; import { routes } from "@/router";
import { createRouter, createWebHistory, Router } from "vue-router"; import { createRouter, createWebHistory, Router } from "vue-router";

View file

@ -1,9 +1,7 @@
import { config, mount } from "@vue/test-utils"; import { mount } from "@vue/test-utils";
import ReportCard from "@/components/Report/ReportCard.vue"; import ReportCard from "@/components/Report/ReportCard.vue";
import { ActorType } from "@/types/enums"; import { ActorType } from "@/types/enums";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { createI18n } from "vue-i18n";
import en from "@/i18n/en_US.json";
const reportData = { const reportData = {
id: "1", id: "1",

View file

@ -1,5 +1,7 @@
const useRouterMock = vi.fn(() => ({ const useRouterMock = vi.fn(() => ({
push: () => {}, push: function () {
// do nothing
},
})); }));
import { config, mount } from "@vue/test-utils"; import { config, mount } from "@vue/test-utils";

View file

@ -1,8 +1,14 @@
const useRouterMock = vi.fn(() => ({ const useRouterMock = vi.fn(() => ({
push: () => {}, push: function () {
replace: () => {}, // do nothing
},
replace: function () {
// do nothing
},
})); }));
const useRouteMock = vi.fn(() => {}); const useRouteMock = vi.fn(function () {
// do nothing
});
import { config, mount, VueWrapper } from "@vue/test-utils"; import { config, mount, VueWrapper } from "@vue/test-utils";
import Login from "@/views/User/LoginView.vue"; import Login from "@/views/User/LoginView.vue";

View file

@ -1,5 +1,7 @@
const useRouterMock = vi.fn(() => ({ const useRouterMock = vi.fn(() => ({
push: () => {}, push: function () {
// do nothing
},
})); }));
import { shallowMount, VueWrapper } from "@vue/test-utils"; import { shallowMount, VueWrapper } from "@vue/test-utils";
@ -21,7 +23,6 @@ vi.mock("vue-router/dist/vue-router.mjs", () => ({
describe("App component", () => { describe("App component", () => {
let wrapper: VueWrapper; let wrapper: VueWrapper;
let mockClient: MockApolloClient | null; let mockClient: MockApolloClient | null;
let requestHandlers: Record<string, RequestHandler>;
const createComponent = (handlers = {}) => { const createComponent = (handlers = {}) => {
const cache = new InMemoryCache({ addTypename: false }); const cache = new InMemoryCache({ addTypename: false });
@ -31,8 +32,6 @@ describe("App component", () => {
resolvers: buildCurrentUserResolver(cache), resolvers: buildCurrentUserResolver(cache),
}); });
requestHandlers = { ...handlers };
wrapper = shallowMount(NavBar, { wrapper = shallowMount(NavBar, {
// stubs: ["router-link", "router-view", "o-dropdown", "o-dropdown-item"], // stubs: ["router-link", "router-view", "o-dropdown", "o-dropdown-item"],
global: { global: {

File diff suppressed because it is too large Load diff

View file

@ -80,6 +80,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do
} }
} = _resolution } = _resolution
) do ) do
Logger.debug("Getting resource for group with username #{username}")
with {:group, %Actor{id: group_id}} <- {:group, Actors.get_actor_by_name(username, :Group)}, with {:group, %Actor{id: group_id}} <- {:group, Actors.get_actor_by_name(username, :Group)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:resource, %Resource{} = resource} <- {:resource, %Resource{} = resource} <-
@ -222,13 +224,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do
{:ok, data} when is_map(data) -> {:ok, data} when is_map(data) ->
{:ok, struct(Metadata, data)} {:ok, struct(Metadata, data)}
{:error, :invalid_parsed_data} -> {:error, _error_type, _} ->
{:error, dgettext("errors", "Unable to fetch resource details from this URL.")} {:error, dgettext("errors", "Unable to fetch resource details from this URL.")}
{:error, err} ->
Logger.warn("Error while fetching preview from #{inspect(resource_url)}")
Logger.debug(inspect(err))
{:error, :unknown_resource}
end end
end end

View file

@ -29,19 +29,19 @@ defmodule Mobilizon.Service.RichMedia.Parser do
def parse(nil), do: {:error, "No URL provided"} def parse(nil), do: {:error, "No URL provided"}
@spec parse(String.t()) :: {:ok, map()} | {:error, any()} @spec parse(String.t()) :: {:ok, map()} | {:error, :http | :parsing | :unknown, any()}
def parse(url) do def parse(url) do
case Cachex.fetch(:rich_media_cache, url, fn _ -> case Cachex.fetch(:rich_media_cache, url, fn _ ->
case parse_url(url) do case parse_url(url) do
{:ok, data} -> {:commit, data} {:ok, data} -> {:commit, data}
{:error, err} -> {:ignore, err} {:error, error_type, error} -> {:ignore, {error_type, error}}
end end
end) do end) do
{status, value} when status in [:ok, :commit] -> {status, value} when status in [:ok, :commit] ->
{:ok, value} {:ok, value}
{_, err} -> {_, {error_type, err}} ->
{:error, err} {:error, error_type, err}
end end
rescue rescue
e -> e ->
@ -56,7 +56,8 @@ defmodule Mobilizon.Service.RichMedia.Parser do
get_filename_from_headers(response_headers) || get_filename_from_url(url) get_filename_from_headers(response_headers) || get_filename_from_url(url)
end end
@spec parse_url(String.t(), Enum.t()) :: {:ok, map()} | {:error, any()} @spec parse_url(String.t(), Enum.t()) ::
{:ok, map()} | {:error, :http | :parsing | :unknown, any()}
defp parse_url(url, options \\ []) do defp parse_url(url, options \\ []) do
user_agent = Keyword.get(options, :user_agent, default_user_agent(url)) user_agent = Keyword.get(options, :user_agent, default_user_agent(url))
headers = [{"User-Agent", user_agent}] headers = [{"User-Agent", user_agent}]
@ -64,13 +65,14 @@ defmodule Mobilizon.Service.RichMedia.Parser do
try do try do
with {:ok, _} <- prevent_local_address(url), with {:ok, _} <- prevent_local_address(url),
{:ok, %{body: body, status: code, headers: response_headers}} {:fetch, {:ok, %{body: body, status: code, headers: response_headers}}}
when code in 200..299 <- when code in 200..299 <-
RichMediaPreviewClient.get( {:fetch,
url, RichMediaPreviewClient.get(
headers: headers, url,
opts: @options headers: headers,
), opts: @options
)},
{:is_html, _response_headers, true} <- {:is_html, _response_headers, true} <-
{:is_html, response_headers, is_html(response_headers)} do {:is_html, response_headers, is_html(response_headers)} do
body body
@ -87,17 +89,17 @@ defmodule Mobilizon.Service.RichMedia.Parser do
{:ok, data} {:ok, data}
{:ok, err} -> {:fetch, {_, err}} ->
Logger.debug("HTTP error: #{inspect(err)}") Logger.debug("HTTP error: #{inspect(err)}")
{:error, "HTTP error: #{inspect(err)}"} {:error, :http, err}
{:error, err} -> {:error, err} ->
Logger.debug("HTTP error: #{inspect(err)}") Logger.debug("Parsing error: #{inspect(err)}")
{:error, "HTTP error: #{inspect(err)}"} {:error, :parsing, err}
end end
rescue rescue
e -> e ->
{:error, "Parsing error: #{inspect(e)} #{inspect(__STACKTRACE__)}"} {:error, :unknown, "Parsing error: #{inspect(e)} #{inspect(__STACKTRACE__)}"}
end end
end end
@ -228,7 +230,7 @@ defmodule Mobilizon.Service.RichMedia.Parser do
check_parsed_data(data, html, false) check_parsed_data(data, html, false)
else else
Logger.debug("Found metadata was invalid or incomplete: #{inspect(data)}") Logger.debug("Found metadata was invalid or incomplete: #{inspect(data)}")
{:error, :invalid_parsed_data} {:error, :parsing, :invalid_parsed_data}
end end
end end
@ -252,11 +254,11 @@ defmodule Mobilizon.Service.RichMedia.Parser do
validate_ip(host) do validate_ip(host) do
{:ok, url} {:ok, url}
else else
{:error, "Host violates local access rules"} {:error, :local_address, "Host violates local access rules"}
end end
_ -> _ ->
{:error, "Could not detect any host"} {:error, :no_host, "Could not detect any host"}
end end
end end
@ -302,6 +304,8 @@ defmodule Mobilizon.Service.RichMedia.Parser do
{:ok, data} {:ok, data}
end end
defp check_remote_picture_path({:error, _, _} = err), do: err
defp check_remote_picture_path(data), do: {:ok, data} defp check_remote_picture_path(data), do: {:ok, data}
@spec format_url(String.t(), String.t()) :: String.t() @spec format_url(String.t(), String.t()) :: String.t()

View file

@ -67,8 +67,8 @@ defmodule Mobilizon.Service.RichMedia.Parsers.OEmbed do
{:ok, data} <- Jason.decode(json), {:ok, data} <- Jason.decode(json),
data <- data <-
data data
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), String.trim(v)} end) |> Map.new(fn {k, v} -> {k, if(is_binary(v), do: String.trim(v), else: v)} end)
|> Map.take(@oembed_allowed_attributes) do |> Map.take(Enum.map(@oembed_allowed_attributes, &to_string/1)) do
{:ok, data} {:ok, data}
end end
end end

View file

@ -133,7 +133,7 @@ defmodule Mobilizon.Mixfile do
{:phoenix_ecto, "~> 4.0"}, {:phoenix_ecto, "~> 4.0"},
{:postgrex, ">= 0.17.1"}, {:postgrex, ">= 0.17.1"},
{:phoenix_html, "~> 3.0"}, {:phoenix_html, "~> 3.0"},
{:phoenix_live_view, "~> 0.18.3"}, {:phoenix_live_view, "~> 0.19.0"},
{:phoenix_view, "~> 2.0"}, {:phoenix_view, "~> 2.0"},
{:gettext, "~> 0.20.0"}, {:gettext, "~> 0.20.0"},
{:cowboy, "~> 2.6"}, {:cowboy, "~> 2.6"},

View file

@ -8,7 +8,7 @@
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"}, "cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
"castore": {:hex, :castore, "1.0.2", "0c6292ecf3e3f20b7c88408f00096337c4bfd99bd46cc2fe63413ddbe45b3573", [:mix], [], "hexpm", "40b2dd2836199203df8500e4a270f10fc006cc95adc8a319e148dc3077391d96"}, "castore": {:hex, :castore, "1.0.2", "0c6292ecf3e3f20b7c88408f00096337c4bfd99bd46cc2fe63413ddbe45b3573", [:mix], [], "hexpm", "40b2dd2836199203df8500e4a270f10fc006cc95adc8a319e148dc3077391d96"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"cldr_utils": {:hex, :cldr_utils, "2.23.1", "5c7df90f10b2ffe2519124f3c0cba1bfc0a303d91c34c2e130e86c4f7bf1840c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "42242882f76499fc28e59c80da8b832904cbe68ea50eeb42e65350b0dae7640c"}, "cldr_utils": {:hex, :cldr_utils, "2.24.0", "5b356769f2baf9dd25b166e9f1b2a5ce955d5b556bc07f56c959cfff1ec9543e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "3fde251e0bbbe473230561019dd6c2958d82a10c134bb5b8b4e21a694196cf85"},
"codepagex": {:hex, :codepagex, "0.1.6", "49110d09a25ee336a983281a48ef883da4c6190481e0b063afe2db481af6117e", [:mix], [], "hexpm", "1521461097dde281edf084062f525a4edc6a5e49f4fd1f5ec41c9c4955d5bd59"}, "codepagex": {:hex, :codepagex, "0.1.6", "49110d09a25ee336a983281a48ef883da4c6190481e0b063afe2db481af6117e", [:mix], [], "hexpm", "1521461097dde281edf084062f525a4edc6a5e49f4fd1f5ec41c9c4955d5bd59"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
@ -36,7 +36,7 @@
"elixir_feed_parser": {:hex, :elixir_feed_parser, "2.1.0", "bb96fb6422158dc7ad59de62ef211cc69d264acbbe63941a64a5dce97bbbc2e6", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2d3c62fe7b396ee3b73d7160bc8fadbd78bfe9597c98c7d79b3f1038d9cba28f"}, "elixir_feed_parser": {:hex, :elixir_feed_parser, "2.1.0", "bb96fb6422158dc7ad59de62ef211cc69d264acbbe63941a64a5dce97bbbc2e6", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2d3c62fe7b396ee3b73d7160bc8fadbd78bfe9597c98c7d79b3f1038d9cba28f"},
"elixir_make": {:hex, :elixir_make, "0.7.6", "67716309dc5d43e16b5abbd00c01b8df6a0c2ab54a8f595468035a50189f9169", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5a0569756b0f7873a77687800c164cca6dfc03a09418e6fcf853d78991f49940"}, "elixir_make": {:hex, :elixir_make, "0.7.6", "67716309dc5d43e16b5abbd00c01b8df6a0c2ab54a8f595468035a50189f9169", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5a0569756b0f7873a77687800c164cca6dfc03a09418e6fcf853d78991f49940"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"erlport": {:git, "https://github.com/tcitworld/erlport.git", "04bcfd732fa458735001328b44e8b3a1764316a5", [branch: "0.10.1-compat"]}, "erlport": {:git, "https://github.com/tcitworld/erlport.git", "1f8f4b1a50ecdf7e959090fb566ac45c63c39b0b", [branch: "0.10.1-compat"]},
"eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"}, "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},
"ex_cldr": {:hex, :ex_cldr, "2.37.1", "6091fa719a7a96f9abee7aba186e63a906d504d08039cc8f0c683a0e71ee1bd7", [:mix], [{:cldr_utils, "~> 2.21", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "5d60c3288454bc966e404ea4f59531f7dbb570d7e927dce62f0ab8466713bf78"}, "ex_cldr": {:hex, :ex_cldr, "2.37.1", "6091fa719a7a96f9abee7aba186e63a906d504d08039cc8f0c683a0e71ee1bd7", [:mix], [{:cldr_utils, "~> 2.21", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "5d60c3288454bc966e404ea4f59531f7dbb570d7e927dce62f0ab8466713bf78"},
"ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.22.0", "016373c6725682851d752b72585581154926b2c8a20ee43e418479911dae0ff9", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.16", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c1c86de086009dce5786eb10fed0d351e52a844307c2ff0e83531f91d3268e8b"}, "ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.22.0", "016373c6725682851d752b72585581154926b2c8a20ee43e418479911dae0ff9", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.16", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c1c86de086009dce5786eb10fed0d351e52a844307c2ff0e83531f91d3268e8b"},
@ -110,8 +110,8 @@
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.1", "fe7a02387a7d26002a46b97e9879591efee7ebffe5f5e114fd196632e6e4a08d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ddccf8b4966180afe7630b105edb3402b1ca485e7468109540d262e842048ba4"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.1", "fe7a02387a7d26002a46b97e9879591efee7ebffe5f5e114fd196632e6e4a08d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ddccf8b4966180afe7630b105edb3402b1ca485e7468109540d262e842048ba4"},
"phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.19.0", "de5643d03e3cdf5ff19cd45b5d14543a3b1ad8551d529f6b24246e88a6c6f1b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a650b6f814c4f386314b98f1aebf92f8652649166612f84ef2e60a20894addfa"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.2", "a4950b63ace57720b0fc1c6da083c53346b36f99021de89595cc4f026288ff51", [:mix], [], "hexpm", "45741676a94c71f9afdfed9d22d49b6856c026ff504db04e3dc03a1d86f8201c"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"}, "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"},
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"}, "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
@ -134,7 +134,7 @@
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"struct_access": {:hex, :struct_access, "1.1.2", "a42e6ceedd9b9ea090ee94a6da089d56e16f374dbbc010c3eebdf8be17df286f", [:mix], [], "hexpm", "e4c411dcc0226081b95709909551fc92b8feb1a3476108348ea7e3f6c12e586a"}, "struct_access": {:hex, :struct_access, "1.1.2", "a42e6ceedd9b9ea090ee94a6da089d56e16f374dbbc010c3eebdf8be17df286f", [:mix], [], "hexpm", "e4c411dcc0226081b95709909551fc92b8feb1a3476108348ea7e3f6c12e586a"},
"sweet_xml": {:hex, :sweet_xml, "0.7.3", "debb256781c75ff6a8c5cbf7981146312b66f044a2898f453709a53e5031b45b", [:mix], [], "hexpm", "e110c867a1b3fe74bfc7dd9893aa851f0eed5518d0d7cad76d7baafd30e4f5ba"}, "sweet_xml": {:hex, :sweet_xml, "0.7.3", "debb256781c75ff6a8c5cbf7981146312b66f044a2898f453709a53e5031b45b", [:mix], [], "hexpm", "e110c867a1b3fe74bfc7dd9893aa851f0eed5518d0d7cad76d7baafd30e4f5ba"},
"swoosh": {:hex, :swoosh, "1.10.2", "77acdc1261de404b893e24224d47459d1b42deb02577c7b31514e0a720f949d6", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1736faf374ed49c6091845cdfd5b3a68c88c5f2bfd989447d12bffafc0dda03a"}, "swoosh": {:hex, :swoosh, "1.11.0", "00b4fff8c08347a47cc5cbe67d64df5aae0607a7a51171944f5b89216e2d62f5", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5e7c49b6259e50a5ed756517e23a7f916c0b73eb0752e864b1d83b28e2c204d9"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"tesla": {:hex, :tesla, "1.7.0", "a62dda2f80d4f8a925eb7b8c5b78c461e0eb996672719fe1a63b26321a5f8b4e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2e64f01ebfdb026209b47bc651a0e65203fcff4ae79c11efb73c4852b00dc313"}, "tesla": {:hex, :tesla, "1.7.0", "a62dda2f80d4f8a925eb7b8c5b78c461e0eb996672719fe1a63b26321a5f8b4e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2e64f01ebfdb026209b47bc651a0e65203fcff4ae79c11efb73c4852b00dc313"},
"timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"},
@ -144,7 +144,7 @@
"ueberauth_cas": {:hex, :ueberauth_cas, "2.3.1", "df45a1f2c5df8bc80191cbca4baeeed808d697702ec5ebe5bd5d5a264481752f", [:mix], [{:httpoison, "~> 1.8", [hex: :httpoison, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "5068ae2b9e217c2f05aa9a67483a6531e21ba0be9a6f6c8749bb7fd1599be321"}, "ueberauth_cas": {:hex, :ueberauth_cas, "2.3.1", "df45a1f2c5df8bc80191cbca4baeeed808d697702ec5ebe5bd5d5a264481752f", [:mix], [{:httpoison, "~> 1.8", [hex: :httpoison, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "5068ae2b9e217c2f05aa9a67483a6531e21ba0be9a6f6c8749bb7fd1599be321"},
"ueberauth_discord": {:hex, :ueberauth_discord, "0.7.0", "463f6dfe1ed10a76739331ce8e1dd3600ab611f10524dd828eb3aa50e76e9d43", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "d6f98ef91abb4ddceada4b7acba470e0e68c4d2de9735ff2f24172a8e19896b4"}, "ueberauth_discord": {:hex, :ueberauth_discord, "0.7.0", "463f6dfe1ed10a76739331ce8e1dd3600ab611f10524dd828eb3aa50e76e9d43", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "d6f98ef91abb4ddceada4b7acba470e0e68c4d2de9735ff2f24172a8e19896b4"},
"ueberauth_facebook": {:hex, :ueberauth_facebook, "0.10.0", "0d607fbd1b7c6e0449981571027d869c2d156b8ad20c42e3672346678c05ccf1", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "bf8ce5d66b1c50da8abff77e8086c1b710bdde63f4acaef19a651ba43a9537a8"}, "ueberauth_facebook": {:hex, :ueberauth_facebook, "0.10.0", "0d607fbd1b7c6e0449981571027d869c2d156b8ad20c42e3672346678c05ccf1", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "bf8ce5d66b1c50da8abff77e8086c1b710bdde63f4acaef19a651ba43a9537a8"},
"ueberauth_github": {:hex, :ueberauth_github, "0.8.2", "6e12332172ebfadb6fbe2feaeddbd8c421f8ebf9d425d1367c2dce2a35f6bf21", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "98ed414ee313bd754135ab9689e8f16863c5691d11c8526800457cce6153d4e2"}, "ueberauth_github": {:hex, :ueberauth_github, "0.8.3", "1c478629b4c1dae446c68834b69194ad5cead3b6c67c913db6fdf64f37f0328f", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "ae0ab2879c32cfa51d7287a48219b262bfdab0b7ec6629f24160564247493cc6"},
"ueberauth_gitlab_strategy": {:hex, :ueberauth_gitlab_strategy, "0.4.0", "96605d304ebb87ce508eccbeb1f94da9ea1c9da20d8913771b6cf24a6cc6c633", [:mix], [{:oauth2, "~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "e86e2e794bb063c07c05a6b1301b73f2be3ba9308d8f47ecc4d510ef9226091e"}, "ueberauth_gitlab_strategy": {:hex, :ueberauth_gitlab_strategy, "0.4.0", "96605d304ebb87ce508eccbeb1f94da9ea1c9da20d8913771b6cf24a6cc6c633", [:mix], [{:oauth2, "~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "e86e2e794bb063c07c05a6b1301b73f2be3ba9308d8f47ecc4d510ef9226091e"},
"ueberauth_google": {:hex, :ueberauth_google, "0.10.2", "b85b3de6070e7bc71bbec3d4dbe2de805b004ae9c19efeb31531f9134ede4033", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.10.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "fcf987749db5e2d890240ce61223c61ee6ac1d638c3378bf1eeeb0e6332e5a12"}, "ueberauth_google": {:hex, :ueberauth_google, "0.10.2", "b85b3de6070e7bc71bbec3d4dbe2de805b004ae9c19efeb31531f9134ede4033", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.10.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "fcf987749db5e2d890240ce61223c61ee6ac1d638c3378bf1eeeb0e6332e5a12"},
"ueberauth_keycloak_strategy": {:hex, :ueberauth_keycloak_strategy, "0.4.0", "51e975874564ef4a6eb0044b9f0c6a08be4ba6086e62e41d385e7dd52fe9568b", [:mix], [{:oauth2, "~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "c03027937bddcbd9ff499e457f9bb05f79018fa321abf79ebcfed2af0007211b"}, "ueberauth_keycloak_strategy": {:hex, :ueberauth_keycloak_strategy, "0.4.0", "51e975874564ef4a6eb0044b9f0c6a08be4ba6086e62e41d385e7dd52fe9568b", [:mix], [{:oauth2, "~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "c03027937bddcbd9ff499e457f9bb05f79018fa321abf79ebcfed2af0007211b"},