Expose personal tokened feeds
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
:to="{ name: RouteName.PREFERENCES }"
|
||||
/>
|
||||
<SettingMenuItem
|
||||
:title="this.$t('Email notifications')"
|
||||
:title="this.$t('Notifications')"
|
||||
:to="{ name: RouteName.NOTIFICATIONS }"
|
||||
/>
|
||||
</SettingMenuSection>
|
||||
|
||||
@@ -241,3 +241,17 @@ export const UPDATE_USER_LOCALE = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const FEED_TOKENS_LOGGED_USER = gql`
|
||||
query {
|
||||
loggedUser {
|
||||
id
|
||||
feedTokens {
|
||||
token
|
||||
actor {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -969,5 +969,13 @@
|
||||
"You replied to a comment on the event {event}.": "You replied to a comment on the event {event}.",
|
||||
"{profile} replied to a comment on the event {event}.": "{profile} replied to a comment on the event {event}.",
|
||||
"New post": "New post",
|
||||
"Comment text can't be empty": "Comment text can't be empty"
|
||||
"Comment text can't be empty": "Comment text can't be empty",
|
||||
"Notifications": "Notifications",
|
||||
"Profile feeds": "Profile feeds",
|
||||
"These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.": "These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.",
|
||||
"Regenerate new links": "Regenerate new links",
|
||||
"Create new links": "Create new links",
|
||||
"You'll need to change the URLs where there were previously entered.": "You'll need to change the URLs where there were previously entered.",
|
||||
"Personal feeds": "Personal feeds",
|
||||
"These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page."
|
||||
}
|
||||
|
||||
@@ -1063,5 +1063,13 @@
|
||||
"You replied to a comment on the event {event}.": "Vous avez répondu à un commentaire sur l'événement {event}.",
|
||||
"{profile} replied to a comment on the event {event}.": "{profile} a répondu à un commentaire sur l'événement {event}.",
|
||||
"New post": "Nouveau billet",
|
||||
"Comment text can't be empty": "Le texte du commentaire ne peut être vide"
|
||||
"Comment text can't be empty": "Le texte du commentaire ne peut être vide",
|
||||
"Notifications": "Notifications",
|
||||
"Profile feeds": "Flux du profil",
|
||||
"These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings.": "Ces flux contiennent des informations sur les événements pour lesquels ce profil spécifique est un⋅e participant⋅e ou un⋅e créateur⋅ice. Vous devriez les garder privés. Vous pouvez trouver des flux pour l'ensemble de vos profils dans vos paramètres de notification.",
|
||||
"Regenerate new links": "Regénérer de nouveaux liens",
|
||||
"Create new links": "Créer de nouveaux liens",
|
||||
"You'll need to change the URLs where there were previously entered.": "Vous devrez changer les URLs là où vous les avez entrées précédemment.",
|
||||
"Personal feeds": "Flux personnels",
|
||||
"These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "Ces flux contiennent des informations sur les événements pour lesquels n'importe lequel de vos profils est un⋅e participant⋅e ou un⋅e créateur⋅ice. Vous devriez les garder privés. Vous pouvez trouver des flux spécifiques à chaque profil sur la page d'édition des profils."
|
||||
}
|
||||
|
||||
@@ -98,6 +98,77 @@
|
||||
$t("Delete this identity")
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<section v-if="isUpdate">
|
||||
<div class="setting-title">
|
||||
<h2>{{ $t("Profile feeds") }}</h2>
|
||||
</div>
|
||||
<p>
|
||||
{{
|
||||
$t(
|
||||
"These feeds contain event data for the events for which this specific profile is a participant or creator. You should keep these private. You can find feeds for all of your profiles into your notification settings."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div v-if="identity.feedTokens && identity.feedTokens.length > 0">
|
||||
<div
|
||||
class="buttons"
|
||||
v-for="feedToken in identity.feedTokens"
|
||||
:key="feedToken.token"
|
||||
>
|
||||
<b-tooltip
|
||||
:label="$t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.atom"
|
||||
always
|
||||
type="is-success"
|
||||
position="is-left"
|
||||
>
|
||||
<b-button
|
||||
tag="a"
|
||||
icon-left="rss"
|
||||
@click="
|
||||
(e) => copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
:href="tokenToURL(feedToken.token, 'atom')"
|
||||
target="_blank"
|
||||
>{{ $t("RSS/Atom Feed") }}</b-button
|
||||
>
|
||||
</b-tooltip>
|
||||
<b-tooltip
|
||||
:label="$t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.ics"
|
||||
always
|
||||
type="is-success"
|
||||
position="is-left"
|
||||
>
|
||||
<b-button
|
||||
tag="a"
|
||||
@click="
|
||||
(e) => copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
icon-left="calendar-sync"
|
||||
:href="tokenToURL(feedToken.token, 'ics')"
|
||||
target="_blank"
|
||||
>{{ $t("ICS/WebCal Feed") }}</b-button
|
||||
>
|
||||
</b-tooltip>
|
||||
<b-button
|
||||
icon-left="refresh"
|
||||
type="is-text"
|
||||
@click="openRegenerateFeedTokensConfirmation"
|
||||
>{{ $t("Regenerate new links") }}</b-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<b-button
|
||||
icon-left="refresh"
|
||||
type="is-text"
|
||||
@click="generateFeedTokens"
|
||||
>{{ $t("Create new links") }}</b-button
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -131,6 +202,10 @@ h1 {
|
||||
.username-field + .field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
::v-deep .buttons > *:not(:last-child) .button {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -151,6 +226,11 @@ import RouteName from "../../../router/name";
|
||||
import { buildFileVariable } from "../../../utils/image";
|
||||
import { changeIdentity } from "../../../utils/auth";
|
||||
import identityEditionMixin from "../../../mixins/identityEdition";
|
||||
import {
|
||||
CREATE_FEED_TOKEN_ACTOR,
|
||||
DELETE_FEED_TOKEN,
|
||||
} from "@/graphql/feed_tokens";
|
||||
import { IFeedToken } from "@/types/feedtoken.model";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@@ -191,6 +271,8 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||
|
||||
RouteName = RouteName;
|
||||
|
||||
showCopiedTooltip = { ics: false, atom: false };
|
||||
|
||||
get message(): string | null {
|
||||
if (this.isUpdate) return null;
|
||||
return this.$t(
|
||||
@@ -353,6 +435,63 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||
return MOBILIZON_INSTANCE_HOST;
|
||||
}
|
||||
|
||||
tokenToURL(token: string, format: string): string {
|
||||
return `${window.location.origin}/events/going/${token}/${format}`;
|
||||
}
|
||||
|
||||
copyURL(e: Event, url: string, format: "ics" | "atom"): void {
|
||||
if (navigator.clipboard) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(url);
|
||||
this.showCopiedTooltip[format] = true;
|
||||
setTimeout(() => {
|
||||
this.showCopiedTooltip[format] = false;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
async generateFeedTokens(): Promise<void> {
|
||||
const newToken = await this.createNewFeedToken();
|
||||
this.identity.feedTokens.push(newToken);
|
||||
}
|
||||
|
||||
async regenerateFeedTokens(): Promise<void> {
|
||||
if (this.identity?.feedTokens.length < 1) return;
|
||||
await this.deleteFeedToken(this.identity.feedTokens[0].token);
|
||||
const newToken = await this.createNewFeedToken();
|
||||
this.identity.feedTokens.pop();
|
||||
this.identity.feedTokens.push(newToken);
|
||||
}
|
||||
|
||||
private async deleteFeedToken(token: string): Promise<void> {
|
||||
await this.$apollo.mutate({
|
||||
mutation: DELETE_FEED_TOKEN,
|
||||
variables: { token },
|
||||
});
|
||||
}
|
||||
|
||||
private async createNewFeedToken(): Promise<IFeedToken> {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: CREATE_FEED_TOKEN_ACTOR,
|
||||
variables: { actor_id: this.identity?.id },
|
||||
});
|
||||
|
||||
return data.createFeedToken;
|
||||
}
|
||||
|
||||
openRegenerateFeedTokensConfirmation(): void {
|
||||
this.$buefy.dialog.confirm({
|
||||
type: "is-warning",
|
||||
title: this.$t("Regenerate new links") as string,
|
||||
message: this.$t(
|
||||
"You'll need to change the URLs where there were previously entered."
|
||||
) as string,
|
||||
confirmText: this.$t("Regenerate new links") as string,
|
||||
cancelText: this.$t("Cancel") as string,
|
||||
onConfirm: () => this.regenerateFeedTokens(),
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteIdentityConfirmation(): void {
|
||||
this.$buefy.dialog.prompt({
|
||||
type: "is-danger",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.NOTIFICATIONS }">{{
|
||||
$t("Email notifications")
|
||||
$t("Notifications")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -118,23 +118,108 @@
|
||||
</b-select>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="setting-title">
|
||||
<h2>{{ $t("Personal feeds") }}</h2>
|
||||
</div>
|
||||
<p>
|
||||
{{
|
||||
$t(
|
||||
"These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div v-if="feedTokens && feedTokens.length > 0">
|
||||
<div
|
||||
class="buttons"
|
||||
v-for="feedToken in feedTokens"
|
||||
:key="feedToken.token"
|
||||
>
|
||||
<b-tooltip
|
||||
:label="$t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.atom"
|
||||
always
|
||||
type="is-success"
|
||||
position="is-left"
|
||||
>
|
||||
<b-button
|
||||
tag="a"
|
||||
icon-left="rss"
|
||||
@click="
|
||||
(e) => copyURL(e, tokenToURL(feedToken.token, 'atom'), 'atom')
|
||||
"
|
||||
:href="tokenToURL(feedToken.token, 'atom')"
|
||||
target="_blank"
|
||||
>{{ $t("RSS/Atom Feed") }}</b-button
|
||||
>
|
||||
</b-tooltip>
|
||||
<b-tooltip
|
||||
:label="$t('URL copied to clipboard')"
|
||||
:active="showCopiedTooltip.ics"
|
||||
always
|
||||
type="is-success"
|
||||
position="is-left"
|
||||
>
|
||||
<b-button
|
||||
tag="a"
|
||||
@click="
|
||||
(e) => copyURL(e, tokenToURL(feedToken.token, 'ics'), 'ics')
|
||||
"
|
||||
icon-left="calendar-sync"
|
||||
:href="tokenToURL(feedToken.token, 'ics')"
|
||||
target="_blank"
|
||||
>{{ $t("ICS/WebCal Feed") }}</b-button
|
||||
>
|
||||
</b-tooltip>
|
||||
<b-button
|
||||
icon-left="refresh"
|
||||
type="is-text"
|
||||
@click="openRegenerateFeedTokensConfirmation"
|
||||
>{{ $t("Regenerate new links") }}</b-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<b-button
|
||||
icon-left="refresh"
|
||||
type="is-text"
|
||||
@click="generateFeedTokens"
|
||||
>{{ $t("Create new links") }}</b-button
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Watch } from "vue-property-decorator";
|
||||
import { INotificationPendingEnum } from "@/types/enums";
|
||||
import { USER_SETTINGS, SET_USER_SETTINGS } from "../../graphql/user";
|
||||
import {
|
||||
USER_SETTINGS,
|
||||
SET_USER_SETTINGS,
|
||||
FEED_TOKENS_LOGGED_USER,
|
||||
} from "../../graphql/user";
|
||||
import { IUser } from "../../types/current-user.model";
|
||||
import RouteName from "../../router/name";
|
||||
import { IFeedToken } from "@/types/feedtoken.model";
|
||||
import { CREATE_FEED_TOKEN, DELETE_FEED_TOKEN } from "@/graphql/feed_tokens";
|
||||
|
||||
@Component({
|
||||
apollo: {
|
||||
loggedUser: USER_SETTINGS,
|
||||
feedTokens: {
|
||||
query: FEED_TOKENS_LOGGED_USER,
|
||||
update: (data) =>
|
||||
data.loggedUser.feedTokens.filter(
|
||||
(token: IFeedToken) => token.actor === null
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
export default class Notifications extends Vue {
|
||||
loggedUser!: IUser;
|
||||
|
||||
feedTokens: IFeedToken[] = [];
|
||||
|
||||
notificationOnDay: boolean | undefined = true;
|
||||
|
||||
notificationEachWeek: boolean | undefined = false;
|
||||
@@ -148,6 +233,8 @@ export default class Notifications extends Vue {
|
||||
|
||||
RouteName = RouteName;
|
||||
|
||||
showCopiedTooltip = { ics: false, atom: false };
|
||||
|
||||
mounted(): void {
|
||||
this.notificationPendingParticipationValues = {
|
||||
[INotificationPendingEnum.NONE]: this.$t("Do not receive any mail"),
|
||||
@@ -176,6 +263,62 @@ export default class Notifications extends Vue {
|
||||
refetchQueries: [{ query: USER_SETTINGS }],
|
||||
});
|
||||
}
|
||||
|
||||
tokenToURL(token: string, format: string): string {
|
||||
return `${window.location.origin}/events/going/${token}/${format}`;
|
||||
}
|
||||
|
||||
copyURL(e: Event, url: string, format: "ics" | "atom"): void {
|
||||
if (navigator.clipboard) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(url);
|
||||
this.showCopiedTooltip[format] = true;
|
||||
setTimeout(() => {
|
||||
this.showCopiedTooltip[format] = false;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
openRegenerateFeedTokensConfirmation(): void {
|
||||
this.$buefy.dialog.confirm({
|
||||
type: "is-warning",
|
||||
title: this.$t("Regenerate new links") as string,
|
||||
message: this.$t(
|
||||
"You'll need to change the URLs where there were previously entered."
|
||||
) as string,
|
||||
confirmText: this.$t("Regenerate new links") as string,
|
||||
cancelText: this.$t("Cancel") as string,
|
||||
onConfirm: () => this.regenerateFeedTokens(),
|
||||
});
|
||||
}
|
||||
|
||||
async regenerateFeedTokens(): Promise<void> {
|
||||
if (this.feedTokens.length < 1) return;
|
||||
await this.deleteFeedToken(this.feedTokens[0].token);
|
||||
const newToken = await this.createNewFeedToken();
|
||||
this.feedTokens.pop();
|
||||
this.feedTokens.push(newToken);
|
||||
}
|
||||
|
||||
async generateFeedTokens(): Promise<void> {
|
||||
const newToken = await this.createNewFeedToken();
|
||||
this.feedTokens.push(newToken);
|
||||
}
|
||||
|
||||
private async deleteFeedToken(token: string): Promise<void> {
|
||||
await this.$apollo.mutate({
|
||||
mutation: DELETE_FEED_TOKEN,
|
||||
variables: { token },
|
||||
});
|
||||
}
|
||||
|
||||
private async createNewFeedToken(): Promise<IFeedToken> {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: CREATE_FEED_TOKEN,
|
||||
});
|
||||
|
||||
return data.createFeedToken;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -193,4 +336,8 @@ export default class Notifications extends Vue {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .buttons > *:not(:last-child) .button {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user