summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/app/boot.js22
-rw-r--r--src/client/app/common/views/components/games/reversi/reversi.game.vue19
-rw-r--r--src/client/app/common/views/components/nav.vue19
-rw-r--r--src/client/app/common/views/components/signup.vue74
-rw-r--r--src/client/app/common/views/components/url-preview.vue20
-rw-r--r--src/client/app/config.ts2
-rw-r--r--src/client/app/desktop/views/components/settings.vue24
-rw-r--r--src/client/app/desktop/views/components/ui.header.account.vue11
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.dashboard.vue14
-rw-r--r--src/client/app/mobile/views/components/follow-button.vue2
-rw-r--r--src/client/app/mobile/views/components/index.ts2
-rw-r--r--src/client/app/mobile/views/components/mute-button.vue79
-rw-r--r--src/client/app/mobile/views/components/ui.nav.vue5
-rw-r--r--src/client/app/mobile/views/pages/settings.vue12
-rw-r--r--src/client/app/mobile/views/pages/user.vue4
-rw-r--r--src/client/app/store.ts10
16 files changed, 232 insertions, 87 deletions
diff --git a/src/client/app/boot.js b/src/client/app/boot.js
index 08c3fdeaee..86d48faf1a 100644
--- a/src/client/app/boot.js
+++ b/src/client/app/boot.js
@@ -32,16 +32,24 @@
//#region Detect app name
let app = null;
- if (url.pathname == '/docs' || url.pathname.startsWith('/docs/')) app = 'docs';
- if (url.pathname == '/dev' || url.pathname.startsWith('/dev/')) app = 'dev';
- if (url.pathname == '/auth' || url.pathname.startsWith('/auth/')) app = 'auth';
+ if (`${url.pathname}/`.startsWith('/docs/')) app = 'docs';
+ if (`${url.pathname}/`.startsWith('/dev/')) app = 'dev';
+ if (`${url.pathname}/`.startsWith('/auth/')) app = 'auth';
//#endregion
//#region Detect the user language
- let lang = navigator.language.split('-')[0];
+ let lang = null;
- // The default language is English
- if (!LANGS.includes(lang)) lang = 'en';
+ if (LANGS.includes(navigator.language)) {
+ lang = navigator.language;
+ } else {
+ lang = LANGS.find(x => x.split('-')[0] == navigator.language);
+
+ if (lang == null) {
+ // Fallback
+ lang = 'en-US';
+ }
+ }
if (settings) {
if (settings.device.lang) lang = settings.device.lang;
@@ -104,7 +112,7 @@
// グローバルにタイマーIDを代入しておく
window.mkBootTimer = window.setTimeout(async () => {
// Fetch meta
- const res = await fetch(API + '/meta', {
+ const res = await fetch('/api/meta', {
method: 'POST',
cache: 'no-cache'
});
diff --git a/src/client/app/common/views/components/games/reversi/reversi.game.vue b/src/client/app/common/views/components/games/reversi/reversi.game.vue
index 389934af97..b432a2308d 100644
--- a/src/client/app/common/views/components/games/reversi/reversi.game.vue
+++ b/src/client/app/common/views/components/games/reversi/reversi.game.vue
@@ -18,11 +18,11 @@
</div>
<div class="board">
- <div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels">
+ <div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
</div>
<div class="flex">
- <div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels">
+ <div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<div v-for="i in game.settings.map.length">{{ i }}</div>
</div>
<div class="cells" :style="cellsStyle">
@@ -30,15 +30,15 @@
:class="{ empty: stone == null, none: o.map[i] == 'null', isEnded: game.isEnded, myTurn: !game.isEnded && isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id, i) : null, prev: o.prevPos == i }"
@click="set(i)"
:title="`${String.fromCharCode(65 + o.transformPosToXy(i)[0])}${o.transformPosToXy(i)[1] + 1}`">
- <img v-if="stone === true" :src="blackUser.avatarUrl" alt="">
- <img v-if="stone === false" :src="whiteUser.avatarUrl" alt="">
+ <img v-if="stone === true" :src="blackUser.avatarUrl" alt="black" :class="{ contrast: $store.state.settings.games.reversi.useContrastStones }">
+ <img v-if="stone === false" :src="whiteUser.avatarUrl" alt="white" :class="{ contrast: $store.state.settings.games.reversi.useContrastStones }">
</div>
</div>
- <div class="labels-y" v-if="this.$store.state.settings.reversiBoardLabels">
+ <div class="labels-y" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<div v-for="i in game.settings.map.length">{{ i }}</div>
</div>
</div>
- <div class="labels-x" v-if="this.$store.state.settings.reversiBoardLabels">
+ <div class="labels-x" v-if="this.$store.state.settings.games.reversi.showBoardLabels">
<span v-for="i in game.settings.map[0].length">{{ String.fromCharCode(64 + i) }}</span>
</div>
</div>
@@ -421,6 +421,13 @@ root(isDark)
width 100%
height 100%
+ &.contrast
+ &[alt="black"]
+ filter brightness(.5)
+
+ &[alt="white"]
+ filter brightness(2)
+
> .graph
display grid
grid-template-columns repeat(61, 1fr)
diff --git a/src/client/app/common/views/components/nav.vue b/src/client/app/common/views/components/nav.vue
index e25dbc78ca..1f745bf69d 100644
--- a/src/client/app/common/views/components/nav.vue
+++ b/src/client/app/common/views/components/nav.vue
@@ -6,7 +6,7 @@
<i>・</i>
<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a>
<i>・</i>
- <a :href="devUrl">%i18n:@develop%</a>
+ <a href="/dev">%i18n:@develop%</a>
<i>・</i>
<a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on %fa:B twitter%</a>
</span>
@@ -14,18 +14,21 @@
<script lang="ts">
import Vue from 'vue';
-import { docsUrl, statsUrl, statusUrl, devUrl, repositoryUrl, feedbackUrl, lang } from '../../../config';
+import { lang } from '../../../config';
export default Vue.extend({
data() {
return {
- aboutUrl: `${docsUrl}/${lang}/about`,
- statsUrl,
- statusUrl,
- devUrl,
- repositoryUrl: repositoryUrl || `https://github.com/syuilo/misskey`,
- feedbackUrl: feedbackUrl || `https://github.com/syuilo/misskey/issues/new`
+ aboutUrl: `/docs/${lang}/about`,
+ repositoryUrl: 'https://github.com/syuilo/misskey',
+ feedbackUrl: 'https://github.com/syuilo/misskey/issues/new'
}
+ },
+ created() {
+ (this as any).os.getMeta().then(meta => {
+ if (meta.repositoryUrl) this.repositoryUrl = meta.repositoryUrl;
+ if (meta.feedbackUrl) this.feedbackUrl = meta.feedbackUrl;
+ });
}
});
</script>
diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue
index 810b850831..f603b9545c 100644
--- a/src/client/app/common/views/components/signup.vue
+++ b/src/client/app/common/views/components/signup.vue
@@ -1,41 +1,43 @@
<template>
<form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
- <ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
- <span>%i18n:@invitation-code%</span>
- <span slot="prefix">%fa:id-card-alt%</span>
- <p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p>
- </ui-input>
- <ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername">
- <span>%i18n:@username%</span>
- <span slot="prefix">@</span>
- <span slot="suffix">@{{ host }}</span>
- <p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
- <p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
- <p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
- <p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
- <p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
- <p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
- <p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
- </ui-input>
- <ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true">
- <span>%i18n:@password%</span>
- <span slot="prefix">%fa:lock%</span>
- <div slot="text">
- <p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
- <p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
- <p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
- </div>
- </ui-input>
- <ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype">
- <span>%i18n:@password% (%i18n:@retype%)</span>
- <span slot="prefix">%fa:lock%</span>
- <div slot="text">
- <p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
- <p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
- </div>
- </ui-input>
- <div v-if="meta && meta.recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSitekey" style="margin: 16px 0;"></div>
- <ui-button type="submit">%i18n:@create%</ui-button>
+ <template v-if="meta">
+ <ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
+ <span>%i18n:@invitation-code%</span>
+ <span slot="prefix">%fa:id-card-alt%</span>
+ <p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p>
+ </ui-input>
+ <ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername">
+ <span>%i18n:@username%</span>
+ <span slot="prefix">@</span>
+ <span slot="suffix">@{{ host }}</span>
+ <p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
+ <p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
+ <p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
+ <p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
+ <p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
+ <p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
+ <p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
+ </ui-input>
+ <ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true">
+ <span>%i18n:@password%</span>
+ <span slot="prefix">%fa:lock%</span>
+ <div slot="text">
+ <p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
+ <p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
+ <p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
+ </div>
+ </ui-input>
+ <ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype">
+ <span>%i18n:@password% (%i18n:@retype%)</span>
+ <span slot="prefix">%fa:lock%</span>
+ <div slot="text">
+ <p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
+ <p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
+ </div>
+ </ui-input>
+ <div v-if="meta.recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSitekey" style="margin: 16px 0;"></div>
+ <ui-button type="submit">%i18n:@create%</ui-button>
+ </template>
</form>
</template>
diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 95dafa8f4c..be69012737 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -1,5 +1,7 @@
<template>
-<iframe v-if="player" :src="player" heigth="250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
+<div v-if="player.url" class="player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`">
+ <iframe :src="player.url" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
+</div>
<div v-else-if="tweetUrl && detail" class="twitter">
<blockquote ref="tweet" class="twitter-tweet" :data-theme="$store.state.device.darkmode ? 'dark' : null">
<a :href="url"></a>
@@ -46,7 +48,11 @@ export default Vue.extend({
thumbnail: null,
icon: null,
sitename: null,
- player: null,
+ player: {
+ url: null,
+ width: null,
+ height: null
+ },
tweetUrl: null,
misskeyUrl
};
@@ -170,9 +176,17 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
-iframe
+.twitter
+ position relative
width 100%
+ > iframe
+ height 100%
+ left 0
+ position absolute
+ top 0
+ width 100%
+
root(isDark)
> a
display block
diff --git a/src/client/app/config.ts b/src/client/app/config.ts
index 76cd536a49..74b9ea21c8 100644
--- a/src/client/app/config.ts
+++ b/src/client/app/config.ts
@@ -4,7 +4,6 @@ declare const _THEME_COLOR_: string;
declare const _COPYRIGHT_: string;
declare const _VERSION_: string;
declare const _CODENAME_: string;
-declare const _LICENSE_: string;
const address = new URL(location.href);
@@ -19,4 +18,3 @@ export const themeColor = _THEME_COLOR_;
export const copyright = _COPYRIGHT_;
export const version = _VERSION_;
export const codename = _CODENAME_;
-export const license = _LICENSE_;
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index e249afed85..df131a1a65 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -56,8 +56,9 @@
<mk-switch v-model="$store.state.settings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%">
<span>%i18n:@show-maps-desc%</span>
</mk-switch>
- <mk-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
<mk-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm" text="%i18n:common.disable-animated-mfm%"/>
+ <mk-switch v-model="$store.state.settings.games.reversi.showBoardLabels" @change="onChangeReversiBoardLabels" text="%i18n:common.show-reversi-board-labels%"/>
+ <mk-switch v-model="$store.state.settings.games.reversi.useContrastStones" @change="onChangeUseContrastReversiStones" text="%i18n:common.use-contrast-reversi-stones%"/>
</section>
<section class="web" v-show="page == 'web'">
@@ -191,12 +192,6 @@
<button class="ui button block" @click="taskmngr">%i18n:@task-manager%</button>
</details>
</section>
-
- <section class="other" v-show="page == 'other'">
- <h1>%i18n:@license%</h1>
- <div v-html="license"></div>
- <a :href="licenseUrl" target="_blank">%i18n:@third-parties%</a>
- </section>
</div>
</div>
</template>
@@ -211,7 +206,7 @@ import XApi from './settings.api.vue';
import XApps from './settings.apps.vue';
import XSignins from './settings.signins.vue';
import XDrive from './settings.drive.vue';
-import { url, docsUrl, license, lang, langs, version } from '../../../config';
+import { url, langs, version } from '../../../config';
import checkForUpdate from '../../../common/scripts/check-for-update';
import MkTaskManager from './taskmanager.vue';
@@ -230,7 +225,6 @@ export default Vue.extend({
return {
page: 'profile',
meta: null,
- license,
version,
langs,
latestVersion: undefined,
@@ -238,10 +232,6 @@ export default Vue.extend({
};
},
computed: {
- licenseUrl(): string {
- return `${docsUrl}/${lang}/license`;
- },
-
apiViaStream: {
get() { return this.$store.state.device.apiViaStream; },
set(value) { this.$store.commit('device/set', { key: 'apiViaStream', value }); }
@@ -387,7 +377,13 @@ export default Vue.extend({
},
onChangeReversiBoardLabels(v) {
this.$store.dispatch('settings/set', {
- key: 'reversiBoardLabels',
+ key: 'games.reversi.showBoardLabels',
+ value: v
+ });
+ },
+ onChangeUseContrastReversiStones(v) {
+ this.$store.dispatch('settings/set', {
+ key: 'games.reversi.useContrastStones',
value: v
});
},
diff --git a/src/client/app/desktop/views/components/ui.header.account.vue b/src/client/app/desktop/views/components/ui.header.account.vue
index 4e0fc1cf1a..5e26389d89 100644
--- a/src/client/app/desktop/views/components/ui.header.account.vue
+++ b/src/client/app/desktop/views/components/ui.header.account.vue
@@ -30,10 +30,8 @@
<li @click="settings">
<p>%fa:cog%<span>%i18n:@settings%</span>%fa:angle-right%</p>
</li>
- </ul>
- <ul>
- <li @click="signout">
- <p class="signout">%fa:power-off%<span>%i18n:@signout%</span></p>
+ <li v-if="$store.state.i.isAdmin">
+ <router-link to="/admin">%fa:terminal%<span>%i18n:@admin%</span>%fa:angle-right%</router-link>
</li>
</ul>
<ul>
@@ -41,6 +39,11 @@
<p><span>%i18n:@dark%</span><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template></p>
</li>
</ul>
+ <ul>
+ <li @click="signout">
+ <p class="signout">%fa:power-off%<span>%i18n:@signout%</span></p>
+ </li>
+ </ul>
</div>
</transition>
</div>
diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
index 3567585cb8..e0d23331b8 100644
--- a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
@@ -11,6 +11,10 @@
<x-cpu-memory :connection="connection"/>
</div>
<div>
+ <label>
+ <input type="checkbox" v-model="disableRegistration" @change="updateMeta">
+ <span>disableRegistration</span>
+ </label>
<button class="ui" @click="invite">%i18n:@invite%</button>
<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
</div>
@@ -28,6 +32,7 @@ export default Vue.extend({
data() {
return {
stats: null,
+ disableRegistration: false,
inviteCode: null,
connection: null,
connectionId: null
@@ -37,6 +42,10 @@ export default Vue.extend({
this.connection = (this as any).os.streams.serverStatsStream.getConnection();
this.connectionId = (this as any).os.streams.serverStatsStream.use();
+ (this as any).os.getMeta().then(meta => {
+ this.disableRegistration = meta.disableRegistration;
+ });
+
(this as any).api('stats').then(stats => {
this.stats = stats;
});
@@ -49,6 +58,11 @@ export default Vue.extend({
(this as any).api('admin/invite').then(x => {
this.inviteCode = x.code;
});
+ },
+ updateMeta() {
+ (this as any).api('admin/update-meta', {
+ disableRegistration: this.disableRegistration
+ });
}
}
});
diff --git a/src/client/app/mobile/views/components/follow-button.vue b/src/client/app/mobile/views/components/follow-button.vue
index b6a52fe1ed..360ee91d4b 100644
--- a/src/client/app/mobile/views/components/follow-button.vue
+++ b/src/client/app/mobile/views/components/follow-button.vue
@@ -99,7 +99,7 @@ export default Vue.extend({
cursor pointer
padding 0 16px
margin 0
- min-width 150px
+ min-width 100px
line-height 36px
font-size 14px
font-weight bold
diff --git a/src/client/app/mobile/views/components/index.ts b/src/client/app/mobile/views/components/index.ts
index 38c130ecbf..3e830f4e96 100644
--- a/src/client/app/mobile/views/components/index.ts
+++ b/src/client/app/mobile/views/components/index.ts
@@ -12,6 +12,7 @@ import noteCard from './note-card.vue';
import userCard from './user-card.vue';
import noteDetail from './note-detail.vue';
import followButton from './follow-button.vue';
+import muteButton from './mute-button.vue';
import friendsMaker from './friends-maker.vue';
import notification from './notification.vue';
import notifications from './notifications.vue';
@@ -36,6 +37,7 @@ Vue.component('mk-note-card', noteCard);
Vue.component('mk-user-card', userCard);
Vue.component('mk-note-detail', noteDetail);
Vue.component('mk-follow-button', followButton);
+Vue.component('mk-mute-button', muteButton);
Vue.component('mk-friends-maker', friendsMaker);
Vue.component('mk-notification', notification);
Vue.component('mk-notifications', notifications);
diff --git a/src/client/app/mobile/views/components/mute-button.vue b/src/client/app/mobile/views/components/mute-button.vue
new file mode 100644
index 0000000000..3cb568615d
--- /dev/null
+++ b/src/client/app/mobile/views/components/mute-button.vue
@@ -0,0 +1,79 @@
+<template>
+<button
+ class="mk-mute-button"
+ :class="{ active: user.isMuted }"
+ @click="onClick">
+ <span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
+ <span v-else>%fa:eye% %i18n:@unmute%</span>
+</button>
+</template>
+
+<script lang="ts">
+import Vue from 'vue'
+export default Vue.extend({
+ props: {
+ user: {
+ type: Object,
+ required: true
+ }
+ },
+ methods: {
+ onClick() {
+ if (!this.user.isMuted) {
+ this.mute();
+ } else {
+ this.unmute();
+ }
+ },
+ mute() {
+ (this as any).api('mute/create', { userId: this.user.id})
+ .then(() => { this.user.isMuted = true })
+ .catch(() => { alert('error')})
+ },
+ unmute() {
+ (this as any).api('mute/delete', { userId: this.user.id })
+ .then(() => { this.user.isMuted = false })
+ .catch(() => { alert('error') })
+ }
+ },
+})
+</script>
+
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.mk-mute-button
+ display block
+ user-select none
+ cursor pointer
+ padding 0 16px
+ margin 0
+ min-width 100px
+ line-height 36px
+ font-size 14px
+ font-weight bold
+ color $theme-color
+ background transparent
+ outline none
+ border solid 1px $theme-color
+ border-radius 36px
+
+ &:hover
+ background rgba($theme-color, 0.1)
+
+ &:active
+ background rgba($theme-color, 0.2)
+
+ &.active
+ color $theme-color-foreground
+ background $theme-color
+
+ &:hover
+ background lighten($theme-color, 10%)
+ border-color lighten($theme-color, 10%)
+ &:active
+ background darken($theme-color, 10%)
+ border-color darken($theme-color, 10%)
+
+</style>
diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue
index 5257dafd0e..39ea513b76 100644
--- a/src/client/app/mobile/views/components/ui.nav.vue
+++ b/src/client/app/mobile/views/components/ui.nav.vue
@@ -30,6 +30,7 @@
<ul>
<li><a @click="search">%fa:search%%i18n:@search%%fa:angle-right%</a></li>
<li><router-link to="/i/settings" :data-active="$route.name == 'settings'">%fa:cog%%i18n:@settings%%fa:angle-right%</router-link></li>
+ <li v-if="$store.getters.isSignedIn && $store.state.i.isAdmin"><router-link to="/admin">%fa:terminal%<span>%i18n:@admin%</span>%fa:angle-right%</router-link></li>
<li @click="dark"><p><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template><span>%i18n:@darkmode%</span></p></li>
</ul>
</div>
@@ -41,7 +42,7 @@
<script lang="ts">
import Vue from 'vue';
-import { docsUrl, lang } from '../../../config';
+import { lang } from '../../../config';
export default Vue.extend({
props: ['isOpen'],
@@ -50,7 +51,7 @@ export default Vue.extend({
hasGameInvitation: false,
connection: null,
connectionId: null,
- aboutUrl: `${docsUrl}/${lang}/about`
+ aboutUrl: `/docs/${lang}/about`
};
},
computed: {
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 7636a0370a..6b82be099d 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -13,8 +13,9 @@
<ui-switch v-model="darkmode">%i18n:@dark-mode%</ui-switch>
<ui-switch v-model="$store.state.settings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</ui-switch>
<ui-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi">%i18n:common.i-like-sushi%</ui-switch>
- <ui-switch v-model="$store.state.settings.reversiBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
<ui-switch v-model="$store.state.settings.disableAnimatedMfm" @change="onChangeDisableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
+ <ui-switch v-model="$store.state.settings.games.reversi.showBoardLabels" @change="onChangeReversiBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
+ <ui-switch v-model="$store.state.settings.games.reversi.useContrastStones" @change="onChangeUseContrastReversiStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
<div>
<div>%i18n:@timeline%</div>
@@ -189,7 +190,14 @@ export default Vue.extend({
onChangeReversiBoardLabels(v) {
this.$store.dispatch('settings/set', {
- key: 'reversiBoardLabels',
+ key: 'games.reversi.showBoardLabels',
+ value: v
+ });
+ },
+
+ onChangeUseContrastReversiStones(v) {
+ this.$store.dispatch('settings/set', {
+ key: 'games.reversi.useContrastStones',
value: v
});
},
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index d573538fdd..e72867352f 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -11,6 +11,7 @@
<a class="avatar">
<img :src="user.avatarUrl" alt="avatar"/>
</a>
+ <mk-mute-button v-if="$store.state.i.id != user.id" :user="user"/>
<mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
</div>
<div class="title">
@@ -184,6 +185,9 @@ root(isDark)
border 4px solid $bg
border-radius 12px
+ > .mk-mute-button
+ float right
+
> .mk-follow-button
float right
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index 7e2cc3976b..ba91a11f25 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -1,5 +1,6 @@
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';
+import * as nestedProperty from 'nested-property';
import MiOS from './mios';
import { hostname } from './config';
@@ -22,7 +23,12 @@ const defaultSettings = {
disableViaMobile: false,
memo: null,
iLikeSushi: false,
- reversiBoardLabels: false
+ games: {
+ reversi: {
+ showBoardLabels: false,
+ useContrastStones: false
+ }
+ }
};
const defaultDeviceSettings = {
@@ -125,7 +131,7 @@ export default (os: MiOS) => new Vuex.Store({
mutations: {
set(state, x: { key: string; value: any }) {
- state[x.key] = x.value;
+ nestedProperty.set(state, x.key, x.value);
},
setHome(state, data) {