Preferences.vue 7.84 KB
Newer Older
Thomas Citharel's avatar
Thomas Citharel committed
1
<template>
2
  <div>
Thomas Citharel's avatar
Thomas Citharel committed
3
4
5
    <nav class="breadcrumb" aria-label="breadcrumbs">
      <ul>
        <li>
Thomas Citharel's avatar
Thomas Citharel committed
6
7
8
          <router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{
            $t("Account")
          }}</router-link>
Thomas Citharel's avatar
Thomas Citharel committed
9
10
        </li>
        <li class="is-active">
Thomas Citharel's avatar
Thomas Citharel committed
11
12
13
          <router-link :to="{ name: RouteName.PREFERENCES }">{{
            $t("Preferences")
          }}</router-link>
Thomas Citharel's avatar
Thomas Citharel committed
14
15
16
17
18
19
20
        </li>
      </ul>
    </nav>
    <div>
      <b-field :label="$t('Language')">
        <b-select
          :loading="!config || !loggedUser"
Thomas Citharel's avatar
Thomas Citharel committed
21
          v-model="locale"
Thomas Citharel's avatar
Thomas Citharel committed
22
23
          :placeholder="$t('Select a language')"
        >
Thomas Citharel's avatar
Thomas Citharel committed
24
          <option v-for="(language, lang) in langs" :value="lang" :key="lang">
Thomas Citharel's avatar
Thomas Citharel committed
25
            {{ language }}
26
          </option>
Thomas Citharel's avatar
Thomas Citharel committed
27
28
        </b-select>
      </b-field>
29
      <b-field :label="$t('Timezone')" v-if="selectedTimezone">
Thomas Citharel's avatar
Thomas Citharel committed
30
31
32
33
34
        <b-select
          :placeholder="$t('Select a timezone')"
          :loading="!config || !loggedUser"
          v-model="selectedTimezone"
        >
Thomas Citharel's avatar
Thomas Citharel committed
35
36
37
38
39
          <optgroup
            :label="group"
            v-for="(groupTimezones, group) in timezones"
            :key="group"
          >
Thomas Citharel's avatar
Thomas Citharel committed
40
41
42
43
44
45
46
47
48
49
            <option
              v-for="timezone in groupTimezones"
              :value="`${group}/${timezone}`"
              :key="timezone"
            >
              {{ sanitize(timezone) }}
            </option>
          </optgroup>
        </b-select>
      </b-field>
50
      <em v-if="Intl.DateTimeFormat().resolvedOptions().timeZone">{{
Thomas Citharel's avatar
Thomas Citharel committed
51
52
53
54
        $t("Timezone detected as {timezone}.", {
          timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
        })
      }}</em>
Thomas Citharel's avatar
Thomas Citharel committed
55
56
57
      <b-message v-else type="is-danger">{{
        $t("Unable to detect timezone.")
      }}</b-message>
58
59
60
61
      <hr />
      <b-field grouped>
        <b-field :label="$t('City or region')" expanded>
          <address-auto-complete
62
            v-if="loggedUser && loggedUser.settings"
63
            :type="AddressSearchType.ADMINISTRATIVE"
64
            :doGeoLocation="false"
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
            v-model="address"
          >
          </address-auto-complete>
        </b-field>
        <b-field :label="$t('Radius')">
          <b-select
            :placeholder="$t('Select a radius')"
            v-model="locationRange"
          >
            <option
              v-for="index in [1, 5, 10, 25, 50, 100]"
              :key="index"
              :value="index"
            >
              {{ $tc("{count} km", index, { count: index }) }}
            </option>
          </b-select>
        </b-field>
83
84
85
86
87
88
        <b-button
          :disabled="address == undefined"
          @click="resetArea"
          class="reset-area"
          icon-left="close"
        />
89
90
91
92
      </b-field>
      <p>
        {{
          $t(
93
            "Your city or region and the radius will only be used to suggest you events nearby. The event radius will consider the administrative center of the area."
94
95
96
          )
        }}
      </p>
Thomas Citharel's avatar
Thomas Citharel committed
97
    </div>
98
  </div>
Thomas Citharel's avatar
Thomas Citharel committed
99
100
</template>
<script lang="ts">
Thomas Citharel's avatar
Thomas Citharel committed
101
import { Component, Vue } from "vue-property-decorator";
102
import ngeohash from "ngeohash";
Thomas Citharel's avatar
Thomas Citharel committed
103
import { saveLocaleData } from "@/utils/auth";
104
import { TIMEZONES } from "../../graphql/config";
Thomas Citharel's avatar
Thomas Citharel committed
105
106
107
108
109
import {
  USER_SETTINGS,
  SET_USER_SETTINGS,
  UPDATE_USER_LOCALE,
} from "../../graphql/user";
110
import { IConfig } from "../../types/config.model";
111
import { IUser, IUserSettings } from "../../types/current-user.model";
Thomas Citharel's avatar
Thomas Citharel committed
112
import langs from "../../i18n/langs.json";
Thomas Citharel's avatar
Thomas Citharel committed
113
import RouteName from "../../router/name";
114
115
116
import AddressAutoComplete from "../../components/Event/AddressAutoComplete.vue";
import { AddressSearchType } from "@/types/enums";
import { Address, IAddress } from "@/types/address.model";
Thomas Citharel's avatar
Thomas Citharel committed
117

118
119
120
121
122
@Component({
  apollo: {
    config: TIMEZONES,
    loggedUser: USER_SETTINGS,
  },
123
124
125
  components: {
    AddressAutoComplete,
  },
126
127
128
129
130
  metaInfo() {
    return {
      title: this.$t("Preferences") as string,
    };
  },
131
})
Thomas Citharel's avatar
Thomas Citharel committed
132
export default class Preferences extends Vue {
133
  config!: IConfig;
Thomas Citharel's avatar
Thomas Citharel committed
134

135
  loggedUser!: IUser;
136

Thomas Citharel's avatar
Thomas Citharel committed
137
138
  RouteName = RouteName;

Thomas Citharel's avatar
Thomas Citharel committed
139
140
  langs: Record<string, string> = langs;

141
142
  AddressSearchType = AddressSearchType;

Thomas Citharel's avatar
Thomas Citharel committed
143
144
145
146
  get selectedTimezone(): string {
    if (this.loggedUser?.settings?.timezone) {
      return this.loggedUser.settings.timezone;
    }
147
148
149
150
151
    const detectedTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
    if (this.loggedUser?.settings?.timezone === null) {
      this.updateUserSettings({ timezone: detectedTimezone });
    }
    return detectedTimezone;
Thomas Citharel's avatar
Thomas Citharel committed
152
153
154
155
156
157
158
159
160
161
162
  }

  set selectedTimezone(selectedTimezone: string) {
    if (selectedTimezone !== this.loggedUser?.settings?.timezone) {
      this.updateUserSettings({ timezone: selectedTimezone });
    }
  }

  get locale(): string {
    if (this.loggedUser?.locale) {
      return this.loggedUser?.locale;
163
    }
Thomas Citharel's avatar
Thomas Citharel committed
164
165
166
167
168
169
170
171
172
173
174
175
    return this.$i18n.locale;
  }

  set locale(locale: string) {
    if (locale) {
      this.$apollo.mutate({
        mutation: UPDATE_USER_LOCALE,
        variables: {
          locale,
        },
      });
      saveLocaleData(locale);
Thomas Citharel's avatar
Thomas Citharel committed
176
    }
177
178
  }

Thomas Citharel's avatar
Thomas Citharel committed
179
  // eslint-disable-next-line class-methods-use-this
180
  sanitize(timezone: string): string {
Thomas Citharel's avatar
Thomas Citharel committed
181
182
183
184
185
186
    return timezone
      .split("_")
      .join(" ")
      .replace("St ", "St. ")
      .split("/")
      .join(" - ");
187
188
  }

Thomas Citharel's avatar
Thomas Citharel committed
189
  get timezones(): Record<string, string[]> {
190
    if (!this.config || !this.config.timezones) return {};
Thomas Citharel's avatar
Thomas Citharel committed
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
    return this.config.timezones.reduce(
      (acc: { [key: string]: Array<string> }, val: string) => {
        const components = val.split("/");
        const [prefix, suffix] = [
          components.shift() as string,
          components.join("/"),
        ];
        const pushOrCreate = (
          acc2: { [key: string]: Array<string> },
          prefix2: string,
          suffix2: string
        ) => {
          // eslint-disable-next-line no-param-reassign
          (acc2[prefix2] = acc2[prefix2] || []).push(suffix2);
          return acc2;
        };
        if (suffix) {
          return pushOrCreate(acc, prefix, suffix);
        }
        return pushOrCreate(acc, this.$t("Other") as string, prefix);
      },
      {}
    );
214
215
  }

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  get address(): IAddress | null {
    if (
      this.loggedUser?.settings?.location?.name &&
      this.loggedUser?.settings?.location?.geohash
    ) {
      const { latitude, longitude } = ngeohash.decode(
        this.loggedUser?.settings?.location?.geohash
      );
      const name = this.loggedUser?.settings?.location?.name;
      return {
        description: name,
        locality: "",
        type: "administrative",
        geom: `${longitude};${latitude}`,
        street: "",
        postalCode: "",
        region: "",
        country: "",
      };
    }
    return null;
  }

  set address(address: IAddress | null) {
    if (address && address.geom) {
      const { geom } = address;
      const addressObject = new Address(address);
      const queryText = addressObject.poiInfos.name;
      const [lon, lat] = geom.split(";");
      const geohash = ngeohash.encode(lat, lon, 6);
      if (queryText && geom) {
        this.updateUserSettings({
          location: {
            geohash,
            name: queryText,
          },
        });
      }
    }
  }

257
  get locationRange(): number | undefined | null {
258
259
260
    return this.loggedUser?.settings?.location?.range;
  }

261
  set locationRange(locationRange: number | undefined | null) {
262
263
264
265
266
267
268
269
270
    if (locationRange) {
      this.updateUserSettings({
        location: {
          range: locationRange,
        },
      });
    }
  }

271
272
273
274
275
276
277
278
279
280
  resetArea(): void {
    this.updateUserSettings({
      location: {
        geohash: null,
        name: null,
        range: null,
      },
    });
  }

281
282
283
284
  private async updateUserSettings(userSettings: IUserSettings) {
    await this.$apollo.mutate<{ setUserSetting: string }>({
      mutation: SET_USER_SETTINGS,
      variables: userSettings,
285
      refetchQueries: [{ query: USER_SETTINGS }],
286
287
    });
  }
Thomas Citharel's avatar
Thomas Citharel committed
288
}
289
</script>
290
291
292
293
294
295
296
<style lang="scss" scoped>
.reset-area {
  align-self: center;
  position: relative;
  top: 10px;
}
</style>