adding creation form

pull/6/head
Namekuji 2022-12-07 00:45:05 -05:00
rodzic 636c1c3c18
commit c4aa9cd2a8
10 zmienionych plików z 294 dodań i 35 usunięć

18
audon-fe/package-lock.json wygenerowano
Wyświetl plik

@ -11,7 +11,7 @@
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"axios": "^1.2.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"masto": "^4.9.0",
"pinia": "^2.0.26",
"vue": "^3.2.45",
@ -1713,7 +1713,13 @@
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.merge": {
"version": "4.6.2",
@ -3898,7 +3904,13 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"lodash.merge": {
"version": "4.6.2",

Wyświetl plik

@ -12,7 +12,7 @@
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"axios": "^1.2.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"masto": "^4.9.0",
"pinia": "^2.0.26",
"vue": "^3.2.45",

Wyświetl plik

@ -1,22 +1,32 @@
<script setup>
import { RouterView } from 'vue-router'
import { useMastodonStore } from "./stores/mastodon"
const donStore = useMastodonStore();
donStore.fetchToken();
</script>
<template>
<v-container class="fill-height">
<v-row align="center" justify="center" class="fill-height">
<v-col id="appView">
<v-responsive class="mx-auto" max-width="600px">
<RouterView />
</v-responsive>
</v-col>
</v-row>
</v-container>
<v-app class="fill-height">
<v-system-bar :height="40">
<v-row>
<v-col class="text-center">
<h2>Audon</h2>
</v-col>
</v-row>
</v-system-bar>
<v-main>
<v-container class="fill-height">
<v-row align="center" justify="center" class="fill-height">
<v-col>
<v-responsive class="mx-auto" max-width="600px">
<RouterView />
</v-responsive>
</v-col>
</v-row>
</v-container>
</v-main>
</v-app>
</template>
<style>
#app .v-application__wrap {
min-height: 100%;
}
</style>

Wyświetl plik

@ -6,3 +6,8 @@ export const validators = {
/^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$/
),
}
export function webfinger(user) {
const url = new URL(user.url)
return `${user.acct}@${url.host}`
}

Wyświetl plik

@ -18,6 +18,7 @@ const vuetify = createVuetify({
defaultTheme: "dark",
},
icons: {
defaultSet: "mdi",
aliases,
sets: {
mdi,
@ -26,6 +27,7 @@ const vuetify = createVuetify({
});
axios.defaults.withCredentials = true;
// if audon server returns 401, display the login form
axios.interceptors.response.use(undefined, (error) => {
if (error.response?.status === 401) {
@ -34,11 +36,22 @@ axios.interceptors.response.use(undefined, (error) => {
}
return Promise.reject(error);
});
router.beforeEach(async (to) => {
const donStore = useMastodonStore();
if (!to.meta.noauth && !donStore.authorized) {
try {
await donStore.fetchToken();
} catch (error) {
if (error.response?.status === 401) {
donStore.$reset();
}
}
}
})
router.afterEach((to) => {
const donStore = useMastodonStore();
if (!to.meta.noauth && !donStore.authorized) {
router.push({ name: "login" });
router.push({ name: "login" }); // need to push in afterEach to get nonempty lastPath in LoginView.vue
}
});

Wyświetl plik

@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";
import LoginView from "../views/LoginView.vue";
import CreateView from "../views/CreateView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@ -25,10 +26,14 @@ const router = createRouter({
path: "/login",
name: "login",
meta: {
noauth: true
noauth: true,
},
component: LoginView,
// component: () => import("../views/LoginView.vue"),
},
{
path: "/create",
name: "create",
component: CreateView,
},
],
});

Wyświetl plik

@ -1,23 +1,47 @@
import { defineStore } from "pinia";
import axios from "axios";
import { login } from "masto";
import router from "../router";
import { webfinger } from "../assets/utils";
export const useMastodonStore = defineStore("mastodon", {
state() {
return {
authorized: false,
oauth: null,
authorized: false
client: null,
userinfo: null,
};
},
getters: {
myWebfinger() {
if (this.userinfo !== null) {
return webfinger(this.userinfo);
}
return "";
},
},
actions: {
async fetchToken() {
const resp = await axios.get("/api/token");
this.oauth = resp.data;
const client = await login({
url: this.oauth.url,
accessToken: this.oauth.token,
});
this.client = client;
this.userinfo = await client.accounts.verifyCredentials();
this.authorized = true;
},
async callMastodonAPI(caller, ...args) {
try {
const resp = await axios.get("/api/token");
this.$state.oauth = resp.data;
this.$state.authorized = true
return await caller(...args);
} catch (error) {
if (error.response?.status !== 401) {
alert(`Server is down: ${error.response.status}`);
if (error.response?.status === 401) {
this.$reset();
router.push({ name: "login" });
}
throw error;
}
},
},

Wyświetl plik

@ -0,0 +1,159 @@
<script>
import { mdiArrowLeft, mdiMagnify, mdiClose } from "@mdi/js";
import { debounce } from "lodash-es";
import { login } from "masto";
import { webfinger } from "../assets/utils";
export default {
created() {
this.cohostSearch = debounce(this.search, 1000);
},
unmounted() {
this.cohostSearch.cancel();
},
data() {
return {
mdiArrowLeft,
mdiMagnify,
mdiClose,
title: "",
description: "",
cohosts: [
{
acct: "admin",
displayName: "Test Name",
avatar:
"https://mstdn.nmkj.tk/system/accounts/avatars/109/453/041/233/122/320/original/a111d61c635f57ae.png",
url: "https://mstdn.nmkj.tk/@admin",
},
],
relationship: "everyone",
relOptions: [
{ title: "制限なし", value: "everyone" },
{ title: "フォロー限定", value: "following" },
{ title: "フォロワー限定", value: "follower" },
{ title: "フォローまたはフォロワー限定", value: "knows" },
{ title: "相互フォロー限定", value: "mutual" },
],
searchResult:
{
acct: "user1",
displayName: "User 1",
avatar:
"https://media.songbird.cloud/accounts/avatars/109/374/604/338/855/643/original/23b2384b4bc8ccae.png",
url: "https://mstdn.nmkj.tk/@user1",
},
searchQuery: "",
isCandiadateLoading: false,
};
},
methods: {
async search(val) {
if (!val) return;
const webfinger = val.split("@");
if (webfinger.length < 2) return;
this.cohostSearch(webfinger);
try {
const url = new URL(`https://${webfinger[1]}`);
this.isCandiadateLoading = true;
const client = await login({ url: url.toString() });
const user = await client.accounts.lookup({ acct: webfinger[0] });
this.searchResult = [user];
} catch {
} finally {
this.isCandiadateLoading = false;
}
},
webfinger
},
};
</script>
<template>
<main>
<div>
<v-btn class="ma-2" variant="text" color="blue" :to="{ name: 'home' }">
<v-icon start :icon="mdiArrowLeft"></v-icon>
戻る
</v-btn>
<v-card>
<v-card-title class="text-center">部屋を新規作成</v-card-title>
<v-card-text>
<v-form>
<v-text-field v-model="title" label="タイトル"></v-text-field>
<v-textarea
auto-grow
v-model="description"
rows="2"
label="説明"
></v-textarea>
<v-select
:items="relOptions"
label="入室制限"
v-model="relationship"
disabled
:messages="['今後のアップデートで追加予定']"
></v-select>
<v-card class="my-2" variant="outlined">
<v-card-title class="text-subtitle-1">共同ホスト</v-card-title>
<v-card-text v-if="(cohosts.length > 0 || searchResult)">
<div v-if="(cohosts.length > 0)">
<v-list lines="two" variant="tonal">
<v-list-item
v-for="(cohost, index) in cohosts"
:key="cohost.url"
:title="cohost.displayName"
rounded
>
<template v-slot:prepend>
<v-avatar class="rounded">
<v-img :src="cohost.avatar"></v-img>
</v-avatar>
</template>
<template v-slot:subtitle>
{{ webfinger(cohost) }}
</template>
<template v-slot:append>
<v-btn variant="plain" size="small" :icon="mdiClose" @click="() => {cohosts.splice(index)}"></v-btn>
</template>
</v-list-item>
</v-list>
</div>
<div v-if="(searchResult)">
<v-divider></v-divider>
<v-list lines="two" variant="flat">
<v-list-item :title="searchResult.displayName">
<template v-slot:prepend>
<v-avatar class="rounded">
<v-img :src="searchResult.avatar"></v-img>
</v-avatar>
</template>
<v-list-item-subtitle>
{{ webfinger(searchResult) }}
</v-list-item-subtitle>
<v-list-item-action end>
</v-list-item-action>
</v-list-item>
</v-list>
</div>
</v-card-text>
<v-card-actions>
<v-text-field
density="compact"
v-model="searchQuery"
:prepend-inner-icon="mdiMagnify"
single-line
hide-details
clearable
:loading="isCandiadateLoading"
placeholder="user@mastodon.example"
>
</v-text-field>
</v-card-actions>
</v-card>
</v-form>
</v-card-text>
</v-card>
</div>
</main>
</template>

Wyświetl plik

@ -1,12 +1,45 @@
<script>
import { RouterLink } from 'vue-router';
import { RouterLink } from "vue-router";
import { useMastodonStore } from "../stores/mastodon";
export default {
}
setup() {
return {
donStore: useMastodonStore(),
};
},
data() {
return {
query: ""
}
}
};
</script>
<template>
<main>
<RouterLink to="/login" replace>Login</RouterLink>
<div class="text-center my-10" >
<v-avatar class="rounded" size="100">
<v-img
:src="donStore.userinfo?.avatar"
:alt="donStore.userinfo?.displayName"
>
</v-img>
</v-avatar>
<h2 class="mt-5">
{{ donStore.userinfo?.displayName }}
</h2>
<div>
<a :href="donStore.userinfo?.url">{{ donStore.myWebfinger }}</a>
</div>
</div>
<v-row class="text-center" justify="center">
<!-- <v-col cols="12">
<v-text-field v-mode="query"></v-text-field>
</v-col> -->
<v-col cols="12">
<v-btn block :to="{name: 'create'}" color="indigo">部屋を作成</v-btn>
</v-col>
</v-row>
</main>
</template>

Wyświetl plik

@ -3,7 +3,7 @@ import { RouterLink } from "vue-router";
import { useVuelidate } from "@vuelidate/core";
import { required, helpers } from "@vuelidate/validators";
import { validators } from "../assets/utils";
import _ from "lodash/collection";
import { map } from "lodash-es";
import axios from "axios";
export default {
@ -37,7 +37,7 @@ export default {
computed: {
serverErrors() {
const errors = this.v$.server.$errors;
const messages = _.map(errors, (e) => e.$message);
const messages = map(errors, (e) => e.$message);
if (this.serverErr !== "") {
messages.push(this.serverErr);
}
@ -56,9 +56,8 @@ export default {
server: this.server,
});
if (response.status === 201) {
// this.$router.push(response.data)
location.assign(response.data);
this.serverErr = "";
location.assign(response.data);
}
} catch (error) {
if (error.response?.status === 404) {
@ -78,7 +77,6 @@ export default {
<v-alert v-if="$route.query.warn" type="warning" variant="text">
<div>ログインが必要です</div>
</v-alert>
<h1>Audon</h1>
<v-form ref="form" @submit.prevent="onSubmit" class="my-3" lazy-validation>
<v-text-field
v-model="server"