Fix lint issues, update deps

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2022-09-20 16:53:26 +02:00
parent 86ca52c2cb
commit 151a7e54ae
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
61 changed files with 1533 additions and 1579 deletions

View File

@ -1,2 +1,2 @@
elixir 1.13.4-otp-25 elixir 1.14.0-otp-25
erlang 25.0.3 erlang 25.0.4

View File

@ -1,7 +1,7 @@
FROM elixir:latest FROM elixir:latest
LABEL maintainer="Thomas Citharel <tcit@tcit.fr>" LABEL maintainer="Thomas Citharel <tcit@tcit.fr>"
ENV REFRESHED_AT=2022-04-06 ENV REFRESHED_AT=2022-09-20
RUN apt-get update -yq && apt-get install -yq build-essential inotify-tools postgresql-client git curl gnupg xvfb libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 cmake exiftool python3-pip python3-setuptools RUN apt-get update -yq && apt-get install -yq build-essential inotify-tools postgresql-client git curl gnupg xvfb libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 cmake exiftool python3-pip python3-setuptools
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash && apt-get install nodejs -yq RUN curl -sL https://deb.nodesource.com/setup_16.x | bash && apt-get install nodejs -yq
RUN npm install -g yarn wait-on RUN npm install -g yarn wait-on

View File

@ -53,4 +53,7 @@ module.exports = {
}, },
ignorePatterns: ["src/typings/*.d.ts", "vue.config.js"], ignorePatterns: ["src/typings/*.d.ts", "vue.config.js"],
globals: {
GeolocationPositionError: true,
},
}; };

View File

@ -45,6 +45,7 @@
"@tiptap/extension-strike": "^2.0.0-beta.26", "@tiptap/extension-strike": "^2.0.0-beta.26",
"@tiptap/extension-text": "^2.0.0-beta.15", "@tiptap/extension-text": "^2.0.0-beta.15",
"@tiptap/extension-underline": "^2.0.0-beta.7", "@tiptap/extension-underline": "^2.0.0-beta.7",
"@tiptap/suggestion": "^2.0.0-beta.195",
"@tiptap/vue-3": "^2.0.0-beta.96", "@tiptap/vue-3": "^2.0.0-beta.96",
"@vue-a11y/announcer": "^2.1.0", "@vue-a11y/announcer": "^2.1.0",
"@vue-a11y/skip-to": "^2.1.2", "@vue-a11y/skip-to": "^2.1.2",
@ -54,10 +55,9 @@
"@vueuse/core": "^9.1.0", "@vueuse/core": "^9.1.0",
"@vueuse/head": "^0.7.9", "@vueuse/head": "^0.7.9",
"@vueuse/router": "^9.0.2", "@vueuse/router": "^9.0.2",
"@xiaoshuapp/draggable": "^4.1.0",
"apollo-absinthe-upload-link": "^1.5.0", "apollo-absinthe-upload-link": "^1.5.0",
"autoprefixer": "^10", "autoprefixer": "^10",
"blurhash": "^1.1.3", "blurhash": "^2.0.0",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"bulma-divider": "^0.2.0", "bulma-divider": "^0.2.0",
"date-fns": "^2.16.0", "date-fns": "^2.16.0",
@ -82,16 +82,15 @@
"tippy.js": "^6.2.3", "tippy.js": "^6.2.3",
"unfetch": "^4.2.0", "unfetch": "^4.2.0",
"vue": "^3.2.37", "vue": "^3.2.37",
"vue-class-component": "8.0.0-rc.1",
"vue-i18n": "9", "vue-i18n": "9",
"vue-material-design-icons": "^5.1.2", "vue-material-design-icons": "^5.1.2",
"vue-matomo": "^4.1.0", "vue-matomo": "^4.1.0",
"vue-meta": "^2.3.1", "vue-meta": "^2.3.1",
"vue-plausible": "^1.3.1", "vue-plausible": "^1.3.1",
"vue-property-decorator": "10.0.0-rc.3",
"vue-router": "4", "vue-router": "4",
"vue-scrollto": "^2.17.1", "vue-scrollto": "^2.17.1",
"vue-use-route-query": "^1.1.0" "vue-use-route-query": "^1.1.0",
"vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@histoire/plugin-vue": "^0.10.0", "@histoire/plugin-vue": "^0.10.0",
@ -113,7 +112,7 @@
"@types/prosemirror-view": "^1.11.4", "@types/prosemirror-view": "^1.11.4",
"@types/sanitize-html": "^2.5.0", "@types/sanitize-html": "^2.5.0",
"@vitejs/plugin-vue": "^3.0.3", "@vitejs/plugin-vue": "^3.0.3",
"@vitest/ui": "^0.22.1", "@vitest/ui": "^0.23.4",
"@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.0", "@vue/eslint-config-typescript": "^11.0.0",
"@vue/test-utils": "^2.0.2", "@vue/test-utils": "^2.0.2",
@ -130,10 +129,10 @@
"prettier-eslint": "^15.0.1", "prettier-eslint": "^15.0.1",
"rollup-plugin-visualizer": "^5.7.1", "rollup-plugin-visualizer": "^5.7.1",
"sass": "^1.34.1", "sass": "^1.34.1",
"typescript": "~4.7.4", "typescript": "~4.8.3",
"vite": "^3.0.9", "vite": "^3.0.9",
"vite-plugin-pwa": "^0.12.3", "vite-plugin-pwa": "^0.13.0",
"vitest": "^0.22.1", "vitest": "^0.23.3",
"vue-i18n-extract": "^2.0.4" "vue-i18n-extract": "^2.0.4"
} }
} }

View File

@ -153,7 +153,7 @@ body {
/* Modal */ /* Modal */
.modal-content { .modal-content {
@apply bg-white dark:bg-zinc-800 rounded px-2 py-4 w-full; @apply bg-white dark:bg-zinc-800 rounded px-2 py-4 w-full z-0;
} }
/* Switch */ /* Switch */
@ -210,7 +210,7 @@ button.menubar__button {
/* Table */ /* Table */
.table tr { .table tr {
@apply odd:bg-white dark:odd:bg-zinc-600 even:bg-gray-50 dark:even:bg-zinc-700 border-b rounded; @apply odd:bg-white dark:odd:bg-zinc-600 last:border-b-0 even:bg-gray-50 dark:even:bg-zinc-700 border-b rounded;
} }
.table-td { .table-td {

View File

@ -8,6 +8,5 @@
border: 2px solid; border: 2px solid;
z-index: 2; z-index: 2;
flex-shrink: 0; flex-shrink: 0;
} }
} }

View File

@ -62,9 +62,10 @@ const mentionOptions: MentionOptions = {
let popup: any; let popup: any;
return { return {
onStart: (props: any) => { onStart: (props: Record<string, any>) => {
component = new VueRenderer(MentionList, { component = new VueRenderer(MentionList, {
propsData: props, props,
editor: props.editor,
}); });
popup = tippy("body", { popup = tippy("body", {

View File

@ -20,7 +20,7 @@ import { ref, watch } from "vue";
const props = defineProps<{ const props = defineProps<{
items: IPerson[]; items: IPerson[];
command: ({ id }: { id: string }) => {}; command: ({ id }: { id: string }) => any;
}>(); }>();
// @Prop({ type: Function, required: true }) command!: any; // @Prop({ type: Function, required: true }) command!: any;
@ -31,37 +31,37 @@ watch(props.items, () => {
selectedIndex.value = 0; selectedIndex.value = 0;
}); });
const onKeyDown = ({ event }: { event: KeyboardEvent }): boolean => { // const onKeyDown = ({ event }: { event: KeyboardEvent }): boolean => {
if (event.key === "ArrowUp") { // if (event.key === "ArrowUp") {
upHandler(); // upHandler();
return true; // return true;
} // }
if (event.key === "ArrowDown") { // if (event.key === "ArrowDown") {
downHandler(); // downHandler();
return true; // return true;
} // }
if (event.key === "Enter") { // if (event.key === "Enter") {
enterHandler(); // enterHandler();
return true; // return true;
} // }
return false; // return false;
}; // };
const upHandler = (): void => { // const upHandler = (): void => {
selectedIndex.value = // selectedIndex.value =
(selectedIndex.value + props.items.length - 1) % props.items.length; // (selectedIndex.value + props.items.length - 1) % props.items.length;
}; // };
const downHandler = (): void => { // const downHandler = (): void => {
selectedIndex.value = (selectedIndex.value + 1) % props.items.length; // selectedIndex.value = (selectedIndex.value + 1) % props.items.length;
}; // };
const enterHandler = (): void => { // const enterHandler = (): void => {
selectItem(selectedIndex.value); // selectItem(selectedIndex.value);
}; // };
const selectItem = (index: number): void => { const selectItem = (index: number): void => {
const item = props.items[index]; const item = props.items[index];

View File

@ -1,149 +0,0 @@
<template>
<div class="address-autocomplete">
<!-- <o-field expanded>
<o-autocomplete
:data="addressData"
v-model="queryText"
:placeholder="$t('e.g. 10 Rue Jangot')"
field="fullName"
:loading="isFetching"
@typing="fetchAsyncData"
icon="map-marker"
expanded
@select="updateSelected"
v-bind="$attrs"
dir="auto"
>
<template #default="{ option }">
<o-icon :icon="option.poiInfos.poiIcon.icon" />
<b>{{ option.poiInfos.name }}</b
><br />
<small>{{ option.poiInfos.alternativeName }}</small>
</template>
</o-autocomplete>
</o-field>
<o-field
v-if="canDoGeoLocation"
:message="fieldErrors"
:type="{ 'is-danger': fieldErrors.length }"
>
<o-button
type="is-text"
v-if="!gettingLocation"
icon-right="target"
@click="locateMe"
@keyup.enter="locateMe"
>{{ $t("Use my location") }}</o-button
>
<span v-else>{{ $t("Getting location") }}</span>
</o-field> -->
<!--
<div v-if="selected && selected.geom" class="control">
<o-checkbox @input="togglemap" />
<label class="label">{{ $t("Show map") }}</label>
</div>
<div class="map" v-if="showmap && selected && selected.geom">
<map-leaflet
:coords="selected.geom"
:marker="{
text: [selected.poiInfos.name, selected.poiInfos.alternativeName],
icon: selected.poiInfos.poiIcon.icon,
}"
:updateDraggableMarkerCallback="reverseGeoCode"
:options="{ zoom: mapDefaultZoom }"
:readOnly="false"
/>
</div>
-->
</div>
</template>
<script lang="ts">
import { Prop, Watch, Vue } from "vue-property-decorator";
import { Address, IAddress } from "../../types/address.model";
// import AddressAutoCompleteMixin from "@/mixins/AddressAutoCompleteMixin";
// @Component({
// inheritAttrs: false,
// })
export default class AddressAutoComplete extends Vue {
@Prop({ required: false, default: false }) type!: string | false;
@Prop({ required: false, default: true, type: Boolean })
doGeoLocation!: boolean;
addressData: IAddress[] = [];
selected: IAddress = new Address();
initialQueryText = "";
addressModalActive = false;
showmap = false;
get queryText2(): string {
if (this.value !== undefined) {
return new Address(this.value).fullName;
}
return this.initialQueryText;
}
set queryText2(queryText: string) {
this.initialQueryText = queryText;
}
@Watch("value")
updateEditing(): void {
if (!this.value?.id) return;
this.selected = this.value;
}
updateSelected(option: IAddress): void {
if (option == null) return;
this.selected = option;
// this.$emit("input", this.selected);
}
resetPopup(): void {
this.selected = new Address();
}
openNewAddressModal(): void {
this.resetPopup();
this.addressModalActive = true;
}
togglemap(): void {
this.showmap = !this.showmap;
}
get canDoGeoLocation(): boolean {
return this.isSecureContext && this.doGeoLocation;
}
}
</script>
<style lang="scss">
.address-autocomplete {
margin-bottom: 0.75rem;
}
.autocomplete {
.dropdown-menu {
z-index: 2000;
}
.dropdown-item.is-disabled {
opacity: 1 !important;
cursor: auto;
}
}
.read-only {
cursor: pointer;
}
.map {
height: 400px;
width: 100%;
}
</style>

View File

@ -150,7 +150,7 @@ import RouteName from "../../router/name";
import InlineAddress from "@/components/Address/InlineAddress.vue"; import InlineAddress from "@/components/Address/InlineAddress.vue";
import { computed, inject } from "vue"; import { computed, inject } from "vue";
import MobilizonTag from "@/components/Tag.vue"; import MobilizonTag from "@/components/TagElement.vue";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Video from "vue-material-design-icons/Video.vue"; import Video from "vue-material-design-icons/Video.vue";
import { formatDateTimeForEvent } from "@/utils/datetime"; import { formatDateTimeForEvent } from "@/utils/datetime";

View File

@ -84,7 +84,7 @@
</template> </template>
<a <a
target="_blank" target="_blank"
class="hover:underline" class="underline"
rel="noopener noreferrer ugc" rel="noopener noreferrer ugc"
:href="event.onlineAddress" :href="event.onlineAddress"
:title=" :title="

View File

@ -130,7 +130,7 @@ import InlineAddress from "@/components/Address/InlineAddress.vue";
import Video from "vue-material-design-icons/Video.vue"; import Video from "vue-material-design-icons/Video.vue";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import AccountMultiple from "vue-material-design-icons/AccountMultiple.vue"; import AccountMultiple from "vue-material-design-icons/AccountMultiple.vue";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
withDefaults( withDefaults(
defineProps<{ defineProps<{

View File

@ -301,7 +301,7 @@ import {
organizerAvatarUrl, organizerAvatarUrl,
organizerDisplayName, organizerDisplayName,
} from "@/types/event.model"; } from "@/types/event.model";
import { displayNameAndUsername, IActor, IPerson } from "@/types/actor"; import { displayNameAndUsername, IPerson } from "@/types/actor";
import { CURRENT_ACTOR_CLIENT } from "@/graphql/actor"; import { CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
import { changeIdentity } from "@/utils/identity"; import { changeIdentity } from "@/utils/identity";
@ -323,29 +323,12 @@ import { useI18n } from "vue-i18n";
import { Dialog } from "@/plugins/dialog"; import { Dialog } from "@/plugins/dialog";
import { Snackbar } from "@/plugins/snackbar"; import { Snackbar } from "@/plugins/snackbar";
import { useDeleteEvent } from "@/composition/apollo/event"; import { useDeleteEvent } from "@/composition/apollo/event";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
const defaultOptions: IEventCardOptions = { const props = defineProps<{
hideDate: true, participation: IParticipant;
loggedPerson: false, options?: IEventCardOptions;
hideDetails: false, }>();
organizerActor: null,
};
const props = withDefaults(
defineProps<{
participation: IParticipant;
options?: IEventCardOptions;
}>(),
{
options: () => ({
hideDate: true,
loggedPerson: false,
hideDetails: false,
organizerActor: null,
}),
}
);
const emit = defineEmits(["eventDeleted"]); const emit = defineEmits(["eventDeleted"]);
@ -353,10 +336,6 @@ const { result: currentActorResult } = useQuery(CURRENT_ACTOR_CLIENT);
const currentActor = computed(() => currentActorResult.value?.currentActor); const currentActor = computed(() => currentActorResult.value?.currentActor);
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
const mergedOptions = computed<IEventCardOptions>(() => {
return { ...defaultOptions, ...props.options };
});
const dialog = inject<Dialog>("dialog"); const dialog = inject<Dialog>("dialog");
const openDeleteEventModal = ( const openDeleteEventModal = (
@ -441,9 +420,8 @@ onDeleteEventError((error) => {
* Delete the event * Delete the event
*/ */
const openDeleteEventModalWrapper = () => { const openDeleteEventModalWrapper = () => {
openDeleteEventModal( openDeleteEventModal(props.participation.event, (event: IEvent) =>
props.participation.event, deleteEvent({ eventId: event.id ?? "" })
deleteEvent(props.participation.event)
); );
}; };
@ -474,15 +452,15 @@ const gotToWithCheck = async (
return router.push(route); return router.push(route);
}; };
const organizerActor = computed<IActor | undefined>(() => { // const organizerActor = computed<IActor | undefined>(() => {
if ( // if (
props.participation.event.attributedTo && // props.participation.event.attributedTo &&
props.participation.event.attributedTo.id // props.participation.event.attributedTo.id
) { // ) {
return props.participation.event.attributedTo; // return props.participation.event.attributedTo;
} // }
return props.participation.event.organizerActor; // return props.participation.event.organizerActor;
}); // });
const seatsLeft = computed<number | null>(() => { const seatsLeft = computed<number | null>(() => {
if (props.participation.event.options.maximumAttendeeCapacity > 0) { if (props.participation.event.options.maximumAttendeeCapacity > 0) {

View File

@ -143,7 +143,7 @@ const props = withDefaults(
} }
); );
const addressModalActive = ref(false); // const addressModalActive = ref(false);
const componentId = 0; const componentId = 0;
@ -186,14 +186,14 @@ const updateSelected = (option: IAddress): void => {
emit("update:modelValue", selected.value); emit("update:modelValue", selected.value);
}; };
const resetPopup = (): void => { // const resetPopup = (): void => {
selected.value = new Address(); // selected.value = new Address();
}; // };
const openNewAddressModal = (): void => { // const openNewAddressModal = (): void => {
resetPopup(); // resetPopup();
addressModalActive.value = true; // addressModalActive.value = true;
}; // };
const checkCurrentPosition = (e: LatLng): boolean => { const checkCurrentPosition = (e: LatLng): boolean => {
if (!selected.value?.geom) return false; if (!selected.value?.geom) return false;

View File

@ -94,7 +94,7 @@ import ExitToApp from "vue-material-design-icons/ExitToApp.vue";
import DotsHorizontal from "vue-material-design-icons/DotsHorizontal.vue"; import DotsHorizontal from "vue-material-design-icons/DotsHorizontal.vue";
import AccountGroup from "vue-material-design-icons/AccountGroup.vue"; import AccountGroup from "vue-material-design-icons/AccountGroup.vue";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
import { htmlToText } from "@/utils/html"; import { htmlToText } from "@/utils/html";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";

View File

@ -8,6 +8,8 @@
@click="clickMap" @click="clickMap"
@update:zoom="updateZoom" @update:zoom="updateZoom"
:options="{ zoomControl: false }" :options="{ zoomControl: false }"
ref="mapComponent"
@ready="onMapReady"
> >
<l-tile-layer :url="tiles?.endpoint" :attribution="attribution"> <l-tile-layer :url="tiles?.endpoint" :attribution="attribution">
</l-tile-layer> </l-tile-layer>
@ -16,28 +18,36 @@
:zoomInTitle="$t('Zoom in')" :zoomInTitle="$t('Zoom in')"
:zoomOutTitle="$t('Zoom out')" :zoomOutTitle="$t('Zoom out')"
></l-control-zoom> ></l-control-zoom>
<!-- <v-locatecontrol
v-if="canDoGeoLocation"
:options="{ icon: 'mdi mdi-map-marker' }"
/> -->
<l-marker <l-marker
:lat-lng="[lat, lon]" :lat-lng="[lat, lon]"
@add="openPopup" @add="openPopup"
@update:latLng="updateDraggableMarkerPosition" @update:latLng="updateDraggableMarkerPosition"
:draggable="!readOnly" :draggable="!readOnly"
> >
<l-popup v-if="popupMultiLine"> <l-icon>
<MapMarker :size="48" class="text-mbz-purple" />
</l-icon>
<l-popup v-if="popupMultiLine" :options="{ offset: new Point(22, 8) }">
<span v-for="line in popupMultiLine" :key="line" <span v-for="line in popupMultiLine" :key="line"
>{{ line }}<br >{{ line }}<br
/></span> /></span>
</l-popup> </l-popup>
</l-marker> </l-marker>
</l-map> </l-map>
<CrosshairsGps ref="locationIcon" class="hidden" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Icon, LatLng, LeafletMouseEvent, LeafletEvent } from "leaflet"; import {
LatLng,
LeafletMouseEvent,
LeafletEvent,
Control,
DomUtil,
Map,
Point,
} from "leaflet";
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
import { import {
LMap, LMap,
@ -47,10 +57,12 @@ import {
LIcon, LIcon,
LControlZoom, LControlZoom,
} from "@vue-leaflet/vue-leaflet"; } from "@vue-leaflet/vue-leaflet";
// import Vue2LeafletLocateControl from "@/components/Map/Vue2LeafletLocateControl.vue";
import { computed, nextTick, onMounted, ref } from "vue"; import { computed, nextTick, onMounted, ref } from "vue";
import { useMapTiles } from "@/composition/apollo/config"; import { useMapTiles } from "@/composition/apollo/config";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import Locatecontrol from "leaflet.locatecontrol";
import CrosshairsGps from "vue-material-design-icons/CrosshairsGps.vue";
import MapMarker from "vue-material-design-icons/MapMarker.vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -77,8 +89,18 @@ const defaultOptions: {
const zoom = ref(defaultOptions.zoom); const zoom = ref(defaultOptions.zoom);
onMounted(() => { const mapComponent = ref();
const mapObject = ref<Map>();
const locateControl = ref<Control.Locate>();
const locationIcon = ref();
const locationIconHTML = computed(() => locationIcon.value?.$el.innerHTML);
onMounted(async () => {
// this part resolve an issue where the markers would not appear // this part resolve an issue where the markers would not appear
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line no-underscore-dangle
// delete Icon.Default.prototype._getIconUrl; // delete Icon.Default.prototype._getIconUrl;
// Icon.Default.mergeOptions({ // Icon.Default.mergeOptions({
// iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"), // iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
@ -87,6 +109,50 @@ onMounted(() => {
// }); // });
}); });
const onMapReady = async () => {
mapObject.value = mapComponent.value.leafletObject;
mountLocateControl();
};
const mountLocateControl = () => {
if (canDoGeoLocation.value && mapObject.value) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
locateControl.value = new Locatecontrol({
strings: { title: t("Show me where I am") as string },
position: "topleft",
drawCircle: false,
drawMarker: false,
createButtonCallback(container: HTMLElement | undefined, options: any) {
const link = DomUtil.create(
"a",
"leaflet-bar-part leaflet-bar-part-single",
container
);
link.title = options.strings.title;
link.href = "#";
link.setAttribute("role", "button");
const icon = DomUtil.create(
"span",
"material-design-icon rss-icon",
link
);
icon.setAttribute("aria-hidden", "true");
icon.setAttribute("role", "img");
icon.insertAdjacentHTML("beforeend", locationIconHTML.value);
console.log("icon for location", {
link,
icon,
});
return { link, icon };
},
...props.options,
}) as Control.Locate;
locateControl.value?.addTo(mapObject.value);
}
};
const openPopup = async (event: LeafletEvent): Promise<void> => { const openPopup = async (event: LeafletEvent): Promise<void> => {
await nextTick(); await nextTick();
event.target.openPopup(); event.target.openPopup();
@ -147,3 +213,6 @@ div.map-container {
} }
} }
</style> </style>
<style>
@import "leaflet.locatecontrol/dist/L.Control.Locate.css";
</style>

View File

@ -1,64 +0,0 @@
<template>
<div style="display: none">
<slot v-if="ready"></slot>
</div>
</template>
<script lang="ts">
/**
* Fork of https://github.com/domoritz/leaflet-locatecontrol
* to try to trigger location manually (not done ATM)
*/
import { DomEvent } from "leaflet";
// import { findRealParent, propsBinder } from "vue2-leaflet";
import Locatecontrol from "leaflet.locatecontrol";
import { Component, Prop, Vue } from "vue-property-decorator";
@Component({
beforeDestroy() {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.parentContainer.removeLayer(this);
},
})
export default class Vue2LeafletLocateControl extends Vue {
@Prop({ type: Object, default: () => ({}) }) options!: Record<
string,
unknown
>;
@Prop({ type: Boolean, default: true }) visible!: boolean;
ready = false;
mapObject!: any;
parentContainer: any;
mounted(): void {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.mapObject = new Locatecontrol({
...this.options,
strings: { title: this.$t("Show me where I am") as string },
});
DomEvent.on(this.mapObject, this.$listeners as any);
propsBinder(this, this.mapObject, this.$props);
this.ready = true;
this.parentContainer = findRealParent(this.$parent);
this.mapObject.addTo(this.parentContainer.mapObject, !this.visible);
this.$nextTick(() => {
this.$emit("ready", this.mapObject);
});
}
public locate(): void {
this.mapObject.start();
}
}
</script>
<style>
/* @import "~leaflet.locatecontrol/dist/L.Control.Locate.css"; */
</style>

View File

@ -86,6 +86,7 @@ import { IMedia } from "@/types/media.model";
import { computed, ref, watch } from "vue"; import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import Upload from "vue-material-design-icons/Upload.vue"; import Upload from "vue-material-design-icons/Upload.vue";
import { formatBytes } from "@/utils/datetime";
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
@ -140,14 +141,4 @@ watch(imageSrc, () => {
const showImageLoadingError = (): void => { const showImageLoadingError = (): void => {
imagePreviewLoadingError.value = true; imagePreviewLoadingError.value = true;
}; };
// https://gist.github.com/zentala/1e6f72438796d74531803cc3833c039c
const formatBytes = (bytes: number, decimals?: number): string => {
if (bytes == 0) return "0 Bytes";
const k = 1024,
dm = decimals || 2,
sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
};
</script> </script>

View File

@ -61,7 +61,7 @@ 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 MbzTag from "@/components/Tag.vue"; import MbzTag from "@/components/TagElement.vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{

View File

@ -22,12 +22,17 @@
</div> </div>
<div class=""> <div class="">
<div class="" v-if="comment"> <div class="" v-if="comment">
<article class="media"> <article class="">
<div class="media-left"> <div class="">
<figure class="image is-48x48" v-if="comment?.actor?.avatar"> <figure class="h-8 w-8" v-if="comment?.actor?.avatar">
<img :src="comment.actor.avatar.url" alt="" /> <img
:src="comment.actor.avatar.url"
alt=""
width="48"
height="48"
/>
</figure> </figure>
<o-icon v-else size="large" icon="account-circle" /> <AccountCircle v-else :size="48" />
</div> </div>
<div class=""> <div class="">
<div class="prose dark:prose-invert"> <div class="prose dark:prose-invert">
@ -65,7 +70,7 @@
</section> </section>
<footer class="flex gap-2 py-3"> <footer class="flex gap-2 py-3">
<o-button ref="cancelButton" @click="close"> <o-button ref="cancelButton" outlined @click="close">
{{ translatedCancelText }} {{ translatedCancelText }}
</o-button> </o-button>
<o-button <o-button

View File

@ -75,7 +75,7 @@ import ResourceItem from "@/components/Resource/ResourceItem.vue";
import FolderItem from "@/components/Resource/FolderItem.vue"; import FolderItem from "@/components/Resource/FolderItem.vue";
import { ref, watch } from "vue"; import { ref, watch } from "vue";
import { IResource } from "@/types/resource"; import { IResource } from "@/types/resource";
import Draggable from "@xiaoshuapp/draggable"; import Draggable from "vuedraggable";
import { IGroup } from "@/types/actor"; import { IGroup } from "@/types/actor";
const props = withDefaults( const props = withDefaults(
@ -124,13 +124,13 @@ watch(checkedAll, () => {
}); });
}); });
const deleteResource = (resourceID: string) => { // const deleteResource = (resourceID: string) => {
validCheckedResources.value = validCheckedResources.value.filter( // validCheckedResources.value = validCheckedResources.value.filter(
(id) => id !== resourceID // (id) => id !== resourceID
); // );
delete checkedResources.value[resourceID]; // delete checkedResources.value[resourceID];
emit("delete", resourceID); // emit("delete", resourceID);
}; // };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "@/styles/_mixins" as *; @use "@/styles/_mixins" as *;

View File

@ -18,7 +18,7 @@
formatDateTimeString(resource.updatedAt?.toString()) formatDateTimeString(resource.updatedAt?.toString())
}}</span> }}</span>
</div> </div>
<!-- <draggable <draggable
v-if="!inline" v-if="!inline"
class="dropzone" class="dropzone"
v-model="list" v-model="list"
@ -26,7 +26,7 @@
:sort="false" :sort="false"
:group="groupObject" :group="groupObject"
@change="onChange" @change="onChange"
/> --> />
</router-link> </router-link>
<resource-dropdown <resource-dropdown
class="actions" class="actions"
@ -39,18 +39,18 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
// import Draggable, { ChangeEvent } from "@xiaoshuapp/draggable"; import Draggable, { ChangeEvent } from "vuedraggable";
// import { SnackbarProgrammatic as Snackbar } from "buefy";
import { IResource } from "@/types/resource"; import { IResource } from "@/types/resource";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
import { IGroup, usernameWithDomain } from "@/types/actor"; import { IGroup, usernameWithDomain } from "@/types/actor";
import ResourceDropdown from "./ResourceDropdown.vue"; import ResourceDropdown from "./ResourceDropdown.vue";
import { UPDATE_RESOURCE } from "@/graphql/resources"; import { UPDATE_RESOURCE } from "@/graphql/resources";
import { ref } from "vue"; import { inject, ref } from "vue";
import { formatDateTimeString } from "@/filters/datetime"; import { formatDateTimeString } from "@/filters/datetime";
import { useMutation } from "@vue/apollo-composable"; import { useMutation } from "@vue/apollo-composable";
import { resourcePathArray } from "@/components/Resource/utils"; import { resourcePathArray } from "@/components/Resource/utils";
import Folder from "vue-material-design-icons/Folder.vue"; import Folder from "vue-material-design-icons/Folder.vue";
import { Snackbar } from "@/plugins/snackbar";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -77,7 +77,7 @@ const groupObject: Record<string, unknown> = {
const onChange = async (evt: ChangeEvent<IResource>) => { const onChange = async (evt: ChangeEvent<IResource>) => {
if (evt.added && evt.added.element) { if (evt.added && evt.added.element) {
const movedResource = evt.added.element as IResource; // const movedResource = evt.added.element as IResource;
moveResource({ moveResource({
id: props.resource.id, id: props.resource.id,
path: `${props.resource.path}/${props.resource.title}`, path: `${props.resource.path}/${props.resource.title}`,
@ -100,19 +100,20 @@ onMovedResource(({ data }) => {
return router.push({ return router.push({
name: RouteName.RESOURCE_FOLDER, name: RouteName.RESOURCE_FOLDER,
params: { params: {
path: ResourceMixin.resourcePathArray(props.resource), path: resourcePathArray(props.resource),
preferredUsername: props.group.preferredUsername, preferredUsername: props.group.preferredUsername,
}, },
}); });
} }
}); });
const snackbar = inject<Snackbar>("snackbar");
onMovedResourceError((e) => { onMovedResourceError((e) => {
// Snackbar.open({ snackbar?.open({
// message: e.message, message: e.message,
// variant: "danger", variant: "danger",
// position: "bottom", position: "bottom",
// }); });
return undefined; return undefined;
}); });
</script> </script>

View File

@ -60,7 +60,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { IResource, mapServiceTypeToIcon } from "@/types/resource"; import { IResource, mapServiceTypeToIcon } from "@/types/resource";
import ResourceDropdown from "@/components/Resource/ResourceDropdown.vue"; import ResourceDropdown from "@/components/Resource/ResourceDropdown.vue";
import { ref, computed } from "vue"; import { computed } from "vue";
import { formatDateTimeString } from "@/filters/datetime"; import { formatDateTimeString } from "@/filters/datetime";
import Link from "vue-material-design-icons/Link.vue"; import Link from "vue-material-design-icons/Link.vue";

View File

@ -2,13 +2,13 @@
<div v-if="loggedUser"> <div v-if="loggedUser">
<section> <section>
<div class="setting-title"> <div class="setting-title">
<h2>{{ $t("Settings") }}</h2> <h2>{{ t("Settings") }}</h2>
</div> </div>
<div> <div>
<h3>{{ $t("Language") }}</h3> <h3>{{ t("Language") }}</h3>
<p> <p>
{{ {{
$t( t(
"This setting will be used to display the website and send you emails in the correct language." "This setting will be used to display the website and send you emails in the correct language."
) )
}} }}
@ -17,7 +17,7 @@
<o-select <o-select
:loading="loading" :loading="loading"
v-model="locale" v-model="locale"
:placeholder="$t('Select a language')" :placeholder="t('Select a language')"
> >
<option v-for="(language, lang) in langs" :value="lang" :key="lang"> <option v-for="(language, lang) in langs" :value="lang" :key="lang">
{{ language }} {{ language }}
@ -27,15 +27,15 @@
</div> </div>
<div> <div>
<h3>{{ $t("Timezone") }}</h3> <h3>{{ t("Timezone") }}</h3>
<p> <p>
{{ {{
$t( t(
"We use your timezone to make sure you get notifications for an event at the correct time." "We use your timezone to make sure you get notifications for an event at the correct time."
) )
}} }}
{{ {{
$t("Your timezone was detected as {timezone}.", { t("Your timezone was detected as {timezone}.", {
timezone, timezone,
}) })
}} }}
@ -43,7 +43,7 @@
variant="danger" variant="danger"
v-if="!loading && !supportedTimezone" v-if="!loading && !supportedTimezone"
> >
{{ $t("Your timezone {timezone} isn't supported.", { timezone }) }} {{ t("Your timezone {timezone} isn't supported.", { timezone }) }}
</o-notification> </o-notification>
</p> </p>
</div> </div>
@ -71,14 +71,14 @@ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const { loggedUser } = useUserSettings(); const { loggedUser } = useUserSettings();
onMounted(() => { onMounted(() => {
updateLocale(locale.value); updateLocale(locale.value as string);
doUpdateSetting({ timezone }); doUpdateSetting({ timezone });
}); });
watch(locale, () => { watch(locale, () => {
if (locale.value) { if (locale.value) {
updateLocale(locale.value); updateLocale(locale.value as string);
saveLocaleData(locale.value); saveLocaleData(locale.value as string);
} }
}); });

View File

@ -272,11 +272,11 @@ const isBasicMode = computed((): boolean => {
return props.mode === "basic"; return props.mode === "basic";
}); });
const insertMention = (obj: { range: any; attrs: any }) => { // const insertMention = (obj: { range: any; attrs: any }) => {
console.debug("initialize Mention"); // console.debug("initialize Mention");
}; // };
const observer = ref<MutationObserver | null>(null); // const observer = ref<MutationObserver | null>(null);
onMounted(() => { onMounted(() => {
editor.value = new Editor({ editor.value = new Editor({

View File

@ -24,14 +24,14 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// import { SnackbarProgrammatic as Snackbar } from "buefy";
import { ITodo } from "../../types/todos"; import { ITodo } from "../../types/todos";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { UPDATE_TODO } from "../../graphql/todos"; import { UPDATE_TODO } from "../../graphql/todos";
import { computed, ref } from "vue"; import { computed, inject, ref } from "vue";
import { useMutation } from "@vue/apollo-composable"; import { useMutation } from "@vue/apollo-composable";
import Account from "vue-material-design-icons/Account.vue"; import Account from "vue-material-design-icons/Account.vue";
import { formatDateString } from "@/filters/datetime"; import { formatDateString } from "@/filters/datetime";
import { Snackbar } from "@/plugins/snackbar";
const props = defineProps<{ todo: ITodo }>(); const props = defineProps<{ todo: ITodo }>();
@ -41,8 +41,8 @@ const status = computed({
get() { get() {
return props.todo.status; return props.todo.status;
}, },
set(status: boolean) { set(newStatus: boolean) {
updateTodo({ status, id: props.todo.id }); updateTodo({ status: newStatus, id: props.todo.id });
}, },
}); });
@ -52,11 +52,13 @@ onDone(() => {
editMode.value = false; editMode.value = false;
}); });
const snackbar = inject<Snackbar>("snackbar");
onError((e) => { onError((e) => {
// Snackbar.open({ snackbar?.open({
// message: e.message, message: e.message,
// variant: "danger", variant: "danger",
// position: "bottom", position: "bottom",
// }); });
}); });
</script> </script>

View File

@ -1,102 +0,0 @@
<template>
<div v-if="attached && closable" class="tags has-addons">
<span class="tag" :class="[variant, size, { rounded }]">
<!-- <o-icon
v-if="icon"
:icon="icon"
:size="size"
:type="iconType"
/> -->
<span :class="{ 'has-ellipsis': ellipsis }" @click="click">
<slot />
</span>
</span>
<a
class="tag"
role="button"
:aria-label="ariaCloseLabel"
:tabindex="tabstop ? 0 : undefined"
:disabled="disabled"
:class="[
size,
closeType,
{ 'is-rounded': rounded },
closeIcon ? 'has-delete-icon' : 'is-delete',
]"
@click="close"
@keyup.delete.prevent="close"
>
<!-- <o-icon
custom-class=""
v-if="closeIcon"
:icon="closeIcon"
:size="size"
:type="closeIconType"
/> -->
</a>
</div>
<span v-else class="tag" :class="[variant, size, { 'is-rounded': rounded }]">
<!-- <o-icon
v-if="icon"
:icon="icon"
:size="size"
:type="iconType"
/> -->
<span :class="{ 'has-ellipsis': ellipsis }" @click="click">
<slot />
</span>
<a
v-if="closable"
role="button"
:aria-label="ariaCloseLabel"
class="delete is-small"
:class="closeType"
:disabled="disabled"
:tabindex="tabstop ? 0 : undefined"
@click="close"
@keyup.delete.prevent="close"
/>
</span>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{
attached?: boolean;
closable?: boolean;
variant?: string;
size?: string;
rounded?: boolean;
disabled?: boolean;
ellipsis?: boolean;
tabstop?: boolean;
ariaCloseLabel?: string;
icon?: string;
iconType?: string;
closeType?: string;
closeIcon?: string;
closeIconType?: string;
}>(),
{
tabstop: true,
}
);
const emit = defineEmits(["close", "click"]);
/**
* Emit close event when delete button is clicked
* or delete key is pressed.
*/
const close = (event: Event) => {
if (props.disabled) return;
emit("close", event);
};
/**
* Emit click event when tag is clicked.
*/
const click = (event: Event) => {
if (props.disabled) return;
emit("click", event);
};
</script>

View File

@ -26,13 +26,12 @@
<o-field v-if="hasInput"> <o-field v-if="hasInput">
<o-input <o-input
v-model="prompt" v-model="prompt"
expanded
class="input" class="input"
ref="input" ref="input"
:class="{ 'is-danger': validationMessage }"
v-bind="inputAttrs" v-bind="inputAttrs"
@keydown.enter="confirm" @keydown.enter="confirm"
/> />
<p class="help is-danger">{{ validationMessage }}</p>
</o-field> </o-field>
</div> </div>
</section> </section>
@ -83,15 +82,14 @@ const emit = defineEmits(["confirm", "cancel", "close"]);
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
const modalOpened = ref(false); // const modalOpened = ref(false);
const validationMessage = ref("");
const prompt = ref<string>(props.hasInput ? props.inputAttrs?.value ?? "" : ""); const prompt = ref<string>(props.hasInput ? props.inputAttrs?.value ?? "" : "");
const input = ref(); const input = ref();
const dialogClass = computed(() => { // const dialogClass = computed(() => {
return [props.size]; // return [props.size];
}); // });
/** /**
* Icon name (MDI) based on the type. * Icon name (MDI) based on the type.
*/ */
@ -114,10 +112,11 @@ const iconByType = computed(() => {
* Call the onConfirm prop (function) and close the Dialog. * Call the onConfirm prop (function) and close the Dialog.
*/ */
const confirm = () => { const confirm = () => {
console.log("dialog confirmed", input.value.$el);
if (input.value !== undefined) { if (input.value !== undefined) {
if (!input.value.checkValidity()) { const inputElement = input.value.$el.querySelector("input");
validationMessage.value = input.value.validationMessage; if (!inputElement.checkValidity()) {
nextTick(() => input.value.select()); nextTick(() => inputElement.select());
return; return;
} }
} }

View File

@ -54,7 +54,9 @@ const props = withDefaults(
indefinite?: boolean; indefinite?: boolean;
}>(), }>(),
{ {
onAction: () => {}, onAction: () => {
// do nothing
},
cancelText: null, cancelText: null,
variant: "dark", variant: "dark",
position: "bottom-right", position: "bottom-right",

View File

@ -13,14 +13,14 @@ import {
MAPS_TILES, MAPS_TILES,
RESOURCE_PROVIDERS, RESOURCE_PROVIDERS,
RESTRICTIONS, RESTRICTIONS,
ROUTING_TYPE,
SEARCH_CONFIG, SEARCH_CONFIG,
TIMEZONES, TIMEZONES,
UPLOAD_LIMITS, UPLOAD_LIMITS,
} from "@/graphql/config"; } from "@/graphql/config";
import { IAnonymousParticipationConfig, IConfig } from "@/types/config.model"; import { IConfig } from "@/types/config.model";
import { ApolloError } from "@apollo/client/core";
import { useQuery } from "@vue/apollo-composable"; import { useQuery } from "@vue/apollo-composable";
import { computed, ref } from "vue"; import { computed } from "vue";
export function useTimezones() { export function useTimezones() {
const { const {
@ -36,26 +36,19 @@ export function useTimezones() {
} }
export function useAnonymousParticipationConfig() { export function useAnonymousParticipationConfig() {
const anonymousParticipationConfig = ref< const {
IAnonymousParticipationConfig | undefined result: configResult,
>(undefined); error,
loading,
} = useQuery<{
config: Pick<IConfig, "anonymous">;
}>(ANONYMOUS_PARTICIPATION_CONFIG);
const error = ref<ApolloError | null>(null); const anonymousParticipationConfig = computed(
() => configResult.value?.config?.anonymous?.participation
);
if (!anonymousParticipationConfig.value) { return { anonymousParticipationConfig, error, loading };
const { onError, onResult } = useQuery<{
config: Pick<IConfig, "anonymous">;
}>(ANONYMOUS_PARTICIPATION_CONFIG);
onResult(({ data }) => {
anonymousParticipationConfig.value =
data.config?.anonymous?.participation;
});
onError((err) => (error.value = err));
}
return { anonymousParticipationConfig, error };
} }
export function useAnonymousReportsConfig() { export function useAnonymousReportsConfig() {
@ -147,6 +140,15 @@ export function useMapTiles() {
return { tiles, error, loading }; return { tiles, error, loading };
} }
export function useRoutingType() {
const { result, error, loading } = useQuery<{
config: Pick<IConfig, "maps">;
}>(ROUTING_TYPE);
const routingType = computed(() => result.value?.config.maps.routing.type);
return { routingType, error, loading };
}
export function useFeatures() { export function useFeatures() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "features">; config: Pick<IConfig, "features">;

View File

@ -55,7 +55,7 @@ export function useGroup(
name: unref(name), name: unref(name),
...options, ...options,
}), }),
() => ({ enabled: unref(name) !== undefined }) () => ({ enabled: unref(name) !== undefined && unref(name) !== "" })
); );
const group = computed(() => result.value?.group); const group = computed(() => result.value?.group);
return { group, error, loading, onResult, onError, refetch }; return { group, error, loading, onResult, onError, refetch };

View File

@ -1,4 +1,3 @@
import { computed } from "vue";
import { useExportFormats, useUploadLimits } from "./apollo/config"; import { useExportFormats, useUploadLimits } from "./apollo/config";
export const useHost = (): string => { export const useHost = (): string => {

View File

@ -347,6 +347,18 @@ export const MAPS_TILES = gql`
} }
`; `;
export const ROUTING_TYPE = gql`
query RoutingType {
config {
maps {
routing {
type
}
}
}
}
`;
export const FEATURES = gql` export const FEATURES = gql`
query Features { query Features {
config { config {

View File

@ -19,6 +19,7 @@ export class Dialog {
onConfirm, onConfirm,
onCancel, onCancel,
inputAttrs, inputAttrs,
hasInput,
}: { }: {
title?: string; title?: string;
message: string; message: string;
@ -29,7 +30,8 @@ export class Dialog {
size?: string; size?: string;
onConfirm?: (prompt: string) => void; onConfirm?: (prompt: string) => void;
onCancel?: (source: string) => void; onCancel?: (source: string) => void;
inputAttrs: Record<string, any>; inputAttrs?: Record<string, any>;
hasInput?: boolean;
}) { }) {
this.app.config.globalProperties.$oruga.modal.open({ this.app.config.globalProperties.$oruga.modal.open({
component: DialogComponent, component: DialogComponent,
@ -44,6 +46,7 @@ export class Dialog {
onConfirm, onConfirm,
onCancel, onCancel,
inputAttrs, inputAttrs,
hasInput,
}, },
}); });
} }

View File

@ -68,7 +68,7 @@ export const routes = [
{ {
path: "/about", path: "/about",
name: RouteName.ABOUT, name: RouteName.ABOUT,
component: (): Promise<any> => import("@/views/About.vue"), component: (): Promise<any> => import("@/views/AboutView.vue"),
meta: { requiredAuth: false }, meta: { requiredAuth: false },
redirect: { name: RouteName.ABOUT_INSTANCE }, redirect: { name: RouteName.ABOUT_INSTANCE },
children: [ children: [

View File

@ -1,36 +1,53 @@
<template> <template>
<div class="p-6"> <div>
<header class=""> <header class="">
<h2 class="">{{ $t("Pick an identity") }}</h2> <h2 class="">{{ t("Pick an identity") }}</h2>
</header> </header>
<section class=""> <section class="">
<div class="list is-hoverable list-none"> <transition-group
<a tag="ul"
class="my-2 block dark:bg-violet-3 rounded-xl p-2" class="grid grid-cols-1 gap-y-3 m-5 max-w-md"
enter-active-class="duration-300 ease-out"
enter-from-class="transform opacity-0"
enter-to-class="opacity-100"
leave-active-class="duration-200 ease-in"
leave-from-class="opacity-100"
leave-to-class="transform opacity-0"
>
<li
class="relative focus-within:shadow-lg"
v-for="identity in identities" v-for="identity in identities"
:key="identity.id" :key="identity?.id"
:class="{
active: currentIdentity && identity.id === currentIdentity.id,
}"
@click="currentIdentity = identity"
> >
<div class="flex gap-2"> <input
<img class="sr-only peer"
class="rounded" type="radio"
v-if="identity.avatar" :value="identity"
:src="identity.avatar.url" name="availableActors"
alt="" v-model="currentIdentity"
width="48" :id="`availableActor-${identity?.id}`"
height="48" />
/> <label
class="flex flex-wrap p-3 bg-white hover:bg-gray-50 dark:bg-violet-3 dark:hover:bg-violet-3/60 border border-gray-300 rounded-lg cursor-pointer peer-checked:ring-primary peer-checked:ring-2 peer-checked:border-transparent"
:for="`availableActor-${identity?.id}`"
>
<figure class="h-12 w-12" v-if="identity?.avatar">
<img
class="rounded-full h-full w-full object-cover"
:src="identity.avatar.url"
alt=""
width="48"
height="48"
/>
</figure>
<AccountCircle v-else :size="48" /> <AccountCircle v-else :size="48" />
<div class=""> <div class="flex-1">
<p>@{{ identity.preferredUsername }}</p> <h3>{{ identity?.name }}</h3>
<small>{{ identity.name }}</small> <small>{{ `@${identity?.preferredUsername}` }}</small>
</div> </div>
</div> </label>
</a> </li>
</div> </transition-group>
</section> </section>
<slot name="footer" /> <slot name="footer" />
</div> </div>

View File

@ -1,4 +1,3 @@
import { useI18n } from "vue-i18n";
<template> <template>
<div> <div>
<div <div

View File

@ -321,7 +321,7 @@ import {
import { Dialog } from "@/plugins/dialog"; import { Dialog } from "@/plugins/dialog";
import { Notifier } from "@/plugins/notifier"; import { Notifier } from "@/plugins/notifier";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
const EVENTS_PER_PAGE = 10; const EVENTS_PER_PAGE = 10;
const POSTS_PER_PAGE = 10; const POSTS_PER_PAGE = 10;

View File

@ -308,7 +308,7 @@ import {
formatDateTimeString, formatDateTimeString,
} from "@/filters/datetime"; } from "@/filters/datetime";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
const EVENTS_PER_PAGE = 10; const EVENTS_PER_PAGE = 10;
const PARTICIPATIONS_PER_PAGE = 10; const PARTICIPATIONS_PER_PAGE = 10;

View File

@ -1,5 +1,5 @@
<template> <template>
<div v-if="user" class="section"> <div v-if="user">
<breadcrumbs-nav <breadcrumbs-nav
:links="[ :links="[
{ name: RouteName.ADMIN, text: t('Admin') }, { name: RouteName.ADMIN, text: t('Admin') },
@ -18,10 +18,10 @@
<section> <section>
<h2 class="text-lg font-bold mb-3">{{ t("Details") }}</h2> <h2 class="text-lg font-bold mb-3">{{ t("Details") }}</h2>
<div class="flex flex-col"> <div class="flex flex-col">
<div class="overflow-x-auto sm:-mx-6"> <div class="overflow-x-auto">
<div class="inline-block py-2 min-w-full sm:px-2"> <div class="inline-block py-2 min-w-full sm:px-2">
<div class="overflow-hidden shadow-md sm:rounded-lg"> <div class="overflow-hidden shadow-md sm:rounded-lg">
<table v-if="metadata.length > 0" class="min-w-full"> <table v-if="metadata.length > 0" class="table w-full">
<tbody> <tbody>
<tr <tr
class="border-b" class="border-b"
@ -229,48 +229,46 @@
aria-modal aria-modal
> >
<form @submit.prevent="updateUserRole"> <form @submit.prevent="updateUserRole">
<div> <header>
<header> <h2>{{ t("Change user role") }}</h2>
<h2 class="modal-card-title">{{ t("Change user role") }}</h2> </header>
</header> <section>
<section> <o-field>
<o-field> <o-radio
<o-radio v-model="newUser.role"
v-model="newUser.role" :native-value="ICurrentUserRole.ADMINISTRATOR"
:native-value="ICurrentUserRole.ADMINISTRATOR" >
> {{ t("Administrator") }}
{{ t("Administrator") }} </o-radio>
</o-radio> </o-field>
</o-field> <o-field>
<o-field> <o-radio
<o-radio v-model="newUser.role"
v-model="newUser.role" :native-value="ICurrentUserRole.MODERATOR"
:native-value="ICurrentUserRole.MODERATOR" >
> {{ t("Moderator") }}
{{ t("Moderator") }} </o-radio>
</o-radio> </o-field>
</o-field> <o-field>
<o-field> <o-radio
<o-radio v-model="newUser.role"
v-model="newUser.role" :native-value="ICurrentUserRole.USER"
:native-value="ICurrentUserRole.USER" >
> {{ t("User") }}
{{ t("User") }} </o-radio>
</o-radio> </o-field>
</o-field> <o-checkbox v-model="newUser.notify">{{
<o-checkbox v-model="newUser.notify">{{ t("Notify the user of the change")
t("Notify the user of the change") }}</o-checkbox>
}}</o-checkbox> </section>
</section> <footer class="mt-2 flex gap-2">
<footer class="mt-2 flex gap-2"> <o-button @click="isRoleChangeModalActive = false" outlined>{{
<o-button @click="isRoleChangeModalActive = false">{{ t("Close")
t("Close") }}</o-button>
}}</o-button> <o-button native-type="submit" variant="primary">{{
<o-button native-type="submit" variant="primary">{{ t("Change role")
t("Change role") }}</o-button>
}}</o-button> </footer>
</footer>
</div>
</form> </form>
</o-modal> </o-modal>
<o-modal <o-modal
@ -284,24 +282,22 @@
aria-modal aria-modal
> >
<form @submit.prevent="confirmUser"> <form @submit.prevent="confirmUser">
<div> <header>
<header> <h2>{{ t("Confirm user") }}</h2>
<h2>{{ t("Confirm user") }}</h2> </header>
</header> <section>
<section> <o-checkbox v-model="newUser.notify">{{
<o-checkbox v-model="newUser.notify">{{ t("Notify the user of the change")
t("Notify the user of the change") }}</o-checkbox>
}}</o-checkbox> </section>
</section> <footer>
<footer> <o-button @click="isConfirmationModalActive = false">{{
<o-button @click="isConfirmationModalActive = false">{{ t("Close")
t("Close") }}</o-button>
}}</o-button> <o-button native-type="submit" variant="primary">{{
<o-button native-type="submit" variant="primary">{{ t("Confirm user")
t("Confirm user") }}</o-button>
}}</o-button> </footer>
</footer>
</div>
</form> </form>
</o-modal> </o-modal>
</div> </div>
@ -424,7 +420,7 @@ const metadata = computed(
}, },
{ {
key: t("Uploaded media total size"), key: t("Uploaded media total size"),
value: formatBytes(user.value.mediaSize, 2, t("0 Bytes")), value: formatBytes(user.value.mediaSize),
}, },
]; ];
} }

View File

@ -443,10 +443,10 @@ const instanceLanguages = computed({
return codeForLanguage(language); return codeForLanguage(language);
}) })
.filter((code) => code !== undefined) as string[]; .filter((code) => code !== undefined) as string[];
// adminSettings = { settingsToWrite.value = {
// ...adminSettings, ...settingsToWrite.value,
// instanceLanguages: newInstanceLanguages, instanceLanguages: newFilteredInstanceLanguages,
// }; };
}, },
}); });

View File

@ -218,10 +218,7 @@
</p> </p>
<o-dropdown> <o-dropdown>
<template #trigger> <template #trigger>
<o-button <o-button icon-right="dots-horizontal">
icon-right="dots-horizontal"
#default="{ active }"
>
{{ t("Actions") }} {{ t("Actions") }}
</o-button> </o-button>
</template> </template>
@ -260,8 +257,8 @@
<o-dropdown-item <o-dropdown-item
aria-role="listitem" aria-role="listitem"
v-if="canManageEvent || event?.draft" v-if="canManageEvent || event?.draft"
@click="openDeleteEventModalWrapper" @click="openDeleteEventModal"
@keyup.enter="openDeleteEventModalWrapper" @keyup.enter="openDeleteEventModal"
><span class="flex gap-1"> ><span class="flex gap-1">
<Delete /> <Delete />
{{ t("Delete") }} {{ t("Delete") }}
@ -386,7 +383,11 @@
has-modal-card has-modal-card
ref="shareModal" ref="shareModal"
> >
<share-event-modal :event="event" :eventCapacityOK="eventCapacityOK" /> <share-event-modal
v-if="event"
:event="event"
:eventCapacityOK="eventCapacityOK"
/>
</o-modal> </o-modal>
<o-modal <o-modal
v-model:active="isJoinModalActive" v-model:active="isJoinModalActive"
@ -394,34 +395,34 @@
ref="participationModal" ref="participationModal"
:close-button-aria-label="t('Close')" :close-button-aria-label="t('Close')"
> >
<identity-picker v-model="identity"> <identity-picker v-if="identity" v-model="identity">
<template #footer> <template #footer>
<footer class="modal-card-foot"> <footer class="flex gap-2">
<button <o-button
class="button" outlined
ref="cancelButton" ref="cancelButton"
@click="isJoinModalActive = false" @click="isJoinModalActive = false"
@keyup.enter="isJoinModalActive = false" @keyup.enter="isJoinModalActive = false"
> >
{{ t("Cancel") }} {{ t("Cancel") }}
</button> </o-button>
<button <o-button
v-if="identity" v-if="identity"
class="button is-primary" variant="primary"
ref="confirmButton" ref="confirmButton"
@click=" @click="
event?.joinOptions === EventJoinOptions.RESTRICTED event?.joinOptions === EventJoinOptions.RESTRICTED
? joinEventWithConfirmation(identity) ? joinEventWithConfirmation(identity as IPerson)
: joinEvent(identity) : joinEvent(identity as IPerson)
" "
@keyup.enter=" @keyup.enter="
event?.joinOptions === EventJoinOptions.RESTRICTED event?.joinOptions === EventJoinOptions.RESTRICTED
? joinEventWithConfirmation(identity) ? joinEventWithConfirmation(identity as IPerson)
: joinEvent(identity) : joinEvent(identity as IPerson)
" "
> >
{{ t("Confirm my particpation") }} {{ t("Confirm my particpation") }}
</button> </o-button>
</footer> </footer>
</template> </template>
</identity-picker> </identity-picker>
@ -449,7 +450,10 @@
</p> </p>
<form <form
@submit.prevent=" @submit.prevent="
joinEvent(actorForConfirmation, messageForConfirmation) joinEvent(
actorForConfirmation as IPerson,
messageForConfirmation
)
" "
> >
<o-field :label="t('Message')"> <o-field :label="t('Message')">
@ -487,22 +491,13 @@
:can-cancel="['escape', 'outside']" :can-cancel="['escape', 'outside']"
> >
<template #default="props"> <template #default="props">
<!-- <event-map <event-map
:routingType="routingType" :routingType="routingType ?? RoutingType.OPENSTREETMAP"
:address="event.physicalAddress" :address="event.physicalAddress"
@close="props.close" @close="props.close"
/> --> />
</template> </template>
</o-modal> </o-modal>
<dialog
variant="danger"
:title="t('Delete event')"
:message="deleteEventMessage"
:confirmText="t('Delete {eventTitle}', { eventTitle: event?.title })"
:onConfirm="
() => (event?.id ? deleteEvent({ eventId: event?.id }) : null)
"
></dialog>
</div> </div>
</div> </div>
</template> </template>
@ -514,12 +509,14 @@ import {
EventVisibility, EventVisibility,
MemberRole, MemberRole,
ParticipantRole, ParticipantRole,
RoutingType,
} from "@/types/enums"; } from "@/types/enums";
import { import {
EVENT_PERSON_PARTICIPATION, EVENT_PERSON_PARTICIPATION,
EVENT_PERSON_PARTICIPATION_SUBSCRIPTION_CHANGED, // EVENT_PERSON_PARTICIPATION_SUBSCRIPTION_CHANGED,
FETCH_EVENT, FETCH_EVENT,
JOIN_EVENT, JOIN_EVENT,
LEAVE_EVENT,
} from "@/graphql/event"; } from "@/graphql/event";
import { IEvent } from "@/types/event.model"; import { IEvent } from "@/types/event.model";
import { import {
@ -543,7 +540,7 @@ import {
isParticipatingInThisEvent, isParticipatingInThisEvent,
removeAnonymousParticipation, removeAnonymousParticipation,
} from "@/services/AnonymousParticipationStorage"; } from "@/services/AnonymousParticipationStorage";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
import EventMetadataSidebar from "@/components/Event/EventMetadataSidebar.vue"; import EventMetadataSidebar from "@/components/Event/EventMetadataSidebar.vue";
import EventBanner from "@/components/Event/EventBanner.vue"; import EventBanner from "@/components/Event/EventBanner.vue";
import EventMap from "@/components/Event/EventMap.vue"; import EventMap from "@/components/Event/EventMap.vue";
@ -583,19 +580,21 @@ import {
useAnonymousParticipationConfig, useAnonymousParticipationConfig,
useAnonymousReportsConfig, useAnonymousReportsConfig,
useEventCategories, useEventCategories,
useRoutingType,
} from "@/composition/apollo/config"; } from "@/composition/apollo/config";
import { useCreateReport } from "@/composition/apollo/report"; import { useCreateReport } from "@/composition/apollo/report";
import Share from "vue-material-design-icons/Share.vue"; import Share from "vue-material-design-icons/Share.vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useProgrammatic } from "@oruga-ui/oruga-next";
import { Dialog } from "@/plugins/dialog"; 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 { useHead } from "@vueuse/head"; import { useHead } from "@vueuse/head";
import { Snackbar } from "@/plugins/snackbar";
const ShareEventModal = defineAsyncComponent( const ShareEventModal = defineAsyncComponent(
() => import("@/components/Event/ShareEventModal.vue") () => import("@/components/Event/ShareEventModal.vue")
); );
/* eslint-disable @typescript-eslint/no-unused-vars */
const IntegrationTwitch = defineAsyncComponent( const IntegrationTwitch = defineAsyncComponent(
() => import("@/components/Event/Integrations/TwitchIntegration.vue") () => import("@/components/Event/Integrations/TwitchIntegration.vue")
); );
@ -611,6 +610,7 @@ const IntegrationJitsiMeet = defineAsyncComponent(
const IntegrationEtherpad = defineAsyncComponent( const IntegrationEtherpad = defineAsyncComponent(
() => import("@/components/Event/Integrations/EtherpadIntegration.vue") () => import("@/components/Event/Integrations/EtherpadIntegration.vue")
); );
/* eslint-enable @typescript-eslint/no-unused-vars */
const props = defineProps<{ const props = defineProps<{
uuid: string; uuid: string;
@ -629,7 +629,7 @@ const currentActorId = computed(() => currentActor.value?.id);
const { loggedUser } = useLoggedUser(); const { loggedUser } = useLoggedUser();
const { const {
result: participationsResult, result: participationsResult,
subscribeToMore: subscribeToMoreParticipation, // subscribeToMore: subscribeToMoreParticipation,
} = useQuery<{ person: IPerson }>( } = useQuery<{ person: IPerson }>(
EVENT_PERSON_PARTICIPATION, EVENT_PERSON_PARTICIPATION,
() => ({ () => ({
@ -653,7 +653,7 @@ const {
// })); // }));
const participations = computed( const participations = computed(
() => participationsResult.value?.person.participations.elements ?? [] () => participationsResult.value?.person.participations?.elements ?? []
); );
const { person } = usePersonStatusGroup( const { person } = usePersonStatusGroup(
@ -680,7 +680,7 @@ const { identities } = useCurrentUserIdentities();
// }; // };
// }, // },
const identity = ref<IPerson | null>(null); const identity = ref<IPerson | undefined | null>(null);
const reportModal = ref(); const reportModal = ref();
const oldParticipationRole = ref<string | undefined>(undefined); const oldParticipationRole = ref<string | undefined>(undefined);
@ -786,12 +786,6 @@ onMounted(async () => {
// return router.push({ name: RouteName.HOME }); // return router.push({ name: RouteName.HOME });
// }); // });
}); });
/**
* Delete the event, then redirect to home.
*/
const openDeleteEventModalWrapper = async (): Promise<void> => {
await openDeleteEventModal(event.value);
};
const deleteEventMessage = computed(() => { const deleteEventMessage = computed(() => {
const participantsLength = event.value?.participantStats.participant; const participantsLength = event.value?.participantStats.participant;
@ -814,17 +808,62 @@ const deleteEventMessage = computed(() => {
})}`; })}`;
}); });
const { mutate: deleteEvent } = useDeleteEvent(); const notifier = inject<Notifier>("notifier");
const dialog = inject<Dialog>("dialog");
const openDeleteEventModal = async ( const {
event: IEvent | undefined mutate: deleteEvent,
): Promise<void> => { onDone: onDeleteEventDone,
function escapeRegExp(string: string) { onError: onDeleteEventError,
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string } = useDeleteEvent();
}
const escapeRegExp = (string: string) => {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}; };
const notifier = inject<Notifier>("notifier"); const openDeleteEventModal = () => {
dialog?.prompt({
title: t("Delete event"),
message: deleteEventMessage.value,
confirmText: t("Delete event"),
cancelText: t("Cancel"),
variant: "danger",
hasIcon: true,
hasInput: true,
inputAttrs: {
placeholder: event.value?.title,
pattern: escapeRegExp(event.value?.title ?? ""),
},
onConfirm: (result: string) => {
console.log("calling delete event", result);
if (result.trim() === event.value?.title) {
event.value?.id ? deleteEvent({ eventId: event.value?.id }) : null;
}
},
});
};
onDeleteEventDone(() => {
router.push({ name: RouteName.MY_EVENTS });
});
onDeleteEventError((error) => {
console.error(error);
});
const {
mutate: createReportMutation,
onDone: onCreateReportDone,
onError: onCreateReportError,
} = useCreateReport();
onCreateReportDone(() => {
notifier?.success(t("Event {eventTitle} reported", { eventTitle }));
});
onCreateReportError((error) => {
console.error(error);
});
const reportEvent = async ( const reportEvent = async (
content: string, content: string,
@ -836,22 +875,12 @@ const reportEvent = async (
reportModal.value.close(); reportModal.value.close();
if (!organizer.value) return; if (!organizer.value) return;
const { mutate, onDone, onError } = useCreateReport(); createReportMutation({
mutate({
eventId: event.value?.id ?? "", eventId: event.value?.id ?? "",
reportedId: organizer.value?.id ?? "", reportedId: organizer.value?.id ?? "",
content, content,
forward, forward,
}); });
onDone(() => {
notifier?.success(t("Event {eventTitle} reported", { eventTitle }));
});
onError((error) => {
console.error(error);
});
}; };
const joinEventWithConfirmation = (actor: IPerson): void => { const joinEventWithConfirmation = (actor: IPerson): void => {
@ -876,7 +905,7 @@ const {
const participationCachedData = store.readQuery<{ person: IPerson }>({ const participationCachedData = store.readQuery<{ person: IPerson }>({
query: EVENT_PERSON_PARTICIPATION, query: EVENT_PERSON_PARTICIPATION,
variables: { eventId: event.value?.id, actorId: identity.value.id }, variables: { eventId: event.value?.id, actorId: identity.value?.id },
}); });
if (participationCachedData?.person == undefined) { if (participationCachedData?.person == undefined) {
@ -887,7 +916,7 @@ const {
} }
store.writeQuery({ store.writeQuery({
query: EVENT_PERSON_PARTICIPATION, query: EVENT_PERSON_PARTICIPATION,
variables: { eventId: event.value?.id, actorId: identity.value.id }, variables: { eventId: event.value?.id, actorId: identity.value?.id },
data: { data: {
person: { person: {
...participationCachedData?.person, ...participationCachedData?.person,
@ -934,14 +963,14 @@ const {
})); }));
const joinEvent = async ( const joinEvent = async (
identity: IPerson, identityForJoin: IPerson,
message: string | null = null message: string | null = null
): Promise<void> => { ): Promise<void> => {
isJoinConfirmationModalActive.value = false; isJoinConfirmationModalActive.value = false;
isJoinModalActive.value = false; isJoinModalActive.value = false;
joinEventMutation({ joinEventMutation({
eventId: event.value?.id, eventId: event.value?.id,
actorId: identity?.id, actorId: identityForJoin?.id,
message, message,
}); });
}; };
@ -960,8 +989,6 @@ onJoinEventMutationError((error) => {
console.error(error); console.error(error);
}); });
const dialog = inject<Dialog>("dialog");
const confirmLeave = (): void => { const confirmLeave = (): void => {
dialog?.confirm({ dialog?.confirm({
title: t('Leaving event "{title}"', { title: t('Leaving event "{title}"', {
@ -975,10 +1002,11 @@ const confirmLeave = (): void => {
), ),
confirmText: t("Leave event"), confirmText: t("Leave event"),
cancelText: t("Cancel"), cancelText: t("Cancel"),
type: "danger", variant: "danger",
hasIcon: true, hasIcon: true,
onConfirm: () => { onConfirm: () => {
if (currentActor.value?.id) { if (event.value && currentActor.value?.id) {
console.log("calling leave event");
leaveEvent(event.value, currentActor.value.id); leaveEvent(event.value, currentActor.value.id);
} }
}, },
@ -1071,14 +1099,14 @@ onFetchEventError(({ graphQLErrors }) =>
handleErrors(graphQLErrors as AbsintheGraphQLErrors) handleErrors(graphQLErrors as AbsintheGraphQLErrors)
); );
const actorIsParticipant = computed((): boolean => { // const actorIsParticipant = computed((): boolean => {
if (actorIsOrganizer.value) return true; // if (actorIsOrganizer.value) return true;
return ( // return (
participations.value.length > 0 && // participations.value.length > 0 &&
participations.value[0].role === ParticipantRole.PARTICIPANT // participations.value[0].role === ParticipantRole.PARTICIPANT
); // );
}); // });
const actorIsOrganizer = computed((): boolean => { const actorIsOrganizer = computed((): boolean => {
return ( return (
@ -1101,11 +1129,11 @@ const canManageEvent = computed((): boolean => {
return actorIsOrganizer.value || hasGroupPrivileges.value; return actorIsOrganizer.value || hasGroupPrivileges.value;
}); });
const endDate = computed((): string | undefined => { // const endDate = computed((): string | undefined => {
return event.value?.endsOn && event.value.endsOn > event.value.beginsOn // return event.value?.endsOn && event.value.endsOn > event.value.beginsOn
? event.value.endsOn // ? event.value.endsOn
: event.value?.beginsOn; // : event.value?.beginsOn;
}); // });
const maximumAttendeeCapacity = computed((): number | undefined => { const maximumAttendeeCapacity = computed((): number | undefined => {
return event.value?.options?.maximumAttendeeCapacity; return event.value?.options?.maximumAttendeeCapacity;
@ -1122,21 +1150,22 @@ const eventCapacityOK = computed((): boolean => {
); );
}); });
const numberOfPlacesStillAvailable = computed((): number | undefined => { // const numberOfPlacesStillAvailable = computed((): number | undefined => {
if (event.value?.draft) return maximumAttendeeCapacity.value; // if (event.value?.draft) return maximumAttendeeCapacity.value;
return ( // return (
(maximumAttendeeCapacity.value ?? 0) - // (maximumAttendeeCapacity.value ?? 0) -
(event.value?.participantStats.participant ?? 0) // (event.value?.participantStats.participant ?? 0)
); // );
}); // });
const anonymousParticipationConfirmed = async (): Promise<boolean> => { const anonymousParticipationConfirmed = async (): Promise<boolean> => {
return isParticipatingInThisEvent(props.uuid); return isParticipatingInThisEvent(props.uuid);
}; };
const cancelAnonymousParticipation = async (): Promise<void> => { const cancelAnonymousParticipation = async (): Promise<void> => {
if (!event.value || !anonymousActorId.value) return;
const token = (await getLeaveTokenForParticipation(props.uuid)) as string; const token = (await getLeaveTokenForParticipation(props.uuid)) as string;
await leaveEvent(event.value, anonymousActorId.value, token); leaveEvent(event.value, anonymousActorId.value, token);
await removeAnonymousParticipation(props.uuid); await removeAnonymousParticipation(props.uuid);
anonymousParticipation.value = null; anonymousParticipation.value = null;
}; };
@ -1192,19 +1221,134 @@ const integrations = computed((): Record<string, IEventMetadataDescription> => {
const showMap = ref(false); const showMap = ref(false);
const routingType = computed((): string | undefined => { const { routingType } = useRoutingType();
return config.value?.maps?.routing?.type;
});
const eventCategory = computed((): string | undefined => { const eventCategory = computed((): string | undefined => {
if (event.value?.category === "MEETING") { if (event.value?.category === "MEETING") {
return undefined; return undefined;
} }
return (eventCategories.value ?? []).find((eventCategory) => { return (eventCategories.value ?? []).find((eventCategoryToFind) => {
return eventCategory.id === event.value?.category; return eventCategoryToFind.id === event.value?.category;
})?.label as string; })?.label as string;
}); });
const {
mutate: leaveEventMutation,
onDone: onLeaveEventMutationDone,
onError: onLeaveEventMutationError,
} = useMutation<{ leaveEvent: { actor: { id: string } } }>(LEAVE_EVENT, () => ({
update: (
store: ApolloCache<{
leaveEvent: IParticipant;
}>,
{ data }: FetchResult,
{ context, variables }
) => {
if (data == null) return;
let participation;
const token = context?.token;
const actorId = variables?.actorId;
const localEventId = variables?.eventId;
const eventUUID = context?.eventUUID;
const isAnonymousParticipationConfirmed =
context?.isAnonymousParticipationConfirmed;
if (!token) {
const participationCachedData = store.readQuery<{
person: IPerson;
}>({
query: EVENT_PERSON_PARTICIPATION,
variables: { eventId: localEventId, actorId },
});
if (participationCachedData == null) return;
const { person: cachedPerson } = participationCachedData;
[participation] = cachedPerson?.participations?.elements ?? [undefined];
store.modify({
id: `Person:${actorId}`,
fields: {
participations() {
return {
elements: [],
total: 0,
};
},
},
});
}
const eventCachedData = store.readQuery<{ event: IEvent }>({
query: FETCH_EVENT,
variables: { uuid: eventUUID },
});
if (eventCachedData == null) return;
const { event: eventCached } = eventCachedData;
if (eventCached === null) {
console.error("Cannot update event cache, because of null value.");
return;
}
const participantStats = { ...eventCached.participantStats };
if (participation && participation?.role === ParticipantRole.NOT_APPROVED) {
participantStats.notApproved -= 1;
} else if (isAnonymousParticipationConfirmed === false) {
participantStats.notConfirmed -= 1;
} else {
participantStats.going -= 1;
participantStats.participant -= 1;
}
store.writeQuery({
query: FETCH_EVENT,
variables: { uuid: eventUUID },
data: {
event: {
...eventCached,
participantStats,
},
},
});
},
}));
const leaveEvent = (
eventToLeave: IEvent,
actorId: string,
token: string | null = null,
isAnonymousParticipationConfirmed: boolean | null = null
): void => {
leaveEventMutation(
{
eventId: eventToLeave.id,
actorId,
token,
},
{
context: {
token,
isAnonymousParticipationConfirmed,
eventUUID: eventToLeave.uuid,
},
}
);
};
onLeaveEventMutationDone(({ data }) => {
if (data) {
notifier?.success(t("You have cancelled your participation"));
}
});
const snackbar = inject<Snackbar>("snackbar");
onLeaveEventMutationError((error) => {
snackbar?.open({
message: error.message,
variant: "danger",
position: "bottom",
});
console.error(error);
});
useHead({ useHead({
title: computed(() => eventTitle.value ?? ""), title: computed(() => eventTitle.value ?? ""),
meta: [{ name: "description", content: eventDescription.value }], meta: [{ name: "description", content: eventDescription.value }],

View File

@ -97,7 +97,6 @@
<event-participation-card <event-participation-card
v-if="'role' in element" v-if="'role' in element"
:participation="element" :participation="element"
:options="{ hideDate: false }"
@event-deleted="eventDeleted" @event-deleted="eventDeleted"
class="participation" class="participation"
/> />

View File

@ -283,7 +283,7 @@ import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Incognito from "vue-material-design-icons/Incognito.vue"; import Incognito from "vue-material-design-icons/Incognito.vue";
import EmptyContent from "@/components/Utils/EmptyContent.vue"; import EmptyContent from "@/components/Utils/EmptyContent.vue";
import { Notifier } from "@/plugins/notifier"; import { Notifier } from "@/plugins/notifier";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
const PARTICIPANTS_PER_PAGE = 10; const PARTICIPANTS_PER_PAGE = 10;
const MESSAGE_ELLIPSIS_LENGTH = 130; const MESSAGE_ELLIPSIS_LENGTH = 130;

View File

@ -267,7 +267,7 @@ import {
import { formatTimeString, formatDateString } from "@/filters/datetime"; import { formatTimeString, formatDateString } from "@/filters/datetime";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import { Notifier } from "@/plugins/notifier"; import { Notifier } from "@/plugins/notifier";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });

View File

@ -135,7 +135,7 @@ import { IGroup, usernameWithDomain, displayName } from "@/types/actor";
import { ActivityType } from "@/types/enums"; import { ActivityType } from "@/types/enums";
import { Paginate } from "@/types/paginate"; import { Paginate } from "@/types/paginate";
import { IActivity } from "../../types/activity.model"; import { IActivity } from "../../types/activity.model";
import Observer from "../../components/Utils/Observer.vue"; import Observer from "../../components/Utils/ObserverElement.vue";
import SkeletonActivityItem from "../../components/Activity/SkeletonActivityItem.vue"; import SkeletonActivityItem from "../../components/Activity/SkeletonActivityItem.vue";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import TimelineText from "vue-material-design-icons/TimelineText.vue"; import TimelineText from "vue-material-design-icons/TimelineText.vue";

View File

@ -219,7 +219,7 @@ import {
displayName, displayName,
} from "@/types/actor"; } from "@/types/actor";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
import LazyImageWrapper from "@/components/Image/LazyImageWrapper.vue"; import LazyImageWrapper from "@/components/Image/LazyImageWrapper.vue";
import ActorInline from "@/components/Account/ActorInline.vue"; import ActorInline from "@/components/Account/ActorInline.vue";
import { formatDistanceToNowStrict, Locale } from "date-fns"; import { formatDistanceToNowStrict, Locale } from "date-fns";

View File

@ -202,7 +202,6 @@ import {
InternalRefetchQueriesInclude, InternalRefetchQueriesInclude,
} from "@apollo/client/core"; } from "@apollo/client/core";
import { useMutation, useQuery } from "@vue/apollo-composable"; import { useMutation, useQuery } from "@vue/apollo-composable";
import { useCurrentActorClient } from "@/composition/apollo/actor";
import { computed, nextTick, reactive, ref, watch } from "vue"; import { computed, nextTick, reactive, ref, watch } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { integerTransformer, useRouteQuery } from "vue-use-route-query"; import { integerTransformer, useRouteQuery } from "vue-use-route-query";
@ -275,7 +274,7 @@ const moveModal = ref(false);
const renameModal = ref(false); const renameModal = ref(false);
const modalError = ref(""); const modalError = ref("");
const resourceRenameInput = ref<HTMLElement>(); const resourceRenameInput = ref<any>();
const modalNewResourceInput = ref<HTMLElement>(); const modalNewResourceInput = ref<HTMLElement>();
const modalNewResourceLinkInput = ref<HTMLElement>(); const modalNewResourceLinkInput = ref<HTMLElement>();
@ -307,7 +306,7 @@ const {
refetchQueries: () => postRefreshQueries(), refetchQueries: () => postRefreshQueries(),
})); }));
createResourceDone(({ data }) => { createResourceDone(() => {
createLinkResourceModal.value = false; createLinkResourceModal.value = false;
createResourceModal.value = false; createResourceModal.value = false;
newResource.title = ""; newResource.title = "";
@ -447,25 +446,25 @@ const { mutate: deleteResource, onError: onDeleteResourceError } = useMutation(
onDeleteResourceError((e) => console.error(e)); onDeleteResourceError((e) => console.error(e));
const handleRename = async (resource: IResource): Promise<void> => { const handleRename = async (resourceToRename: IResource): Promise<void> => {
renameModal.value = true; renameModal.value = true;
updatedResource.value = { ...resource }; updatedResource.value = { ...resourceToRename };
await nextTick(); await nextTick();
resourceRenameInput.value?.$el.focus(); resourceRenameInput.value?.$el.focus();
resourceRenameInput.value?.$el.querySelector("input")?.select(); resourceRenameInput.value?.$el.querySelector("input")?.select();
}; };
const handleMove = (resource: IResource): void => { const handleMove = (resourceToMove: IResource): void => {
moveModal.value = true; moveModal.value = true;
updatedResource.value = { ...resource }; updatedResource.value = { ...resourceToMove };
}; };
const moveResource = async ( const moveResource = async (
resource: IResource, resourceToMove: IResource,
oldParent: IResource | undefined oldParent: IResource | undefined
): Promise<void> => { ): Promise<void> => {
const parentPath = oldParent && oldParent.path ? oldParent.path || "/" : "/"; const parentPath = oldParent && oldParent.path ? oldParent.path || "/" : "/";
await updateResource(resource, parentPath); await updateResource(resourceToMove, parentPath);
moveModal.value = false; moveModal.value = false;
}; };
@ -501,10 +500,10 @@ const { mutate: updateResourceMutation } = useMutation<{
console.error("Cannot update resource cache, because of null value."); console.error("Cannot update resource cache, because of null value.");
return; return;
} }
const updatedResource: IResource = data.updateResource; const postUpdatedResource: IResource = data.updateResource;
const updatedElementList = oldParentCachedResource.children.elements.filter( const updatedElementList = oldParentCachedResource.children.elements.filter(
(cachedResource) => cachedResource.id !== updatedResource.id (cachedResource) => cachedResource.id !== postUpdatedResource.id
); );
store.writeQuery({ store.writeQuery({
@ -526,14 +525,14 @@ const { mutate: updateResourceMutation } = useMutation<{
console.debug("Finished removing ressource from old parent"); console.debug("Finished removing ressource from old parent");
console.debug("Adding resource to new parent"); console.debug("Adding resource to new parent");
if (!updatedResource.parent || !updatedResource.parent.path) { if (!postUpdatedResource.parent || !postUpdatedResource.parent.path) {
console.debug("No cache found for new parent"); console.debug("No cache found for new parent");
return; return;
} }
const newParentCachedData = store.readQuery<{ resource: IResource }>({ const newParentCachedData = store.readQuery<{ resource: IResource }>({
query: GET_RESOURCE, query: GET_RESOURCE,
variables: { variables: {
path: updatedResource.parent.path, path: postUpdatedResource.parent.path,
username: resource.value.actor.preferredUsername, username: resource.value.actor.preferredUsername,
}, },
}); });
@ -547,7 +546,7 @@ const { mutate: updateResourceMutation } = useMutation<{
store.writeQuery({ store.writeQuery({
query: GET_RESOURCE, query: GET_RESOURCE,
variables: { variables: {
path: updatedResource.parent.path, path: postUpdatedResource.parent.path,
username: resource.value.actor.preferredUsername, username: resource.value.actor.preferredUsername,
}, },
data: { data: {
@ -565,15 +564,15 @@ const { mutate: updateResourceMutation } = useMutation<{
})); }));
const updateResource = async ( const updateResource = async (
resource: IResource, resourceToUpdate: IResource,
parentPath: string | null = null parentPath: string | null = null
): Promise<void> => { ): Promise<void> => {
updateResourceMutation( updateResourceMutation(
{ {
id: resource.id, id: resourceToUpdate.id,
title: resource.title, title: resourceToUpdate.title,
parentId: resource.parent ? resource.parent.id : null, parentId: resourceToUpdate.parent ? resourceToUpdate.parent.id : null,
path: resource.path, path: resourceToUpdate.path,
}, },
{ context: { parentPath } } { context: { parentPath } }
); );

View File

@ -1,5 +1,5 @@
import { mount } from "@vue/test-utils"; import { mount } from "@vue/test-utils";
import Tag from "@/components/Tag.vue"; import Tag from "@/components/TagElement.vue";
import { it, expect } from "vitest"; import { it, expect } from "vitest";
const tagContent = "My tag"; const tagContent = "My tag";

File diff suppressed because it is too large Load Diff

View File

@ -23,11 +23,19 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
@spec find_user(any(), map(), Absinthe.Resolution.t()) :: {:ok, User.t()} | {:error, String.t()} @spec find_user(any(), map(), Absinthe.Resolution.t()) :: {:ok, User.t()} | {:error, String.t()}
def find_user(_parent, %{id: id}, %{context: %{current_user: %User{role: role}}}) def find_user(_parent, %{id: id}, %{context: %{current_user: %User{role: role}}})
when is_moderator(role) do when is_moderator(role) do
with {:ok, %User{} = user} <- Users.get_user_with_actors(id) do case Users.get_user_with_actors(id) do
{:ok, user} {:ok, %User{} = user} ->
{:ok, user}
_ ->
{:error, :user_not_found}
end end
end end
def find_user(_parent, _args, _resolution) do
{:error, :unauthorized}
end
@doc """ @doc """
Return current logged-in user Return current logged-in user
""" """

View File

@ -2,9 +2,10 @@
<% :event_comment_mention -> %> <% :event_comment_mention -> %>
<%= dgettext("activity", "%{profile} mentionned you in a comment under event %{event}.", %{ <%= dgettext("activity", "%{profile} mentionned you in a comment under event %{event}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
}) })
@ -12,9 +13,10 @@
<% :participation_event_comment -> %> <% :participation_event_comment -> %>
<%= dgettext("activity", "%{profile} has posted an announcement under event %{event}.", %{ <%= dgettext("activity", "%{profile} has posted an announcement under event %{event}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
}) })
@ -23,9 +25,10 @@
<%= if @activity.subject_params["comment_reply_to"] do %> <%= if @activity.subject_params["comment_reply_to"] do %>
<%= dgettext("activity", "%{profile} has posted a new reply under your event %{event}.", %{ <%= dgettext("activity", "%{profile} has posted a new reply under your event %{event}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}#comment-#{@activity.subject_params["comment_reply_to_uuid"]}-#{@activity.subject_params["comment_uuid"]}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}#comment-#{@activity.subject_params["comment_reply_to_uuid"]}-#{@activity.subject_params["comment_uuid"]}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
}) })
@ -36,9 +39,10 @@
"%{profile} has posted a new comment under your event %{event}.", "%{profile} has posted a new comment under your event %{event}.",
%{ %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}#comment-#{@activity.subject_params["comment_uuid"]}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}#comment-#{@activity.subject_params["comment_uuid"]}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
} }

View File

@ -2,9 +2,10 @@
<% :event_created -> %> <% :event_created -> %>
<%= dgettext("activity", "The event %{event} was created by %{profile}.", %{ <%= dgettext("activity", "The event %{event} was created by %{profile}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
}) })
@ -12,9 +13,10 @@
<% :event_updated -> %> <% :event_updated -> %>
<%= dgettext("activity", "The event %{event} was updated by %{profile}.", %{ <%= dgettext("activity", "The event %{event} was updated by %{profile}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
}) })
@ -29,9 +31,10 @@
<%= if @activity.subject_params["comment_reply_to"] do %> <%= if @activity.subject_params["comment_reply_to"] do %>
<%= dgettext("activity", "%{profile} replied to a comment on the event %{event}.", %{ <%= dgettext("activity", "%{profile} replied to a comment on the event %{event}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
}) })
@ -39,9 +42,10 @@
<% else %> <% else %>
<%= dgettext("activity", "%{profile} posted a comment on the event %{event}.", %{ <%= dgettext("activity", "%{profile} posted a comment on the event %{event}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
}) })

View File

@ -2,9 +2,10 @@
<% :group_created -> %> <% :group_created -> %>
<%= dgettext("activity", "%{profile} created the group %{group}.", %{ <%= dgettext("activity", "%{profile} created the group %{group}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
group: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, group:
:actor, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["group_federated_username"]) |> URI.decode()}\"> :actor,
@activity.subject_params["group_federated_username"]) |> URI.decode()}\">
#{@activity.subject_params["group_name"]} #{@activity.subject_params["group_name"]}
</a>" </a>"
}) })
@ -12,9 +13,10 @@
<% :group_updated -> %> <% :group_updated -> %>
<%= dgettext("activity", "%{profile} updated the group %{group}.", %{ <%= dgettext("activity", "%{profile} updated the group %{group}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
group: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, group:
:actor, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["group_federated_username"]) |> URI.decode()}\"> :actor,
@activity.subject_params["group_federated_username"]) |> URI.decode()}\">
#{@activity.subject_params["group_name"]} #{@activity.subject_params["group_name"]}
</a>" </a>"
}) })

View File

@ -2,9 +2,10 @@
<% :post_created -> %> <% :post_created -> %>
<%= dgettext("activity", "The post %{post} was created by %{profile}.", %{ <%= dgettext("activity", "The post %{post} was created by %{profile}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
post: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, post:
:post, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["post_slug"]) |> URI.decode()}\"> :post,
@activity.subject_params["post_slug"]) |> URI.decode()}\">
#{@activity.subject_params["post_title"]} #{@activity.subject_params["post_title"]}
</a>" </a>"
}) })
@ -12,9 +13,10 @@
<% :post_updated -> %> <% :post_updated -> %>
<%= dgettext("activity", "The post %{post} was updated by %{profile}.", %{ <%= dgettext("activity", "The post %{post} was updated by %{profile}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
post: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, post:
:post, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["post_slug"]) |> URI.decode()}\"> :post,
@activity.subject_params["post_slug"]) |> URI.decode()}\">
#{@activity.subject_params["post_title"]} #{@activity.subject_params["post_title"]}
</a>" </a>"
}) })

View File

@ -3,9 +3,10 @@
<%= if @activity.subject_params["is_folder"] do %> <%= if @activity.subject_params["is_folder"] do %>
<%= dgettext("activity", "%{profile} created the folder %{resource}.", %{ <%= dgettext("activity", "%{profile} created the folder %{resource}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
resource: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, resource:
:resource, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\"> :resource,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["resource_title"]} #{@activity.subject_params["resource_title"]}
</a>" </a>"
}) })
@ -13,9 +14,10 @@
<% else %> <% else %>
<%= dgettext("activity", "%{profile} created the resource %{resource}.", %{ <%= dgettext("activity", "%{profile} created the resource %{resource}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
resource: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, resource:
:resource, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\"> :resource,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["resource_title"]} #{@activity.subject_params["resource_title"]}
</a>" </a>"
}) })
@ -28,9 +30,10 @@
"%{profile} renamed the folder from %{old_resource_title} to %{resource}.", "%{profile} renamed the folder from %{old_resource_title} to %{resource}.",
%{ %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
resource: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, resource:
:resource, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\"> :resource,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["resource_title"]} #{@activity.subject_params["resource_title"]}
</a>", </a>",
old_resource_title: "<b>#{@activity.subject_params["old_resource_title"]}</b>" old_resource_title: "<b>#{@activity.subject_params["old_resource_title"]}</b>"
@ -43,9 +46,10 @@
"%{profile} renamed the resource from %{old_resource_title} to %{resource}.", "%{profile} renamed the resource from %{old_resource_title} to %{resource}.",
%{ %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
resource: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, resource:
:resource, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\"> :resource,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["resource_title"]} #{@activity.subject_params["resource_title"]}
</a>", </a>",
old_resource_title: "<b>#{@activity.subject_params["old_resource_title"]}</b>" old_resource_title: "<b>#{@activity.subject_params["old_resource_title"]}</b>"
@ -57,9 +61,10 @@
<%= if @activity.subject_params["is_folder"] do %> <%= if @activity.subject_params["is_folder"] do %>
<%= dgettext("activity", "%{profile} moved the folder %{resource}.", %{ <%= dgettext("activity", "%{profile} moved the folder %{resource}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
resource: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, resource:
:resource, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\"> :resource,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["resource_title"]} #{@activity.subject_params["resource_title"]}
</a>" </a>"
}) })
@ -67,9 +72,10 @@
<% else %> <% else %>
<%= dgettext("activity", "%{profile} moved the resource %{resource}.", %{ <%= dgettext("activity", "%{profile} moved the resource %{resource}.", %{
profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", profile: "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
resource: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, resource:
:resource, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\"> :resource,
@activity.subject_params["resource_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["resource_title"]} #{@activity.subject_params["resource_title"]}
</a>" </a>"
}) })

View File

@ -51,9 +51,10 @@
%{ %{
profile: profile:
"<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>", "<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint, event:
:event, "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
@activity.subject_params["event_uuid"]) |> URI.decode()}\"> :event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]} #{@activity.subject_params["event_title"]}
</a>" </a>"
} }

View File

@ -16,7 +16,7 @@
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"credo": {:hex, :credo, "1.6.6", "f51f8d45db1af3b2e2f7bee3e6d3c871737bda4a91bff00c5eec276517d1a19c", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "625520ce0984ee0f9f1f198165cd46fa73c1e59a17ebc520038b8fce056a5bdc"}, "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
"dataloader": {:hex, :dataloader, "1.0.10", "a42f07641b1a0572e0b21a2a5ae1be11da486a6790f3d0d14512d96ff3e3bbe9", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0 or ~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "54cd70cec09addf4b2ace14cc186a283a149fd4d3ec5475b155951bf33cd963f"}, "dataloader": {:hex, :dataloader, "1.0.10", "a42f07641b1a0572e0b21a2a5ae1be11da486a6790f3d0d14512d96ff3e3bbe9", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0 or ~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "54cd70cec09addf4b2ace14cc186a283a149fd4d3ec5475b155951bf33cd963f"},
"db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"}, "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
@ -62,7 +62,7 @@
"geolix": {:hex, :geolix, "2.0.0", "7e65764bedfc98d08a3ddb24c417657c9d438eff163280b45fbb7de289626acd", [:mix], [], "hexpm", "8742bf588ed0bb7def2c443204d09d355990846c6efdff96ded66aac24c301df"}, "geolix": {:hex, :geolix, "2.0.0", "7e65764bedfc98d08a3ddb24c417657c9d438eff163280b45fbb7de289626acd", [:mix], [], "hexpm", "8742bf588ed0bb7def2c443204d09d355990846c6efdff96ded66aac24c301df"},
"geolix_adapter_mmdb2": {:hex, :geolix_adapter_mmdb2, "0.6.0", "6ab9dbf6ea395817aa1fd2597be25d0dda1853c7f664e62716e47728d18bc4f9", [:mix], [{:geolix, "~> 2.0", [hex: :geolix, repo: "hexpm", optional: false]}, {:mmdb2_decoder, "~> 3.0", [hex: :mmdb2_decoder, repo: "hexpm", optional: false]}], "hexpm", "06ff962feae8a310cffdf86b74bfcda6e2d0dccb439bb1f62df2b657b1c0269b"}, "geolix_adapter_mmdb2": {:hex, :geolix_adapter_mmdb2, "0.6.0", "6ab9dbf6ea395817aa1fd2597be25d0dda1853c7f664e62716e47728d18bc4f9", [:mix], [{:geolix, "~> 2.0", [hex: :geolix, repo: "hexpm", optional: false]}, {:mmdb2_decoder, "~> 3.0", [hex: :mmdb2_decoder, repo: "hexpm", optional: false]}], "hexpm", "06ff962feae8a310cffdf86b74bfcda6e2d0dccb439bb1f62df2b657b1c0269b"},
"gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"}, "gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"},
"guardian": {:hex, :guardian, "2.2.4", "3dafdc19665411c96b2796d184064d691bc08813a132da5119e39302a252b755", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "6f83d4309c16ec2469da8606bb2a9815512cc2fac1595ad34b79940a224eb110"}, "guardian": {:hex, :guardian, "2.3.0", "1e2a90e809fbd99439f5279db03fb30b7b2b2fc0d3870a0d76a84b099f1a2892", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "ced3ace74fc2b22b2b25dbe99e6d205443dd0d57d1cc25155a223b5e9656205e"},
"guardian_db": {:hex, :guardian_db, "2.1.0", "ec95a9d99cdd1e550555d09a7bb4a340d8887aad0697f594590c2fd74be02426", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "f8e7d543ac92c395f3a7fd5acbe6829faeade57d688f7562e2f0fca8f94a0d70"}, "guardian_db": {:hex, :guardian_db, "2.1.0", "ec95a9d99cdd1e550555d09a7bb4a340d8887aad0697f594590c2fd74be02426", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "f8e7d543ac92c395f3a7fd5acbe6829faeade57d688f7562e2f0fca8f94a0d70"},
"guardian_phoenix": {:hex, :guardian_phoenix, "2.0.1", "89a817265af09a6ddf7cb1e77f17ffca90cea2db10ff888375ef34502b2731b1", [:mix], [{:guardian, "~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "21f439246715192b231f228680465d1ed5fbdf01555a4a3b17165532f5f9a08c"}, "guardian_phoenix": {:hex, :guardian_phoenix, "2.0.1", "89a817265af09a6ddf7cb1e77f17ffca90cea2db10ff888375ef34502b2731b1", [:mix], [{:guardian, "~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "21f439246715192b231f228680465d1ed5fbdf01555a4a3b17165532f5f9a08c"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
@ -74,7 +74,7 @@
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
"ip_reserved": {:hex, :ip_reserved, "0.1.1", "e5112d71f1abf05207f82fd9597d369a5fde1e0b6d1bbe77c02a99bb26ecdc33", [:mix], [{:inet_cidr, "~> 1.0.0", [hex: :inet_cidr, repo: "hexpm", optional: false]}], "hexpm", "55fcd2b6e211caef09ea3f54ef37d43030bec486325d12fe865ab5ed8140a4fe"}, "ip_reserved": {:hex, :ip_reserved, "0.1.1", "e5112d71f1abf05207f82fd9597d369a5fde1e0b6d1bbe77c02a99bb26ecdc33", [:mix], [{:inet_cidr, "~> 1.0.0", [hex: :inet_cidr, repo: "hexpm", optional: false]}], "hexpm", "55fcd2b6e211caef09ea3f54ef37d43030bec486325d12fe865ab5ed8140a4fe"},
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"jose": {:hex, :jose, "1.11.2", "f4c018ccf4fdce22c71e44d471f15f723cb3efab5d909ab2ba202b5bf35557b3", [:mix, :rebar3], [], "hexpm", "98143fbc48d55f3a18daba82d34fe48959d44538e9697c08f34200fa5f0947d2"}, "jose": {:hex, :jose, "1.11.2", "f4c018ccf4fdce22c71e44d471f15f723cb3efab5d909ab2ba202b5bf35557b3", [:mix, :rebar3], [], "hexpm", "98143fbc48d55f3a18daba82d34fe48959d44538e9697c08f34200fa5f0947d2"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
"junit_formatter": {:hex, :junit_formatter, "3.3.1", "c729befb848f1b9571f317d2fefa648e9d4869befc4b2980daca7c1edc468e40", [:mix], [], "hexpm", "761fc5be4b4c15d8ba91a6dafde0b2c2ae6db9da7b8832a55b5a1deb524da72b"}, "junit_formatter": {:hex, :junit_formatter, "3.3.1", "c729befb848f1b9571f317d2fefa648e9d4869befc4b2980daca7c1edc468e40", [:mix], [], "hexpm", "761fc5be4b4c15d8ba91a6dafde0b2c2ae6db9da7b8832a55b5a1deb524da72b"},
@ -97,10 +97,10 @@
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"oauth2": {:hex, :oauth2, "2.0.1", "70729503e05378697b958919bb2d65b002ba6b28c8112328063648a9348aaa3f", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "c64e20d4d105bcdbcbe03170fb530d0eddc3a3e6b135a87528a22c8aecf74c52"}, "oauth2": {:hex, :oauth2, "2.0.1", "70729503e05378697b958919bb2d65b002ba6b28c8112328063648a9348aaa3f", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "c64e20d4d105bcdbcbe03170fb530d0eddc3a3e6b135a87528a22c8aecf74c52"},
"oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"}, "oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"},
"oban": {:hex, :oban, "2.13.2", "878e1296b47bad2027ca5dbe69f7547fa0d83cab81faf41aec421944f157b342", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "21a0aa631722bb6c969079ff14acf5d3fa7b6bc547f363c8834f43b2c9cd654a"}, "oban": {:hex, :oban, "2.13.3", "57c80ceb75489e126218b8a82ec5e4fee01b6588a81b566650bb8167db4111f3", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8e997bddae17ec92bd12293f8b44f252f2f92ec03c54826390d1237a51ea47b2"},
"paasaa": {:hex, :paasaa, "0.6.0", "07c8ed81010caa25db351d474f0c053072c809821c60f9646f7b1547bec52f6d", [:mix], [], "hexpm", "732ddfc21bac0831edb26aec468af3ec2b8997d74f6209810b1cc53199c29f2e"}, "paasaa": {:hex, :paasaa, "0.6.0", "07c8ed81010caa25db351d474f0c053072c809821c60f9646f7b1547bec52f6d", [:mix], [], "hexpm", "732ddfc21bac0831edb26aec468af3ec2b8997d74f6209810b1cc53199c29f2e"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.6.11", "29f3c0fd12fa1fc4d4b05e341578e55bc78d96ea83a022587a7e276884d397e4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1664e34f80c25ea4918fbadd957f491225ef601c0e00b4e644b1a772864bfbc2"}, "phoenix": {:hex, :phoenix, "1.6.12", "f8f8ac077600f84419806dd53114b2e77aedde7a502e74181a7d886355aa0643", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d6cf5583c9c20f7103c40e6014ef802d96553b8e5d6585ad6e627bd5ddb0d12"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [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", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [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", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [: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", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [: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", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
@ -124,13 +124,13 @@
"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.7.4", "f967d9b2659e81bab241b96267aae1001d35c2beea2df9c03dcf47b007bf566f", [: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", "1553d994b4cf069162965e63de1e1c53d8236e127118d21e56ce2abeaa3f25b4"}, "swoosh": {:hex, :swoosh, "1.8.0", "51b3cfe5bb08196eba0f64b0e30f589533915b5088dd203fde110f29704289db", [: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", "fd77341d3e775a81fccfd26baacb9a47328ac8c27df0fb1d06f0312306721472"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"}, "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
"tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [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]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"}, "tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [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]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"},
"timex": {:hex, :timex, "3.7.9", "790cdfc4acfce434e442f98c02ea6d84d0239073bfd668968f82ac63e9a6788d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "64691582e5bb87130f721fc709acfb70f24405833998fabf35be968984860ce1"}, "timex": {:hex, :timex, "3.7.9", "790cdfc4acfce434e442f98c02ea6d84d0239073bfd668968f82ac63e9a6788d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "64691582e5bb87130f721fc709acfb70f24405833998fabf35be968984860ce1"},
"tz_world": {:hex, :tz_world, "1.1.0", "af078ccbc63b618912d05a18402abf450252535a086e5a5e5189b54b710e5653", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8e0a89a8e00968e0260f3176cab03c8960393cc10990ad10622d9eceb7ccfbe6"}, "tz_world": {:hex, :tz_world, "1.1.0", "af078ccbc63b618912d05a18402abf450252535a086e5a5e5189b54b710e5653", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8e0a89a8e00968e0260f3176cab03c8960393cc10990ad10622d9eceb7ccfbe6"},
"tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"},
"ueberauth": {:hex, :ueberauth, "0.10.2", "6e3b17d48c1cb7d5b2bca1255e908d49edf6150c622e5210ab3306acb84929aa", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "dfb7a5af57ed396341317b987815beace548f7deb33e760663546953b3f71c9d"}, "ueberauth": {:hex, :ueberauth, "0.10.3", "4a3bd7ab7b5d93d301d264f0f6858392654ee92171f4437d067d1ae227c051d9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "1394f36a6c64e97f2038cf95228e7e52b4cb75417962e30418fbe9902b30e6d3"},
"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"},