Migrate to Vue 3 and Vite
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -9,12 +9,11 @@ import {
|
||||
} from "@/constants";
|
||||
import { ILogin, IToken } from "@/types/login.model";
|
||||
import { UPDATE_CURRENT_USER_CLIENT } from "@/graphql/user";
|
||||
import { ApolloClient } from "@apollo/client/core/ApolloClient";
|
||||
import { IPerson } from "@/types/actor";
|
||||
import { IDENTITIES, UPDATE_CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
|
||||
import { UPDATE_CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
|
||||
import { ICurrentUserRole } from "@/types/enums";
|
||||
import { NormalizedCacheObject } from "@apollo/client/cache/inmemory/types";
|
||||
import { LOGOUT } from "@/graphql/auth";
|
||||
import { provideApolloClient, useMutation } from "@vue/apollo-composable";
|
||||
import { apolloClient } from "@/vue-apollo";
|
||||
|
||||
export function saveTokenData(obj: IToken): void {
|
||||
localStorage.setItem(AUTH_ACCESS_TOKEN, obj.accessToken);
|
||||
@@ -37,10 +36,6 @@ export function getLocaleData(): string | null {
|
||||
return localStorage ? localStorage.getItem(USER_LOCALE) : null;
|
||||
}
|
||||
|
||||
export function saveActorData(obj: IPerson): void {
|
||||
localStorage.setItem(AUTH_USER_ACTOR_ID, `${obj.id}`);
|
||||
}
|
||||
|
||||
export function deleteUserData(): void {
|
||||
[
|
||||
AUTH_USER_ID,
|
||||
@@ -54,78 +49,35 @@ export function deleteUserData(): void {
|
||||
});
|
||||
}
|
||||
|
||||
export class NoIdentitiesException extends Error {}
|
||||
export async function logout(performServerLogout = true): Promise<void> {
|
||||
const { mutate: logout } = provideApolloClient(apolloClient)(() =>
|
||||
useMutation(LOGOUT)
|
||||
);
|
||||
const { mutate: cleanUserClient } = provideApolloClient(apolloClient)(() =>
|
||||
useMutation(UPDATE_CURRENT_USER_CLIENT)
|
||||
);
|
||||
const { mutate: cleanActorClient } = provideApolloClient(apolloClient)(() =>
|
||||
useMutation(UPDATE_CURRENT_ACTOR_CLIENT)
|
||||
);
|
||||
|
||||
export async function changeIdentity(
|
||||
apollo: ApolloClient<NormalizedCacheObject>,
|
||||
identity: IPerson
|
||||
): Promise<void> {
|
||||
await apollo.mutate({
|
||||
mutation: UPDATE_CURRENT_ACTOR_CLIENT,
|
||||
variables: identity,
|
||||
});
|
||||
saveActorData(identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* We fetch from localStorage the latest actor ID used,
|
||||
* then fetch the current identities to set in cache
|
||||
* the current identity used
|
||||
*/
|
||||
export async function initializeCurrentActor(
|
||||
apollo: ApolloClient<any>
|
||||
): Promise<void> {
|
||||
const actorId = localStorage.getItem(AUTH_USER_ACTOR_ID);
|
||||
|
||||
const result = await apollo.query({
|
||||
query: IDENTITIES,
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
const { identities } = result.data;
|
||||
if (identities.length < 1) {
|
||||
console.warn("Logged user has no identities!");
|
||||
throw new NoIdentitiesException();
|
||||
}
|
||||
const activeIdentity =
|
||||
identities.find((identity: IPerson) => identity.id === actorId) ||
|
||||
(identities[0] as IPerson);
|
||||
|
||||
if (activeIdentity) {
|
||||
await changeIdentity(apollo, activeIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
export async function logout(
|
||||
apollo: ApolloClient<NormalizedCacheObject>,
|
||||
performServerLogout = true
|
||||
): Promise<void> {
|
||||
if (performServerLogout) {
|
||||
await apollo.mutate({
|
||||
mutation: LOGOUT,
|
||||
variables: {
|
||||
refreshToken: localStorage.getItem(AUTH_REFRESH_TOKEN),
|
||||
},
|
||||
logout({
|
||||
refreshToken: localStorage.getItem(AUTH_REFRESH_TOKEN),
|
||||
});
|
||||
}
|
||||
|
||||
await apollo.mutate({
|
||||
mutation: UPDATE_CURRENT_USER_CLIENT,
|
||||
variables: {
|
||||
id: null,
|
||||
email: null,
|
||||
isLoggedIn: false,
|
||||
role: ICurrentUserRole.USER,
|
||||
},
|
||||
cleanUserClient({
|
||||
id: null,
|
||||
email: null,
|
||||
isLoggedIn: false,
|
||||
role: ICurrentUserRole.USER,
|
||||
});
|
||||
|
||||
await apollo.mutate({
|
||||
mutation: UPDATE_CURRENT_ACTOR_CLIENT,
|
||||
variables: {
|
||||
id: null,
|
||||
avatar: null,
|
||||
preferredUsername: null,
|
||||
name: null,
|
||||
},
|
||||
cleanActorClient({
|
||||
id: null,
|
||||
avatar: null,
|
||||
preferredUsername: null,
|
||||
name: null,
|
||||
});
|
||||
|
||||
deleteUserData();
|
||||
|
||||
28
js/src/utils/graphics.ts
Normal file
28
js/src/utils/graphics.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import random from "lodash/random";
|
||||
|
||||
export const randomGradient = (): string => {
|
||||
const direction = [
|
||||
"bg-gradient-to-t",
|
||||
"bg-gradient-to-tr",
|
||||
"bg-gradient-to-r",
|
||||
"bg-gradient-to-br",
|
||||
"bg-gradient-to-b",
|
||||
"bg-gradient-to-bl",
|
||||
"bg-gradient-to-l",
|
||||
"bg-gradient-to-tl",
|
||||
];
|
||||
const gradients = [
|
||||
"from-pink-500 via-red-500 to-yellow-500",
|
||||
"from-green-400 via-blue-500 to-purple-600",
|
||||
"from-pink-400 via-purple-300 to-indigo-400",
|
||||
"from-yellow-300 via-yellow-500 to-yellow-700",
|
||||
"from-yellow-300 via-green-400 to-green-500",
|
||||
"from-red-400 via-red-600 to-yellow-400",
|
||||
"from-green-400 via-green-500 to-blue-700",
|
||||
"from-yellow-400 via-yellow-500 to-yellow-700",
|
||||
"from-green-300 via-green-400 to-purple-700",
|
||||
];
|
||||
return `${direction[random(0, direction.length - 1)]} ${
|
||||
gradients[random(0, gradients.length - 1)]
|
||||
}`;
|
||||
};
|
||||
@@ -1,10 +1,9 @@
|
||||
import Vue from "vue";
|
||||
import VueI18n from "vue-i18n";
|
||||
import { DateFnsPlugin } from "@/plugins/dateFns";
|
||||
import { createI18n } from "vue-i18n";
|
||||
import en from "../i18n/en_US.json";
|
||||
import langs from "../i18n/langs.json";
|
||||
import { getLocaleData } from "./auth";
|
||||
import pluralizationRules from "../i18n/pluralRules";
|
||||
// import messages from "@intlify/vite-plugin-vue-i18n/messages";
|
||||
|
||||
const DEFAULT_LOCALE = "en_US";
|
||||
|
||||
@@ -39,17 +38,18 @@ export const locale =
|
||||
|
||||
console.debug("chosen locale", locale);
|
||||
|
||||
Vue.use(VueI18n);
|
||||
|
||||
export const i18n = new VueI18n({
|
||||
locale: DEFAULT_LOCALE, // set locale
|
||||
export const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: locale, // set locale
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// messages, // set locale messages
|
||||
messages: en, // set locale messages
|
||||
fallbackLocale: DEFAULT_LOCALE,
|
||||
formatFallbackMessages: true,
|
||||
pluralizationRules,
|
||||
fallbackRootWithEmptyString: true,
|
||||
globalInjection: true,
|
||||
});
|
||||
|
||||
console.debug("set VueI18n with default locale", DEFAULT_LOCALE);
|
||||
@@ -58,7 +58,7 @@ const loadedLanguages = [DEFAULT_LOCALE];
|
||||
|
||||
function setI18nLanguage(lang: string): string {
|
||||
console.debug("setting i18n locale to", lang);
|
||||
i18n.locale = lang;
|
||||
i18n.global.locale = lang;
|
||||
setLanguageInDOM(lang);
|
||||
return lang;
|
||||
}
|
||||
@@ -93,19 +93,9 @@ function vueI18NfileForLanguage(lang: string) {
|
||||
return fileForLanguage(matches, lang);
|
||||
}
|
||||
|
||||
function dateFnsfileForLanguage(lang: string) {
|
||||
const matches: Record<string, string> = {
|
||||
en_US: "en-US",
|
||||
en: "en-US",
|
||||
};
|
||||
return fileForLanguage(matches, lang);
|
||||
}
|
||||
|
||||
Vue.use(DateFnsPlugin, { locale: dateFnsfileForLanguage(locale) });
|
||||
|
||||
export async function loadLanguageAsync(lang: string): Promise<string> {
|
||||
// If the same language
|
||||
if (i18n.locale === lang) {
|
||||
if (i18n.global.locale === lang) {
|
||||
console.debug("already using language", lang);
|
||||
return Promise.resolve(setI18nLanguage(lang));
|
||||
}
|
||||
@@ -118,11 +108,9 @@ export async function loadLanguageAsync(lang: string): Promise<string> {
|
||||
// If the language hasn't been loaded yet
|
||||
console.debug("loading language", lang);
|
||||
const newMessages = await import(
|
||||
/* webpackChunkName: "lang-[request]" */ `@/i18n/${vueI18NfileForLanguage(
|
||||
lang
|
||||
)}.json`
|
||||
`../i18n/${vueI18NfileForLanguage(lang)}.json`
|
||||
);
|
||||
i18n.setLocaleMessage(lang, newMessages.default);
|
||||
i18n.global.setLocaleMessage(lang, newMessages.default);
|
||||
loadedLanguages.push(lang);
|
||||
return setI18nLanguage(lang);
|
||||
}
|
||||
|
||||
69
js/src/utils/identity.ts
Normal file
69
js/src/utils/identity.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { AUTH_USER_ACTOR_ID } from "@/constants";
|
||||
import { UPDATE_CURRENT_ACTOR_CLIENT, IDENTITIES } from "@/graphql/actor";
|
||||
import { IPerson } from "@/types/actor";
|
||||
import { apolloClient } from "@/vue-apollo";
|
||||
import {
|
||||
provideApolloClient,
|
||||
useMutation,
|
||||
useQuery,
|
||||
} from "@vue/apollo-composable";
|
||||
import { computed, watch } from "vue";
|
||||
|
||||
export class NoIdentitiesException extends Error {}
|
||||
|
||||
export function saveActorData(obj: IPerson): void {
|
||||
localStorage.setItem(AUTH_USER_ACTOR_ID, `${obj.id}`);
|
||||
}
|
||||
|
||||
export async function changeIdentity(identity: IPerson): Promise<void> {
|
||||
console.debug("Changing identity to", identity);
|
||||
if (!identity.id) return;
|
||||
const { mutate: updateCurrentActorClient } = provideApolloClient(
|
||||
apolloClient
|
||||
)(() => useMutation(UPDATE_CURRENT_ACTOR_CLIENT));
|
||||
|
||||
updateCurrentActorClient(identity);
|
||||
if (identity.id) {
|
||||
saveActorData(identity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We fetch from localStorage the latest actor ID used,
|
||||
* then fetch the current identities to set in cache
|
||||
* the current identity used
|
||||
*/
|
||||
export async function initializeCurrentActor(): Promise<void> {
|
||||
const actorId = localStorage.getItem(AUTH_USER_ACTOR_ID);
|
||||
console.debug(
|
||||
"initializing current actor, using actorId from localstorage",
|
||||
actorId
|
||||
);
|
||||
|
||||
const { result: identitiesResult } = provideApolloClient(apolloClient)(() =>
|
||||
useQuery<{ identities: IPerson[] }>(IDENTITIES)
|
||||
);
|
||||
|
||||
console.debug("identitiesResult", identitiesResult);
|
||||
|
||||
const identities = computed(() => identitiesResult.value?.identities);
|
||||
|
||||
watch(identities, async () => {
|
||||
console.debug("identities found", identities.value);
|
||||
if (identities.value && identities.value.length < 1) {
|
||||
console.warn("Logged user has no identities!");
|
||||
throw new NoIdentitiesException();
|
||||
}
|
||||
const activeIdentity =
|
||||
(identities.value || []).find(
|
||||
(identity: IPerson | undefined) => identity?.id === actorId
|
||||
) || ((identities.value || [])[0] as IPerson);
|
||||
|
||||
console.debug("active identity is", activeIdentity);
|
||||
|
||||
if (activeIdentity) {
|
||||
console.debug("active identity found, setting it up");
|
||||
await changeIdentity(activeIdentity);
|
||||
}
|
||||
});
|
||||
}
|
||||
22
js/src/utils/location.ts
Normal file
22
js/src/utils/location.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import ngeohash from "ngeohash";
|
||||
|
||||
const GEOHASH_DEPTH = 9; // put enough accuracy, radius will be used anyway
|
||||
|
||||
export const coordsToGeoHash = (
|
||||
lat: number | undefined,
|
||||
lon: number | undefined,
|
||||
depth = GEOHASH_DEPTH
|
||||
): string | undefined => {
|
||||
if (lat && lon && depth) {
|
||||
return ngeohash.encode(lat, lon, GEOHASH_DEPTH);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const geoHashToCoords = (
|
||||
geohash: string | undefined
|
||||
): { latitude: number; longitude: number } | undefined => {
|
||||
if (!geohash) return undefined;
|
||||
const { latitude, longitude } = ngeohash.decode(geohash);
|
||||
return latitude && longitude ? { latitude, longitude } : undefined;
|
||||
};
|
||||
80
js/src/utils/share.ts
Normal file
80
js/src/utils/share.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
export const twitterShareUrl = (
|
||||
url: string | undefined,
|
||||
text: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url || !text) return undefined;
|
||||
return `https://twitter.com/intent/tweet?url=${encodeURIComponent(
|
||||
url
|
||||
)}&text=${text}`;
|
||||
};
|
||||
|
||||
export const facebookShareUrl = (
|
||||
url: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url) return undefined;
|
||||
return `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
|
||||
url
|
||||
)}`;
|
||||
};
|
||||
|
||||
export const linkedInShareUrl = (
|
||||
url: string | undefined,
|
||||
text: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url || !text) return undefined;
|
||||
return `https://www.linkedin.com/shareArticle?mini=true&url=${encodeURIComponent(
|
||||
url
|
||||
)}&title=${text}`;
|
||||
};
|
||||
|
||||
export const whatsAppShareUrl = (
|
||||
url: string | undefined,
|
||||
text: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url || !text) return undefined;
|
||||
return `https://wa.me/?text=${encodeURIComponent(
|
||||
basicTextToEncode(url, text)
|
||||
)}`;
|
||||
};
|
||||
|
||||
export const telegramShareUrl = (
|
||||
url: string | undefined,
|
||||
text: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url || !text) return undefined;
|
||||
return `https://t.me/share/url?url=${encodeURIComponent(
|
||||
url
|
||||
)}&text=${encodeURIComponent(text)}`;
|
||||
};
|
||||
|
||||
export const emailShareUrl = (
|
||||
url: string | undefined,
|
||||
text: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url || !text) return undefined;
|
||||
return `mailto:?to=&body=${url}&subject=${text}`;
|
||||
};
|
||||
|
||||
export const diasporaShareUrl = (
|
||||
url: string | undefined,
|
||||
text: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url || !text) return undefined;
|
||||
return `https://share.diasporafoundation.org/?title=${encodeURIComponent(
|
||||
text
|
||||
)}&url=${encodeURIComponent(url)}`;
|
||||
};
|
||||
|
||||
export const mastodonShareUrl = (
|
||||
url: string | undefined,
|
||||
text: string | undefined
|
||||
): string | undefined => {
|
||||
if (!url || !text) return undefined;
|
||||
return `https://toot.kytta.dev/?text=${encodeURIComponent(
|
||||
basicTextToEncode(url, text)
|
||||
)}`;
|
||||
};
|
||||
|
||||
const basicTextToEncode = (url: string, text: string): string => {
|
||||
return `${text}\r\n${url}`;
|
||||
};
|
||||
Reference in New Issue
Block a user