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
erlang 25.0.3
elixir 1.14.0-otp-25
erlang 25.0.4

View File

@ -1,7 +1,7 @@
FROM elixir:latest
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 curl -sL https://deb.nodesource.com/setup_16.x | bash && apt-get install nodejs -yq
RUN npm install -g yarn wait-on

View File

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

View File

@ -153,7 +153,7 @@ body {
/* Modal */
.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 */
@ -210,7 +210,7 @@ button.menubar__button {
/* Table */
.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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -301,7 +301,7 @@ import {
organizerAvatarUrl,
organizerDisplayName,
} 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 RouteName from "@/router/name";
import { changeIdentity } from "@/utils/identity";
@ -323,29 +323,12 @@ import { useI18n } from "vue-i18n";
import { Dialog } from "@/plugins/dialog";
import { Snackbar } from "@/plugins/snackbar";
import { useDeleteEvent } from "@/composition/apollo/event";
import Tag from "@/components/Tag.vue";
import Tag from "@/components/TagElement.vue";
const defaultOptions: IEventCardOptions = {
hideDate: true,
loggedPerson: false,
hideDetails: false,
organizerActor: null,
};
const props = withDefaults(
defineProps<{
participation: IParticipant;
options?: IEventCardOptions;
}>(),
{
options: () => ({
hideDate: true,
loggedPerson: false,
hideDetails: false,
organizerActor: null,
}),
}
);
const props = defineProps<{
participation: IParticipant;
options?: IEventCardOptions;
}>();
const emit = defineEmits(["eventDeleted"]);
@ -353,10 +336,6 @@ const { result: currentActorResult } = useQuery(CURRENT_ACTOR_CLIENT);
const currentActor = computed(() => currentActorResult.value?.currentActor);
const { t } = useI18n({ useScope: "global" });
const mergedOptions = computed<IEventCardOptions>(() => {
return { ...defaultOptions, ...props.options };
});
const dialog = inject<Dialog>("dialog");
const openDeleteEventModal = (
@ -441,9 +420,8 @@ onDeleteEventError((error) => {
* Delete the event
*/
const openDeleteEventModalWrapper = () => {
openDeleteEventModal(
props.participation.event,
deleteEvent(props.participation.event)
openDeleteEventModal(props.participation.event, (event: IEvent) =>
deleteEvent({ eventId: event.id ?? "" })
);
};
@ -474,15 +452,15 @@ const gotToWithCheck = async (
return router.push(route);
};
const organizerActor = computed<IActor | undefined>(() => {
if (
props.participation.event.attributedTo &&
props.participation.event.attributedTo.id
) {
return props.participation.event.attributedTo;
}
return props.participation.event.organizerActor;
});
// const organizerActor = computed<IActor | undefined>(() => {
// if (
// props.participation.event.attributedTo &&
// props.participation.event.attributedTo.id
// ) {
// return props.participation.event.attributedTo;
// }
// return props.participation.event.organizerActor;
// });
const seatsLeft = computed<number | null>(() => {
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;
@ -186,14 +186,14 @@ const updateSelected = (option: IAddress): void => {
emit("update:modelValue", selected.value);
};
const resetPopup = (): void => {
selected.value = new Address();
};
// const resetPopup = (): void => {
// selected.value = new Address();
// };
const openNewAddressModal = (): void => {
resetPopup();
addressModalActive.value = true;
};
// const openNewAddressModal = (): void => {
// resetPopup();
// addressModalActive.value = true;
// };
const checkCurrentPosition = (e: LatLng): boolean => {
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 AccountGroup from "vue-material-design-icons/AccountGroup.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 { useI18n } from "vue-i18n";

View File

@ -8,6 +8,8 @@
@click="clickMap"
@update:zoom="updateZoom"
:options="{ zoomControl: false }"
ref="mapComponent"
@ready="onMapReady"
>
<l-tile-layer :url="tiles?.endpoint" :attribution="attribution">
</l-tile-layer>
@ -16,28 +18,36 @@
:zoomInTitle="$t('Zoom in')"
:zoomOutTitle="$t('Zoom out')"
></l-control-zoom>
<!-- <v-locatecontrol
v-if="canDoGeoLocation"
:options="{ icon: 'mdi mdi-map-marker' }"
/> -->
<l-marker
:lat-lng="[lat, lon]"
@add="openPopup"
@update:latLng="updateDraggableMarkerPosition"
: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"
>{{ line }}<br
/></span>
</l-popup>
</l-marker>
</l-map>
<CrosshairsGps ref="locationIcon" class="hidden" />
</div>
</template>
<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 {
LMap,
@ -47,10 +57,12 @@ import {
LIcon,
LControlZoom,
} from "@vue-leaflet/vue-leaflet";
// import Vue2LeafletLocateControl from "@/components/Map/Vue2LeafletLocateControl.vue";
import { computed, nextTick, onMounted, ref } from "vue";
import { useMapTiles } from "@/composition/apollo/config";
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(
defineProps<{
@ -77,8 +89,18 @@ const defaultOptions: {
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
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line no-underscore-dangle
// delete Icon.Default.prototype._getIconUrl;
// Icon.Default.mergeOptions({
// 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> => {
await nextTick();
event.target.openPopup();
@ -147,3 +213,6 @@ div.map-container {
}
}
</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 { useI18n } from "vue-i18n";
import Upload from "vue-material-design-icons/Upload.vue";
import { formatBytes } from "@/utils/datetime";
const { t } = useI18n({ useScope: "global" });
@ -140,14 +141,4 @@ watch(imageSrc, () => {
const showImageLoadingError = (): void => {
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>

View File

@ -61,7 +61,7 @@ import { formatDateTimeString } from "@/filters/datetime";
import Tag from "vue-material-design-icons/Tag.vue";
import AccountEdit from "vue-material-design-icons/AccountEdit.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(
defineProps<{

View File

@ -22,12 +22,17 @@
</div>
<div class="">
<div class="" v-if="comment">
<article class="media">
<div class="media-left">
<figure class="image is-48x48" v-if="comment?.actor?.avatar">
<img :src="comment.actor.avatar.url" alt="" />
<article class="">
<div class="">
<figure class="h-8 w-8" v-if="comment?.actor?.avatar">
<img
:src="comment.actor.avatar.url"
alt=""
width="48"
height="48"
/>
</figure>
<o-icon v-else size="large" icon="account-circle" />
<AccountCircle v-else :size="48" />
</div>
<div class="">
<div class="prose dark:prose-invert">
@ -65,7 +70,7 @@
</section>
<footer class="flex gap-2 py-3">
<o-button ref="cancelButton" @click="close">
<o-button ref="cancelButton" outlined @click="close">
{{ translatedCancelText }}
</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 { ref, watch } from "vue";
import { IResource } from "@/types/resource";
import Draggable from "@xiaoshuapp/draggable";
import Draggable from "vuedraggable";
import { IGroup } from "@/types/actor";
const props = withDefaults(
@ -124,13 +124,13 @@ watch(checkedAll, () => {
});
});
const deleteResource = (resourceID: string) => {
validCheckedResources.value = validCheckedResources.value.filter(
(id) => id !== resourceID
);
delete checkedResources.value[resourceID];
emit("delete", resourceID);
};
// const deleteResource = (resourceID: string) => {
// validCheckedResources.value = validCheckedResources.value.filter(
// (id) => id !== resourceID
// );
// delete checkedResources.value[resourceID];
// emit("delete", resourceID);
// };
</script>
<style lang="scss" scoped>
@use "@/styles/_mixins" as *;

View File

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

View File

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

View File

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

View File

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

View File

@ -24,14 +24,14 @@
</div>
</template>
<script lang="ts" setup>
// import { SnackbarProgrammatic as Snackbar } from "buefy";
import { ITodo } from "../../types/todos";
import RouteName from "../../router/name";
import { UPDATE_TODO } from "../../graphql/todos";
import { computed, ref } from "vue";
import { computed, inject, ref } from "vue";
import { useMutation } from "@vue/apollo-composable";
import Account from "vue-material-design-icons/Account.vue";
import { formatDateString } from "@/filters/datetime";
import { Snackbar } from "@/plugins/snackbar";
const props = defineProps<{ todo: ITodo }>();
@ -41,8 +41,8 @@ const status = computed({
get() {
return props.todo.status;
},
set(status: boolean) {
updateTodo({ status, id: props.todo.id });
set(newStatus: boolean) {
updateTodo({ status: newStatus, id: props.todo.id });
},
});
@ -52,11 +52,13 @@ onDone(() => {
editMode.value = false;
});
const snackbar = inject<Snackbar>("snackbar");
onError((e) => {
// Snackbar.open({
// message: e.message,
// variant: "danger",
// position: "bottom",
// });
snackbar?.open({
message: e.message,
variant: "danger",
position: "bottom",
});
});
</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-input
v-model="prompt"
expanded
class="input"
ref="input"
:class="{ 'is-danger': validationMessage }"
v-bind="inputAttrs"
@keydown.enter="confirm"
/>
<p class="help is-danger">{{ validationMessage }}</p>
</o-field>
</div>
</section>
@ -83,15 +82,14 @@ const emit = defineEmits(["confirm", "cancel", "close"]);
const { t } = useI18n({ useScope: "global" });
const modalOpened = ref(false);
const validationMessage = ref("");
// const modalOpened = ref(false);
const prompt = ref<string>(props.hasInput ? props.inputAttrs?.value ?? "" : "");
const input = ref();
const dialogClass = computed(() => {
return [props.size];
});
// const dialogClass = computed(() => {
// return [props.size];
// });
/**
* Icon name (MDI) based on the type.
*/
@ -114,10 +112,11 @@ const iconByType = computed(() => {
* Call the onConfirm prop (function) and close the Dialog.
*/
const confirm = () => {
console.log("dialog confirmed", input.value.$el);
if (input.value !== undefined) {
if (!input.value.checkValidity()) {
validationMessage.value = input.value.validationMessage;
nextTick(() => input.value.select());
const inputElement = input.value.$el.querySelector("input");
if (!inputElement.checkValidity()) {
nextTick(() => inputElement.select());
return;
}
}

View File

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

View File

@ -13,14 +13,14 @@ import {
MAPS_TILES,
RESOURCE_PROVIDERS,
RESTRICTIONS,
ROUTING_TYPE,
SEARCH_CONFIG,
TIMEZONES,
UPLOAD_LIMITS,
} from "@/graphql/config";
import { IAnonymousParticipationConfig, IConfig } from "@/types/config.model";
import { ApolloError } from "@apollo/client/core";
import { IConfig } from "@/types/config.model";
import { useQuery } from "@vue/apollo-composable";
import { computed, ref } from "vue";
import { computed } from "vue";
export function useTimezones() {
const {
@ -36,26 +36,19 @@ export function useTimezones() {
}
export function useAnonymousParticipationConfig() {
const anonymousParticipationConfig = ref<
IAnonymousParticipationConfig | undefined
>(undefined);
const {
result: configResult,
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) {
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 };
return { anonymousParticipationConfig, error, loading };
}
export function useAnonymousReportsConfig() {
@ -147,6 +140,15 @@ export function useMapTiles() {
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() {
const { result, error, loading } = useQuery<{
config: Pick<IConfig, "features">;

View File

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

View File

@ -1,4 +1,3 @@
import { computed } from "vue";
import { useExportFormats, useUploadLimits } from "./apollo/config";
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`
query Features {
config {

View File

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

View File

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

View File

@ -1,36 +1,53 @@
<template>
<div class="p-6">
<div>
<header class="">
<h2 class="">{{ $t("Pick an identity") }}</h2>
<h2 class="">{{ t("Pick an identity") }}</h2>
</header>
<section class="">
<div class="list is-hoverable list-none">
<a
class="my-2 block dark:bg-violet-3 rounded-xl p-2"
<transition-group
tag="ul"
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"
:key="identity.id"
:class="{
active: currentIdentity && identity.id === currentIdentity.id,
}"
@click="currentIdentity = identity"
:key="identity?.id"
>
<div class="flex gap-2">
<img
class="rounded"
v-if="identity.avatar"
:src="identity.avatar.url"
alt=""
width="48"
height="48"
/>
<input
class="sr-only peer"
type="radio"
:value="identity"
name="availableActors"
v-model="currentIdentity"
:id="`availableActor-${identity?.id}`"
/>
<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" />
<div class="">
<p>@{{ identity.preferredUsername }}</p>
<small>{{ identity.name }}</small>
<div class="flex-1">
<h3>{{ identity?.name }}</h3>
<small>{{ `@${identity?.preferredUsername}` }}</small>
</div>
</div>
</a>
</div>
</label>
</li>
</transition-group>
</section>
<slot name="footer" />
</div>

View File

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

View File

@ -321,7 +321,7 @@ import {
import { Dialog } from "@/plugins/dialog";
import { Notifier } from "@/plugins/notifier";
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 POSTS_PER_PAGE = 10;

View File

@ -308,7 +308,7 @@ import {
formatDateTimeString,
} from "@/filters/datetime";
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 PARTICIPATIONS_PER_PAGE = 10;

View File

@ -1,5 +1,5 @@
<template>
<div v-if="user" class="section">
<div v-if="user">
<breadcrumbs-nav
:links="[
{ name: RouteName.ADMIN, text: t('Admin') },
@ -18,10 +18,10 @@
<section>
<h2 class="text-lg font-bold mb-3">{{ t("Details") }}</h2>
<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="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>
<tr
class="border-b"
@ -229,48 +229,46 @@
aria-modal
>
<form @submit.prevent="updateUserRole">
<div>
<header>
<h2 class="modal-card-title">{{ t("Change user role") }}</h2>
</header>
<section>
<o-field>
<o-radio
v-model="newUser.role"
:native-value="ICurrentUserRole.ADMINISTRATOR"
>
{{ t("Administrator") }}
</o-radio>
</o-field>
<o-field>
<o-radio
v-model="newUser.role"
:native-value="ICurrentUserRole.MODERATOR"
>
{{ t("Moderator") }}
</o-radio>
</o-field>
<o-field>
<o-radio
v-model="newUser.role"
:native-value="ICurrentUserRole.USER"
>
{{ t("User") }}
</o-radio>
</o-field>
<o-checkbox v-model="newUser.notify">{{
t("Notify the user of the change")
}}</o-checkbox>
</section>
<footer class="mt-2 flex gap-2">
<o-button @click="isRoleChangeModalActive = false">{{
t("Close")
}}</o-button>
<o-button native-type="submit" variant="primary">{{
t("Change role")
}}</o-button>
</footer>
</div>
<header>
<h2>{{ t("Change user role") }}</h2>
</header>
<section>
<o-field>
<o-radio
v-model="newUser.role"
:native-value="ICurrentUserRole.ADMINISTRATOR"
>
{{ t("Administrator") }}
</o-radio>
</o-field>
<o-field>
<o-radio
v-model="newUser.role"
:native-value="ICurrentUserRole.MODERATOR"
>
{{ t("Moderator") }}
</o-radio>
</o-field>
<o-field>
<o-radio
v-model="newUser.role"
:native-value="ICurrentUserRole.USER"
>
{{ t("User") }}
</o-radio>
</o-field>
<o-checkbox v-model="newUser.notify">{{
t("Notify the user of the change")
}}</o-checkbox>
</section>
<footer class="mt-2 flex gap-2">
<o-button @click="isRoleChangeModalActive = false" outlined>{{
t("Close")
}}</o-button>
<o-button native-type="submit" variant="primary">{{
t("Change role")
}}</o-button>
</footer>
</form>
</o-modal>
<o-modal
@ -284,24 +282,22 @@
aria-modal
>
<form @submit.prevent="confirmUser">
<div>
<header>
<h2>{{ t("Confirm user") }}</h2>
</header>
<section>
<o-checkbox v-model="newUser.notify">{{
t("Notify the user of the change")
}}</o-checkbox>
</section>
<footer>
<o-button @click="isConfirmationModalActive = false">{{
t("Close")
}}</o-button>
<o-button native-type="submit" variant="primary">{{
t("Confirm user")
}}</o-button>
</footer>
</div>
<header>
<h2>{{ t("Confirm user") }}</h2>
</header>
<section>
<o-checkbox v-model="newUser.notify">{{
t("Notify the user of the change")
}}</o-checkbox>
</section>
<footer>
<o-button @click="isConfirmationModalActive = false">{{
t("Close")
}}</o-button>
<o-button native-type="submit" variant="primary">{{
t("Confirm user")
}}</o-button>
</footer>
</form>
</o-modal>
</div>
@ -424,7 +420,7 @@ const metadata = computed(
},
{
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);
})
.filter((code) => code !== undefined) as string[];
// adminSettings = {
// ...adminSettings,
// instanceLanguages: newInstanceLanguages,
// };
settingsToWrite.value = {
...settingsToWrite.value,
instanceLanguages: newFilteredInstanceLanguages,
};
},
});

View File

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

View File

@ -97,7 +97,6 @@
<event-participation-card
v-if="'role' in element"
:participation="element"
:options="{ hideDate: false }"
@event-deleted="eventDeleted"
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 EmptyContent from "@/components/Utils/EmptyContent.vue";
import { Notifier } from "@/plugins/notifier";
import Tag from "@/components/Tag.vue";
import Tag from "@/components/TagElement.vue";
const PARTICIPANTS_PER_PAGE = 10;
const MESSAGE_ELLIPSIS_LENGTH = 130;

View File

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

View File

@ -135,7 +135,7 @@ import { IGroup, usernameWithDomain, displayName } from "@/types/actor";
import { ActivityType } from "@/types/enums";
import { Paginate } from "@/types/paginate";
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 RouteName from "../../router/name";
import TimelineText from "vue-material-design-icons/TimelineText.vue";

View File

@ -219,7 +219,7 @@ import {
displayName,
} from "@/types/actor";
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 ActorInline from "@/components/Account/ActorInline.vue";
import { formatDistanceToNowStrict, Locale } from "date-fns";

View File

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

View File

@ -1,5 +1,5 @@
import { mount } from "@vue/test-utils";
import Tag from "@/components/Tag.vue";
import Tag from "@/components/TagElement.vue";
import { it, expect } from "vitest";
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()}
def find_user(_parent, %{id: id}, %{context: %{current_user: %User{role: role}}})
when is_moderator(role) do
with {:ok, %User{} = user} <- Users.get_user_with_actors(id) do
{:ok, user}
case Users.get_user_with_actors(id) do
{:ok, %User{} = user} ->
{:ok, user}
_ ->
{:error, :user_not_found}
end
end
def find_user(_parent, _args, _resolution) do
{:error, :unauthorized}
end
@doc """
Return current logged-in user
"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -51,9 +51,10 @@
%{
profile:
"<b>#{Mobilizon.Actors.Actor.display_name_and_username(@activity.author)}</b>",
event: "<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
:event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
event:
"<a href=\"#{Routes.page_url(Mobilizon.Web.Endpoint,
:event,
@activity.subject_params["event_uuid"]) |> URI.decode()}\">
#{@activity.subject_params["event_title"]}
</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_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"},
"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"},
"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"},
@ -62,7 +62,7 @@
"geolix": {:hex, :geolix, "2.0.0", "7e65764bedfc98d08a3ddb24c417657c9d438eff163280b45fbb7de289626acd", [:mix], [], "hexpm", "8742bf588ed0bb7def2c443204d09d355990846c6efdff96ded66aac24c301df"},
"geolix_adapter_mmdb2": {:hex, :geolix_adapter_mmdb2, "0.6.0", "6ab9dbf6ea395817aa1fd2597be25d0dda1853c7f664e62716e47728d18bc4f9", [:mix], [{:geolix, "~> 2.0", [hex: :geolix, repo: "hexpm", optional: false]}, {:mmdb2_decoder, "~> 3.0", [hex: :mmdb2_decoder, repo: "hexpm", optional: false]}], "hexpm", "06ff962feae8a310cffdf86b74bfcda6e2d0dccb439bb1f62df2b657b1c0269b"},
"gettext": {:hex, :gettext, "0.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_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"},
@ -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"},
"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"},
"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"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
"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"},
"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"},
"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"},
"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_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"},
@ -124,13 +124,13 @@
"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"},
"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"},
"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"},
"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"},
"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_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"},