add initial support of i18n

pull/6/head
Namekuji 2022-12-16 21:30:46 -05:00
rodzic 4884afbbbb
commit abee1fa2e9
17 zmienionych plików z 1143 dodań i 150 usunięć

Wyświetl plik

@ -1,12 +1,30 @@
# Domain of your Audon server
LOCAL_DOMAIN=audon.example.com
#### Database Settings ####
# Host of MongoDB, set as [host]:[port]
DB_HOST=db:27017
# Database name in MongoDB
DB_NAME=audon
# Username to connect to MongoDB
DB_USER=mongo
# Password to connect to MongoDB
DB_PASS=mongo
#### Redis Settings ####
# Host of Redis, set as [host]:[port]
REDIS_HOST=redis:6379
# Username to connect to Redis (optional)
REDIS_USER=
# Password to connect to Redis (optional)
REDIS_PASS=
### LiveKit Settings ###
# Same as the keys field in livekit.yaml
LIVEKIT_API_KEY=devkey
# Same as the keys field in livekit.yaml
LIVEKIT_API_SECRET=secret
# Host of LiveKit, should be a different domain from LOCAL_DOMAIN
LIVEKIT_HOST=livekit.example.com
# This value will be returned by Audon backend to browsers. Set the same domain as LIVEKIT_HOST if you are not sure.
LIVEKIT_LOCAL_DOMAIN=livekit.example.com

846
audon-fe/package-lock.json wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -9,6 +9,7 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"@intlify/unplugin-vue-i18n": "^0.8.1",
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"@vueuse/core": "^9.6.0",
@ -18,6 +19,7 @@
"masto": "^4.9.0",
"pinia": "^2.0.26",
"vue": "^3.2.45",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.6",
"vuetify": "^3.0.3"
},

Wyświetl plik

@ -1,28 +1,64 @@
<script setup>
import { RouterView, RouterLink } from 'vue-router'
<script>
import { RouterView, RouterLink } from "vue-router";
import locales from "./locales"
export default {
setup() {
return {
locales
}
},
methods: {
onLocaleChange() {
localStorage.setItem("locale", this.$i18n.locale);
}
}
}
</script>
<template>
<v-app class="fill-height">
<v-system-bar window>
<v-row>
<v-col class="text-center">
<h2><RouterLink :to="{name: 'home'}" style="text-decoration: inherit; color: inherit;">Audon</RouterLink></h2>
</v-col>
</v-row>
<div style="position:fixed">v0.1.0-dev3</div>
<h2 class="text-center w-100">
<RouterLink
:to="{ name: 'home' }"
style="text-decoration: inherit; color: inherit;"
>Audon</RouterLink
>
</h2>
</v-system-bar>
<v-main>
<v-container class="fill-height">
<v-row align="center" justify="center" class="fill-height" id="mainArea">
<v-row
align="center"
justify="center"
class="fill-height"
id="mainArea"
>
<v-col>
<v-responsive class="mx-auto" max-width="600px">
<RouterView />
<RouterView />
</v-responsive>
</v-col>
</v-row>
</v-container>
</v-main>
<v-bottom-navigation :height="30">
<div class="w-100 d-flex justify-space-between align-center px-3">
<div>v0.1.0-dev3</div>
<div>
<select v-model="$i18n.locale" id="localeSelector" @change="onLocaleChange">
<option
v-for="locale in $i18n.availableLocales"
:key="`locale-${locale}`"
:value="locale"
>
{{ locales[locale] }}
</option>
</select>
</div>
</div>
</v-bottom-navigation>
</v-app>
</template>
@ -30,4 +66,9 @@ import { RouterView, RouterLink } from 'vue-router'
#app .v-application__wrap {
min-height: 100%;
}
#localeSelector option {
background: black;
color: white;
}
</style>

Wyświetl plik

@ -0,0 +1,69 @@
about: "What is Audon?"
back: "Back"
login: "Sign In"
logout: "Sign out"
logoutConfirm: "Are you sure you want to sign out from Audon?"
loginRequired: "You need to sign in to view this page."
create: "Create"
cancel: "Cancel"
edit: "Edit"
save: "Save"
share: "Share"
copy: "Copy"
copied: "Copied"
enterRoom: "Enter"
leaveRoom: "Leave"
closeRoom: "Close"
close: "Close"
server: "Your Mastodon server"
addressRequired: "Enter your server address"
invalidAddress: "Invalid address"
serverNotFound: "Server not found"
createNewRoom: "Create New Room"
editRoom: "Edit Room"
comingFuture: "Coming with future update!"
form:
title: "Title"
titleRequired: "Room title required"
description: "Description"
restriction: "Who can join"
cohosts: "Cohosts"
cohostCanAlwaysJoin: "Cohosts can join regardless of this setting."
schedule: "Schedule at"
relationships:
everyone: "Everyone"
following: "Your following accounts"
follower: "Your follower accounts"
knowing: "Your following or follower accounts"
mutual: "Your mutual-follow accounts"
private: "Cohosts only"
shareRoomMessage: "Join my Audon room!\n{link}\n\nTitle: {title}"
roomReady:
header: "Your room is ready!"
message: "Your room \"{title}\" is now ready. Share the following URL with other participants."
errors:
notFound: "{value} not found"
alreadyAdded: "Already added"
connectionFailed: "Failed to connect"
alreadyConnected: "You have already joined this room on another device. Please wait for a minute to recoonect."
alreadyClosed: "This room has already been closed."
restriction:
following: "Only host's following accounts can join."
follower: "Only host's follower accounts can join."
knowing: "Only host's following or follower accounts can join."
mutual: "Only hots's mutual-follow accounts can join."
private: "Only cohosts can join."
default: "You are not allowed to join."
startListening: "Start Listening"
browserMuted: "Your sound is muted by the browser. Press @:startListening to continue."
speakRequest:
label: "Speak Requests"
dialog: "Are you sure you want to send a request to be a speaker?"
norequest: "No request"
sent: "Request sent!"
receive: "New speak request received!"
microphoneBlocked: "Your browser blocked recording."
closeRoomConfirm: "Are you sure you want to close this room?"
roomEvent:
closedByHost: "Host has closed this room."
removed: "You have been requested to leave."

Wyświetl plik

@ -0,0 +1,7 @@
// This defines display names of locales for the language selector in App.vue.
// Keys must match the file names of *.yaml in src/locales.
export default {
en: "English",
ja: "日本語"
}

Wyświetl plik

@ -0,0 +1,68 @@
about: "Audonについて"
back: "戻る"
login: "ログイン"
logout: "ログアウト"
logoutConfirm: "Audon からログアウトしますか?"
loginRequired: "続行するにはログインする必要があります。"
create: "作成"
cancel: "キャンセル"
edit: "編集"
save: "保存"
copy: "コピー"
copied: "コピーしました"
enterRoom: "入室"
leaveRoom: "退室"
closeRoom: "閉室"
close: "閉じる"
server: "Mastodon サーバー"
addressRequired: "アドレスを入力してください"
invalidAddress: "アドレスが有効ではありません"
serverNotFound: "サーバーが見つかりません"
createNewRoom: "部屋を作成"
editRoom: "部屋の編集"
comingFuture: "今後のアップデートで追加予定"
form:
title: "タイトル"
titleRequired: "部屋の名前を入力してください"
description: "説明"
restriction: "入室制限"
cohosts: "共同ホスト"
cohostCanAlwaysJoin: "共同ホストは制限に関わらず入室できます。"
schedule: "開始予約"
relationships:
everyone: "制限なし"
following: "あなたのフォロー限定"
follower: "あなたのフォロワー限定"
knowing: "あなたのフォローまたはフォロワー限定"
mutual: "あなたの相互フォロー限定"
private: "共同ホスト限定"
shareRoomMessage: "Audon で部屋を作りました!\n参加用リンク {link}\nタイトル {title}"
roomReady:
header: "お部屋の用意ができました!"
message: "{title} を作りました。参加者に以下の URL を共有してください。"
errors:
notFound: "{value} が見つかりません"
alreadyAdded: "すでに追加済みです"
connectionFailed: "接続できませんでした。"
alreadyConnected: "他のデバイスで入室済みです。切断された場合はしばらく待ってからやり直してください。"
alreadyClosed: "この部屋はすでに閉じられています。"
restriction:
following: "この部屋はホストのフォロー限定です。"
follower: "この部屋はホストのフォロワー限定です。"
knowing: "この部屋はホストのフォローまたはフォロワー限定です。"
mutual: "この部屋はホストの相互フォロー限定です。"
private: "この部屋は共同ホスト限定です。"
default: "入室が許可されていません。"
startListening: "視聴を始める"
browserMuted: "ブラウザの設定により無音になっています。続行するには @:startListening ボタンを押してください。"
speakRequest:
label: "発言リクエスト"
dialog: "発言をリクエストしますか?"
norequest: "リクエストはありません"
sent: "発言リクエストを送信しました"
receive: "新しい発言リクエストがあります"
microphoneBlocked: "ブラウザが録音を許可していません。"
closeRoomConfirm: "この部屋を閉じますか?"
roomEvent:
closedByHost: "ホストにより部屋が閉じられました。"
removed: "部屋から退去しました。"

Wyświetl plik

@ -4,7 +4,11 @@ import { createPinia } from "pinia";
import { createVuetify } from "vuetify";
import { aliases, mdi } from "vuetify/iconsets/mdi-svg";
import { createI18n } from "vue-i18n";
import messages from "@intlify/unplugin-vue-i18n/messages";
import axios from "axios";
import { split } from "lodash-es";
import App from "./App.vue";
import router from "./router";
@ -27,6 +31,18 @@ const vuetify = createVuetify({
},
});
const userLocale =
navigator.languages && navigator.languages.length
? navigator.languages[0]
: navigator.language;
const prefLocale = localStorage.getItem("locale");
const i18n = createI18n({
locale: prefLocale ?? split(userLocale, "-", 1)[0],
fallbackLocale: "en",
messages,
});
axios.defaults.withCredentials = true;
// if audon server returns 401, display the login form
@ -52,7 +68,7 @@ router.beforeEach(async (to) => {
router.afterEach((to, from) => {
const donStore = useMastodonStore();
if (!to.meta.noauth && !donStore.authorized) {
const query = to.name !== "home" ? {l: to.path} : {};
const query = to.name !== "home" ? { l: to.path } : {};
router.push({ name: "login", query }); // need to push in afterEach to get nonempty lastPath in LoginView.vue
} else if (to.name === "login" && donStore.authorized) {
router.replace({ name: "home" });
@ -61,6 +77,7 @@ router.afterEach((to, from) => {
const app = createApp(App);
app.use(i18n);
app.use(createPinia());
app.use(vuetify);
app.use(router);

Wyświetl plik

@ -17,7 +17,7 @@ export default {
<template>
<div class="about">
準備中
Under construction
<div>
<RouterLink :to="{ name: 'home' }">Home</RouterLink>
</div>

Wyświetl plik

@ -45,12 +45,12 @@ export default {
cohosts: [],
relationship: "everyone",
relOptions: [
{ title: "制限なし", value: "everyone" },
{ title: "あなたのフォロー限定", value: "following" },
{ title: "あなたのフォロワー限定", value: "follower" },
{ title: "あなたのフォローまたはフォロワー限定", value: "knowing" },
{ title: "あなたの相互フォロー限定", value: "mutual" },
{ title: "共同ホスト限定", value: "private" },
{ title: this.$t("form.relationships.everyone"), value: "everyone" },
{ title: this.$t("form.relationships.following"), value: "following" },
{ title: this.$t("form.relationships.follower"), value: "follower" },
{ title: this.$t("form.relationships.knowing"), value: "knowing" },
{ title: this.$t("form.relationships.mutual"), value: "mutual" },
{ title: this.$t("form.relationships.private"), value: "private" },
],
scheduledAt: null,
searchResult: null,
@ -69,12 +69,12 @@ export default {
validations() {
return {
title: {
required: helpers.withMessage("部屋の名前を入力してください", required),
maxLength: maxLength(100)
required: helpers.withMessage(this.$t("form.titleRequired"), required),
maxLength: maxLength(100),
},
description: {
maxLength: maxLength(500)
}
maxLength: maxLength(500),
},
};
},
computed: {
@ -95,9 +95,7 @@ export default {
if (!donURL) return "";
const url = new URL(donURL);
const texts = [
"Audon で部屋を作りました!",
`参加用リンク: ${this.roomURL}`,
`タイトル: ${this.title}`,
this.$t("shareRoomMessage", { link: this.roomURL, title: this.title }),
];
if (this.description)
texts.push(truncate("\n" + this.description, { length: 200 }));
@ -110,7 +108,7 @@ export default {
this.cohostSearch.cancel();
if (!val) return;
if (some(this.cohosts, { finger: val })) {
this.searchError.message = "すでに追加済みです";
this.searchError.message = this.$t("errors.alreadyAdded");
this.searchError.colour = "warning";
this.searchError.enabled = true;
return;
@ -141,7 +139,7 @@ export default {
this.searchResult = user;
} catch (error) {
if (error.isMastoError && error.statusCode === 404) {
this.searchError.message = `${val} が見つかりません`;
this.searchError.message = this.$t("errors.notFound", { value: val });
this.searchError.colour = "error";
this.searchError.enabled = true;
}
@ -172,7 +170,7 @@ export default {
remote_id: u.id,
remote_url: u.url,
})),
restriction: this.relationship
restriction: this.relationship,
};
this.isSubmissionLoading = false;
try {
@ -195,16 +193,12 @@ export default {
<template>
<v-dialog v-model="isDialogActive" persistent max-width="700">
<v-alert
type="success"
color="blue-gray"
title="お部屋の用意ができました!"
>
<v-alert type="success" color="blue-gray" :title="$t('roomReady.header')">
<div>
{{ title }} を作りました参加者に以下の URL を共有してください
{{ $t("roomReady.message", { title }) }}
</div>
<div class="my-3">
<h3 style="word-break: break-all;">{{ roomURL }}</h3>
<h3 style="word-break: break-all">{{ roomURL }}</h3>
</div>
<div>
<v-btn
@ -213,7 +207,7 @@ export default {
@click="onShareClick"
color="#563ACC"
size="small"
>シェア</v-btn
>{{ $t("share") }}</v-btn
>
<v-btn
@click="clipboard.copy(roomURL)"
@ -222,7 +216,7 @@ export default {
:prepend-icon="
clipboard.copied.value ? mdiClipboardCheck : mdiClipboardEdit
"
>{{ clipboard.copied.value ? "コピーしました" : "コピー" }}</v-btn
>{{ clipboard.copied.value ? $t("copied") : $t("copy") }}</v-btn
>
</div>
<div class="text-center mt-10 mb-1">
@ -230,7 +224,7 @@ export default {
color="indigo"
:to="{ name: 'room', params: { id: createdRoomID } }"
size="large"
>入室</v-btn
>{{ $t("enterRoom") }}</v-btn
>
</div>
</v-alert>
@ -239,7 +233,7 @@ export default {
<div>
<v-btn class="ma-2" variant="text" color="blue" :to="{ name: 'home' }">
<v-icon start :icon="mdiArrowLeft"></v-icon>
戻る
{{ $t("back") }}
</v-btn>
<v-snackbar
v-model="searchError.enabled"
@ -251,12 +245,14 @@ export default {
{{ searchError.message }}
</v-snackbar>
<v-card :loading="isSubmissionLoading">
<v-card-title class="text-center">部屋を新規作成</v-card-title>
<v-card-title class="text-center">{{
$t("createNewRoom")
}}</v-card-title>
<v-card-text>
<v-form>
<v-text-field
v-model="title"
label="タイトル"
:label="$t('form.title')"
:counter="100"
:error-messages="titleErrors"
required
@ -267,18 +263,23 @@ export default {
auto-grow
v-model="description"
rows="2"
label="説明"
:label="$t('form.description')"
:counter="500"
></v-textarea>
<v-select
:items="relOptions"
label="入室制限"
:label="$t('form.restriction')"
v-model="relationship"
:messages="['共同ホストは制限に関わらず入室できます']"
:messages="[$t('form.cohostCanAlwaysJoin')]"
></v-select>
<v-card class="my-3" variant="outlined">
<v-card-title class="text-subtitle-1">共同ホスト</v-card-title>
<v-card-text v-if="cohosts.length > 0 || searchResult" class="py-0">
<v-card-title class="text-subtitle-1">{{
$t("form.cohosts")
}}</v-card-title>
<v-card-text
v-if="cohosts.length > 0 || searchResult"
class="py-0"
>
<template v-if="cohosts.length > 0">
<v-list lines="two" variant="tonal">
<v-list-item
@ -358,15 +359,15 @@ export default {
<v-text-field
type="datetime-local"
v-model="scheduledAt"
label="開始予約"
:label="$t('form.schedule')"
disabled
:messages="['今後のアップデートで追加予定']"
:messages="[$t('comingFuture')]"
></v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn block color="indigo" @click="onSubmit" variant="flat">
作成
{{ $t("create") }}
</v-btn>
</v-card-actions>
</v-card>

Wyświetl plik

@ -15,7 +15,7 @@ export default {
},
methods: {
async onLogout() {
if (!confirm("Audon からログアウトしますか?")) return;
// if (!confirm(this.$t("logoutConfirm"))) return;
try {
const resp = await axios.post("/app/logout");
@ -42,7 +42,7 @@ export default {
color="red"
@click="onLogout"
>
ログアウト
{{ $t("logout") }}
</v-btn>
</div>
<div class="text-center my-10">
@ -65,7 +65,7 @@ export default {
<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-btn block :to="{ name: 'create' }" color="indigo">{{ $t("createNewRoom") }}</v-btn>
</v-col>
</v-row>
</main>

Wyświetl plik

@ -21,9 +21,9 @@ export default {
validations() {
return {
server: {
required: helpers.withMessage("アドレスを入力してください", required),
required: helpers.withMessage(this.$t("addressRequired"), required),
hostname: helpers.withMessage(
"有効なアドレスを入力してください",
this.$t("invalidAddress"),
validators.fqdn
),
},
@ -56,7 +56,7 @@ export default {
}
} catch (error) {
if (error.response?.status === 404) {
this.serverErr = "サーバーが見つかりません";
this.serverErr = this.$t("serverNotFound");
}
}
},
@ -69,14 +69,14 @@ export default {
</script>
<template>
<v-alert v-if="$route.query.warn" type="warning" variant="text">
<div>ログインが必要です</div>
<v-alert v-if="$route.query.l" type="warning" variant="text">
<div>{{ $t("loginRequired") }}</div>
</v-alert>
<v-form ref="form" @submit.prevent="onSubmit" class="my-3" lazy-validation>
<v-text-field
v-model="server"
name="server"
label="Mastodon サーバー"
:label="$t('server')"
placeholder="mastodon.example"
class="mb-2"
:error-messages="serverErrors"
@ -85,10 +85,10 @@ export default {
clearable
/>
<v-btn block @click="onSubmit" :disabled="!v$.$dirty || v$.$error"
>ログイン</v-btn
>{{ $t("login") }}</v-btn
>
</v-form>
<div class="w-100 text-right">
<RouterLink to="/about">利用規約</RouterLink>
<RouterLink to="/about">{{ $t("about") }}</RouterLink>
</div>
</template>

Wyświetl plik

@ -59,7 +59,7 @@ export default {
editingRoomInfo: {
title: {
required: helpers.withMessage(
"部屋の名前を入力してください",
this.$t("form.titleRequired"),
required
),
maxLength: maxLength(100),
@ -102,12 +102,12 @@ export default {
restriction: "",
},
relOptions: [
{ title: "制限なし", value: "everyone" },
{ title: "あなたのフォロー限定", value: "following" },
{ title: "あなたのフォロワー限定", value: "follower" },
{ title: "あなたのフォローまたはフォロワー限定", value: "knowing" },
{ title: "あなたの相互フォロー限定", value: "mutual" },
{ title: "共同ホスト限定", value: "private" },
{ title: this.$t("form.relationships.everyone"), value: "everyone" },
{ title: this.$t("form.relationships.following"), value: "following" },
{ title: this.$t("form.relationships.follower"), value: "follower" },
{ title: this.$t("form.relationships.knowing"), value: "knowing" },
{ title: this.$t("form.relationships.mutual"), value: "mutual" },
{ title: this.$t("form.relationships.private"), value: "private" },
],
participants: {},
cachedMastoData: {},
@ -245,10 +245,10 @@ export default {
let message = "";
switch (reason) {
case DisconnectReason.ROOM_DELETED:
message = "ホストにより部屋が閉じられました。";
message = self.$t("roomEvent.closedByHost");
break;
case DisconnectReason.PARTICIPANT_REMOVED:
message = "部屋から退去しました";
message = self.$t("roomEvent.removed");
break;
case DisconnectReason.CLIENT_INITIATED:
break;
@ -330,7 +330,7 @@ export default {
publishOpts
);
} catch {
alert("ブラウザが録音を許可していません");
alert(this.$t("microphoneBlocked"));
}
}
} catch (error) {
@ -339,23 +339,22 @@ export default {
let message = "";
switch (error.response?.data) {
case "following":
message = "この部屋はホストのフォロー限定です。";
message = this.$t("errors.restriction.following");
break;
case "follower":
message = "この部屋はホストのフォロワー限定です。";
message = this.$t("errors.restriction.follower");
break;
case "knowing":
message =
"この部屋はホストのフォローまたはフォロワー限定です。";
message = this.$t("errors.restriction.knowing");
break;
case "mutual":
message = "この部屋はホストの相互フォロー限定です。";
message = this.$t("errors.restriction.mutual");
break;
case "private":
message = "この部屋は共同ホスト限定です。";
message = this.$t("errors.restriction.private");
break;
default:
message = "入室が許可されていません。";
message = this.$t("errors.restriction.default");
}
alert(message);
break;
@ -363,12 +362,10 @@ export default {
pushNotFound(this.$route);
break;
case 406:
alert(
"他のデバイスで入室済みです。切断された場合はしばらく待ってからやり直してください。"
);
alert(this.$t("errors.alreadyConnected"));
break;
case 410:
alert("この部屋はすでに閉じられています。");
alert(this.$t("errors.alreadyClosed"));
break;
default:
alert(error);
@ -381,7 +378,7 @@ export default {
onResize() {
const mainArea = document.getElementById("mainArea");
const height = mainArea.clientHeight;
this.mainHeight = height > 700 ? 700 : window.innerHeight - 70;
this.mainHeight = height > 700 ? 700 : window.innerHeight - 95;
},
isHost(identity) {
return identity === this.roomInfo.host?.audon_id;
@ -427,7 +424,7 @@ export default {
await this.publishDataToHostAndCohosts(data);
},
async requestSpeak() {
if (confirm("発言をリクエストしますか?")) {
if (confirm(this.$t("speakRequest.dialog"))) {
await this.publishDataToHostAndCohosts({ kind: "speak_request" });
this.showRequestedNotification = true;
}
@ -499,16 +496,15 @@ export default {
);
}
} catch {
alert("ブラウザが録音を許可していません");
alert(this.$t("microphoneBlocked"));
}
} else {
// alert("");
this.requestSpeak();
}
},
async onRoomClose() {
// TODO: change this from confirm to a vuetify thing
if (confirm("この部屋を閉じますか?")) {
if (confirm(this.$t("closeRoomConfirm"))) {
try {
await axios.delete(`/api/room/${this.roomID}`);
} catch (error) {
@ -521,7 +517,7 @@ export default {
await this.roomClient.startAudio();
this.autoplayDisabled = false;
} catch {
alert("接続できませんでした。退室します。");
alert(this.$t("errors.connectionFailed"));
await this.roomClient.disconnect();
}
},
@ -557,11 +553,11 @@ export default {
<template>
<v-dialog v-model="showEditDialog" max-width="500" persistent>
<v-card>
<v-card-title>部屋の編集</v-card-title>
<v-card-title>{{ $t("editRoom") }}</v-card-title>
<v-card-text>
<v-text-field
v-model="editingRoomInfo.title"
label="タイトル"
:label="$t('form.title')"
:error-messages="titleErrors"
:counter="100"
required
@ -572,14 +568,14 @@ export default {
auto-grow
v-model="editingRoomInfo.description"
rows="2"
label="説明"
:label="$t('form.description')"
:counter="500"
></v-textarea>
<v-select
:items="relOptions"
label="入室制限"
:label="$t('form.restriction')"
v-model="editingRoomInfo.restriction"
:messages="['共同ホストは制限に関わらず入室できます']"
:messages="[$t('form.cohostCanAlwaysJoin')]"
></v-select>
</v-card-text>
<v-divider></v-divider>
@ -589,28 +585,32 @@ export default {
showEditDialog = false;
editingRoomInfo = clone(roomInfo);
"
>キャンセル</v-btn
>{{ $t("cancel") }}</v-btn
>
<v-btn @click="onEditSubmit"></v-btn>
<v-btn @click="onEditSubmit">{{ $t("save") }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="autoplayDisabled" max-width="500" persistent>
<v-alert color="indigo">
<div class="mb-5">
ブラウザの設定により無音になっています続行するには視聴を始めるボタンを押してください
{{ $t("browserMuted") }}
</div>
<div class="text-center mb-3">
<v-btn color="gray" @click="onStartListening"></v-btn>
<v-btn color="gray" @click="onStartListening">{{
$t("startListening")
}}</v-btn>
</div>
<div class="text-center">
<v-btn variant="text" @click="roomClient.disconnect()">退</v-btn>
<v-btn variant="text" @click="roomClient.disconnect()">{{
$t("leaveRoom")
}}</v-btn>
</div>
</v-alert>
</v-dialog>
<v-dialog v-model="showRequestDialog" max-width="500">
<v-card max-height="600" class="d-flex flex-column">
<v-card-title>発言リクエスト</v-card-title>
<v-card-title>{{ $t("speakRequest.label") }}</v-card-title>
<v-card-text class="flex-grow-1 overflow-auto py-0">
<v-list v-if="speakRequests.size > 0" lines="two" variant="tonal">
<v-list-item
@ -651,11 +651,13 @@ export default {
</v-list-item-subtitle>
</v-list-item>
</v-list>
<p class="text-center py-3" v-else></p>
<p class="text-center py-3" v-else>
{{ $t("speakRequest.norequest") }}
</p>
</v-card-text>
<v-divider></v-divider>
<v-card-actions class="justify-end">
<v-btn @click="showRequestDialog = false">閉じる</v-btn>
<v-btn @click="showRequestDialog = false">{{ $t("close") }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@ -665,7 +667,7 @@ export default {
v-model="showRequestedNotification"
color="info"
>
<strong>発言リクエストを送信しました</strong>
<strong>{{ $t("speakRequest.sent") }}</strong>
<template v-slot:actions>
<v-btn
variant="text"
@ -688,7 +690,7 @@ export default {
showRequestNotification = false;
"
>
<strong>新しい発言リクエストがあります</strong>
<strong>{{ $t("speakRequest.receive") }}</strong>
</div>
<template v-slot:actions>
<v-btn
@ -717,12 +719,12 @@ export default {
</template>
<v-list>
<v-list-item
title="編集"
:title="$t('edit')"
:prepend-icon="mdiPencil"
@click="showEditDialog = true"
></v-list-item>
<v-list-item
title="閉室"
:title="$t('closeRoom')"
:prepend-icon="mdiDoorClosed"
@click="onRoomClose"
></v-list-item>

Wyświetl plik

@ -4,9 +4,17 @@ import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vuetify from "vite-plugin-vuetify";
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vuetify({ autoImport: true })],
plugins: [
vue(),
vuetify({ autoImport: true }),
VueI18nPlugin({
include: "./src/locales/*.yaml",
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),

Wyświetl plik

@ -44,7 +44,7 @@ services:
audon:
build: .
image: namekuji/audon
image: nmkj/audon
env_file:
- .env.production
restart: unless-stopped

Wyświetl plik

@ -194,7 +194,7 @@ func joinRoomHandler(c echo.Context) (err error) {
// check room restriction
if room.IsPrivate() && !canTalk {
return ErrOperationNotPermitted
return c.String(http.StatusForbidden, string(room.Restriction))
}
if !canTalk && (room.IsFollowingOnly() || room.IsFollowerOnly() || room.IsFollowingOrFollowerOnly() || room.IsMutualOnly()) {
mastoClient, _ := getMastodonClient(c)