Merge branch 'add-onboarding-settings' into 'master'

Add onboarding settings

See merge request framasoft/mobilizon!462
This commit is contained in:
Thomas Citharel 2020-06-11 11:20:17 +02:00
commit fea721e5f9
9 changed files with 213 additions and 16 deletions

View File

@ -58,11 +58,10 @@ import Logo from "./components/Logo.vue";
import { initializeCurrentActor } from "./utils/auth"; import { initializeCurrentActor } from "./utils/auth";
import { CONFIG } from "./graphql/config"; import { CONFIG } from "./graphql/config";
import { IConfig } from "./types/config.model"; import { IConfig } from "./types/config.model";
import { ICurrentUser } from "./types/current-user.model";
@Component({ @Component({
apollo: { apollo: {
currentUser: { currentUser: CURRENT_USER_CLIENT,
query: CURRENT_USER_CLIENT,
},
config: CONFIG, config: CONFIG,
}, },
components: { components: {
@ -73,6 +72,7 @@ import { IConfig } from "./types/config.model";
}) })
export default class App extends Vue { export default class App extends Vue {
config!: IConfig; config!: IConfig;
currentUser!: ICurrentUser;
async created() { async created() {
if (await this.initializeCurrentUser()) { if (await this.initializeCurrentUser()) {

View File

@ -0,0 +1,68 @@
<template>
<div v-if="loggedUser">
<section>
<div class="setting-title">
<h2>{{ $t("Participation notifications") }}</h2>
</div>
<div class="field">
<strong>{{
$t("We'll always send you emails to notify about important event updates")
}}</strong>
<p>
{{
$t(
"Like title or physical address update, start or end date change or event being confirmed or cancelled."
)
}}
</p>
</div>
<div class="field">
<b-checkbox v-model="notificationOnDay" @input="updateSetting({ notificationOnDay })">
<strong>{{ $t("Notification on the day of the event") }}</strong>
<p>
{{
$t("We'll use your timezone settings to send a recap of the morning of the event.")
}}
</p>
</b-checkbox>
</div>
<p>{{ $t("To activate more notifications, head over to the notification settings.") }}</p>
<div class="has-text-centered">
<router-link
:to="{ name: RouteName.NOTIFICATIONS }"
class="button is-primary is-outlined"
>{{ $t("Manage my notifications") }}</router-link
>
</div>
</section>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";
import { USER_SETTINGS, SET_USER_SETTINGS } from "../../graphql/user";
import {
ICurrentUser,
INotificationPendingParticipationEnum,
} from "../../types/current-user.model";
import RouteName from "../../router/name";
@Component({
apollo: {
loggedUser: USER_SETTINGS,
},
})
export default class NotificationsOnboarding extends Vue {
loggedUser!: ICurrentUser;
notificationOnDay = true;
RouteName = RouteName;
async updateSetting(variables: object) {
await this.$apollo.mutate<{ setUserSettings: string }>({
mutation: SET_USER_SETTINGS,
variables,
});
}
}
</script>

View File

@ -0,0 +1,53 @@
<template>
<div v-if="loggedUser">
<section>
<div class="setting-title">
<h2>{{ $t("Settings") }}</h2>
</div>
<p>
<h3>{{ $t("Timezone") }}</h3>
{{ $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}.", { timezone: Intl.DateTimeFormat().resolvedOptions().timeZone }) }}
</p>
<div class="has-text-centered">
<router-link :to="{ name: RouteName.PREFERENCES }" class="button is-primary is-outlined">{{
$t("Manage my settings")
}}</router-link>
</div>
</section>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";
import { USER_SETTINGS, SET_USER_SETTINGS } from "../../graphql/user";
import {
ICurrentUser,
INotificationPendingParticipationEnum,
} from "../../types/current-user.model";
import RouteName from "../../router/name";
@Component({
apollo: {
loggedUser: USER_SETTINGS,
},
})
export default class SettingsOnboarding extends Vue {
loggedUser!: ICurrentUser;
notificationOnDay = true;
RouteName = RouteName;
async updateSetting(variables: object) {
await this.$apollo.mutate<{ setUserSettings: string }>({
mutation: SET_USER_SETTINGS,
variables,
});
}
}
</script>
<style lang="scss" scoped>
h3 {
font-weight: bold;
}
</style>

View File

@ -600,5 +600,12 @@
"We asked professional designers to help us develop our vision for Mobilizon. We took time to study the {digital_habits} in order to understand the features they need to gather, organize, and mobilize.": "We asked professional designers to help us develop our vision for Mobilizon. We took time to study the {digital_habits} in order to understand the features they need to gather, organize, and mobilize.", "We asked professional designers to help us develop our vision for Mobilizon. We took time to study the {digital_habits} in order to understand the features they need to gather, organize, and mobilize.": "We asked professional designers to help us develop our vision for Mobilizon. We took time to study the {digital_habits} in order to understand the features they need to gather, organize, and mobilize.",
"So that, right from its conception, Mobilizon would {fit_needs_uses_people} who are going to use it.": "So that, right from its conception, Mobilizon would {fit_needs_uses_people} who are going to use it.", "So that, right from its conception, Mobilizon would {fit_needs_uses_people} who are going to use it.": "So that, right from its conception, Mobilizon would {fit_needs_uses_people} who are going to use it.",
"fit the needs and uses of the people": "fit the needs and uses of the people", "fit the needs and uses of the people": "fit the needs and uses of the people",
"Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the fall of 2020</b>.": "Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the fall of 2020</b>." "Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the fall of 2020</b>.": "Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the fall of 2020</b>.",
"To activate more notifications, head over to the notification settings.": "To activate more notifications, head over to the notification settings.",
"Manage my notifications": "Manage my notifications",
"We use your timezone to make sure you get notifications for an event at the correct time.": "We use your timezone to make sure you get notifications for an event at the correct time.",
"Your timezone was detected as {timezone}.": "Your timezone was detected as {timezone}.",
"Manage my settings": "Manage my settings",
"Let's define a few settings": "Let's define a few settings",
"All good, let's continue!": "All good, let's continue!"
} }

View File

@ -619,5 +619,12 @@
"We asked professional designers to help us develop our vision for Mobilizon. We took time to study the {digital_habits} in order to understand the features they need to gather, organize, and mobilize.": "Nous avons demandé de laide à des professionnel·les du design pour concevoir ce que pourrait être Mobilizon. Nous avons pris le temps détudier {digital_habits} afin de comprendre les fonctionnalités dont ils et elles ont besoin pour se rassembler, sorganiser, se mobiliser.", "We asked professional designers to help us develop our vision for Mobilizon. We took time to study the {digital_habits} in order to understand the features they need to gather, organize, and mobilize.": "Nous avons demandé de laide à des professionnel·les du design pour concevoir ce que pourrait être Mobilizon. Nous avons pris le temps détudier {digital_habits} afin de comprendre les fonctionnalités dont ils et elles ont besoin pour se rassembler, sorganiser, se mobiliser.",
"So that, right from its conception, Mobilizon would {fit_needs_uses_people} who are going to use it.": "Afin que Mobilizon corresponde, dès sa conception, {fit_needs_uses_people} des personnes qui sont vouées à lutiliser.", "So that, right from its conception, Mobilizon would {fit_needs_uses_people} who are going to use it.": "Afin que Mobilizon corresponde, dès sa conception, {fit_needs_uses_people} des personnes qui sont vouées à lutiliser.",
"fit the needs and uses of the people": "aux besoins et usages", "fit the needs and uses of the people": "aux besoins et usages",
"Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the fall of 2020</b>.": "Mobilizon est en cours de développement, nous ajouterons de nouvelles fonctionnalités à ce site lors de mises à jour régulières, jusqu'à la publication de <b>la version 1 du logiciel à l'automne 2020</b>." "Mobilizon is under development, we will add new features to this site during regular updates, until the release of <b>version 1 of the software in the fall of 2020</b>.": "Mobilizon est en cours de développement, nous ajouterons de nouvelles fonctionnalités à ce site lors de mises à jour régulières, jusqu'à la publication de <b>la version 1 du logiciel à l'automne 2020</b>.",
"To activate more notifications, head over to the notification settings.": "Pour activer plus de notifications, rendez-vous dans vos paramètres de notification.",
"Manage my notifications": "Gérer mes notifications",
"We use your timezone to make sure you get notifications for an event at the correct time.": "Nous utilisons votre fuseau hoaire pour nous assurer que vous recevez les notifications pour un événement au bon moment.",
"Your timezone was detected as {timezone}.": "Votre fuseau horaire a été détecté en tant que {timezone}.",
"Manage my settings": "Gérer mes paramètres",
"Let's define a few settings": "Définissons quelques paramètres",
"All good, let's continue!": "C'est tout bon, continuons !"
} }

View File

@ -28,8 +28,8 @@ export enum INotificationPendingParticipationEnum {
export interface IUserSettings { export interface IUserSettings {
timezone: string; timezone: string;
notificationOnDay: string; notificationOnDay: boolean;
notificationEachWeek: string; notificationEachWeek: boolean;
notificationBeforeEvent: string; notificationBeforeEvent: boolean;
notificationPendingParticipation: INotificationPendingParticipationEnum; notificationPendingParticipation: INotificationPendingParticipationEnum;
} }

View File

@ -30,7 +30,7 @@
</div> </div>
</div> </div>
</section> </section>
<div class="container section" v-if="config"> <div class="container section" v-if="config && loggedUser && loggedUser.settings">
<section v-if="currentActor.id"> <section v-if="currentActor.id">
<b-message type="is-info" v-if="welcomeBack">{{ <b-message type="is-info" v-if="welcomeBack">{{
$t("Welcome back {username}!", { username: currentActor.displayName() }) $t("Welcome back {username}!", { username: currentActor.displayName() })
@ -102,6 +102,7 @@
<b-message v-else type="is-danger">{{ $t("No events found") }}</b-message> <b-message v-else type="is-danger">{{ $t("No events found") }}</b-message>
</section> </section>
</div> </div>
<settings-onboard v-else-if="config && loggedUser && loggedUser.settings == undefined" />
</div> </div>
</template> </template>
@ -114,7 +115,7 @@ import EventCard from "../components/Event/EventCard.vue";
import { CURRENT_ACTOR_CLIENT, LOGGED_USER_PARTICIPATIONS } from "../graphql/actor"; import { CURRENT_ACTOR_CLIENT, LOGGED_USER_PARTICIPATIONS } from "../graphql/actor";
import { IPerson, Person } from "../types/actor"; import { IPerson, Person } from "../types/actor";
import { ICurrentUser } from "../types/current-user.model"; import { ICurrentUser } from "../types/current-user.model";
import { CURRENT_USER_CLIENT } from "../graphql/user"; import { CURRENT_USER_CLIENT, USER_SETTINGS } from "../graphql/user";
import RouteName from "../router/name"; import RouteName from "../router/name";
import { import {
EventModel, EventModel,
@ -138,12 +139,12 @@ import Subtitle from "../components/Utils/Subtitle.vue";
query: CURRENT_ACTOR_CLIENT, query: CURRENT_ACTOR_CLIENT,
update: (data) => new Person(data.currentActor), update: (data) => new Person(data.currentActor),
}, },
currentUser: { currentUser: CURRENT_USER_CLIENT,
query: CURRENT_USER_CLIENT, loggedUser: {
}, query: USER_SETTINGS,
config: { fetchPolicy: "no-cache",
query: CONFIG,
}, },
config: CONFIG,
currentUserParticipations: { currentUserParticipations: {
query: LOGGED_USER_PARTICIPATIONS, query: LOGGED_USER_PARTICIPATIONS,
variables() { variables() {
@ -167,6 +168,7 @@ import Subtitle from "../components/Utils/Subtitle.vue";
DateComponent, DateComponent,
EventListCard, EventListCard,
EventCard, EventCard,
"settings-onboard": () => import("./User/SettingsOnboard.vue"),
}, },
metaInfo() { metaInfo() {
return { return {
@ -189,6 +191,8 @@ export default class Home extends Vue {
currentUser!: ICurrentUser; currentUser!: ICurrentUser;
loggedUser!: ICurrentUser;
currentActor!: IPerson; currentActor!: IPerson;
config!: IConfig; config!: IConfig;

View File

@ -24,7 +24,7 @@
$t("We'll use your timezone settings to send a recap of the morning of the event.") $t("We'll use your timezone settings to send a recap of the morning of the event.")
}} }}
</p> </p>
<span v-if="loggedUser.settings.timezone">{{ <span v-if="loggedUser.settings && loggedUser.settings.timezone">{{
$t("Your timezone is currently set to {timezone}.", { $t("Your timezone is currently set to {timezone}.", {
timezone: loggedUser.settings.timezone, timezone: loggedUser.settings.timezone,
}) })
@ -115,6 +115,16 @@ export default class Notifications extends Vue {
}; };
} }
@Watch("loggedUser")
setSettings() {
if (this.loggedUser && this.loggedUser.settings) {
this.notificationOnDay = this.loggedUser.settings.notificationOnDay;
this.notificationEachWeek = this.loggedUser.settings.notificationEachWeek;
this.notificationBeforeEvent = this.loggedUser.settings.notificationBeforeEvent;
this.notificationPendingParticipation = this.loggedUser.settings.notificationPendingParticipation;
}
}
async updateSetting(variables: object) { async updateSetting(variables: object) {
await this.$apollo.mutate<{ setUserSettings: string }>({ await this.$apollo.mutate<{ setUserSettings: string }>({
mutation: SET_USER_SETTINGS, mutation: SET_USER_SETTINGS,

View File

@ -0,0 +1,48 @@
<template>
<div class="section container">
<h1 class="title">{{ $t("Let's define a few settings") }}</h1>
<settings-onboarding />
<notifications-onboarding />
<section class="has-text-centered section">
<b-button @click="refresh()" type="is-success" size="is-big">
{{ $t("All good, let's continue!") }}
</b-button>
</section>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from "vue-property-decorator";
import { SET_USER_SETTINGS } from "../../graphql/user";
import { TIMEZONES } from "../../graphql/config";
import { IConfig } from "../../types/config.model";
@Component({
components: {
NotificationsOnboarding: () => import("../../components/Settings/NotificationsOnboarding.vue"),
SettingsOnboarding: () => import("../../components/Settings/SettingsOnboarding.vue"),
},
apollo: {
config: TIMEZONES,
},
})
export default class SettingsOnboard extends Vue {
config!: IConfig;
@Watch("config")
async timezoneLoaded() {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
if (this.config && this.config.timezones.includes(timezone)) {
await this.$apollo.mutate<{ setUserSettings: string }>({
mutation: SET_USER_SETTINGS,
variables: {
timezone,
},
});
}
}
refresh() {
location.reload();
}
}
</script>