diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-23 18:25:44 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-23 18:25:44 +0900 |
| commit | 37dc1c9a82c72ca1d28ab05a55269eac650133db (patch) | |
| tree | 573d6d6f6f4ffd5b63ef137543ab685c40c31508 /src/client/pages | |
| parent | Merge branch 'develop' (diff) | |
| parent | Update package.json (diff) | |
| download | misskey-37dc1c9a82c72ca1d28ab05a55269eac650133db.tar.gz misskey-37dc1c9a82c72ca1d28ab05a55269eac650133db.tar.bz2 misskey-37dc1c9a82c72ca1d28ab05a55269eac650133db.zip | |
Merge branch 'develop'
Diffstat (limited to 'src/client/pages')
146 files changed, 2897 insertions, 2134 deletions
diff --git a/src/client/pages/_error_.vue b/src/client/pages/_error_.vue index 67c1a1991c..6caecd6eaf 100644 --- a/src/client/pages/_error_.vue +++ b/src/client/pages/_error_.vue @@ -3,7 +3,7 @@ <div class="_section"> <div class="mjndxjch _content"> <img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> - <p><Fa :icon="faExclamationTriangle"/> {{ $ts.pageLoadError }}</p> + <p><i class="fas fa-exclamation-triangle"></i> {{ $ts.pageLoadError }}</p> <p>{{ $ts.pageLoadErrorDescription }}</p> </div> </div> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import * as symbols from '@client/symbols'; @@ -24,9 +23,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.error, - icon: faExclamationTriangle + icon: 'fas fa-exclamation-triangle' }, - faExclamationTriangle }; }, }); diff --git a/src/client/pages/about-misskey.vue b/src/client/pages/about-misskey.vue index aae8a7052a..1d57c80810 100644 --- a/src/client/pages/about-misskey.vue +++ b/src/client/pages/about-misskey.vue @@ -15,17 +15,17 @@ </section> <FormGroup> <FormLink to="https://github.com/misskey-dev/misskey" external> - <template #icon><Fa :icon="faCode"/></template> + <template #icon><i class="fas fa-code"></i></template> {{ $ts._aboutMisskey.source }} <template #suffix>GitHub</template> </FormLink> <FormLink to="https://crowdin.com/project/misskey" external> - <template #icon><Fa :icon="faLanguage"/></template> + <template #icon><i class="fas fa-language"></i></template> {{ $ts._aboutMisskey.translation }} <template #suffix>Crowdin</template> </FormLink> <FormLink to="https://www.patreon.com/syuilo" external> - <template #icon><Fa :icon="faHandHoldingMedical"/></template> + <template #icon><i class="fas fa-hand-holding-medical"></i></template> {{ $ts._aboutMisskey.donate }} <template #suffix>Patreon</template> </FormLink> @@ -54,7 +54,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faInfoCircle, faCode, faLanguage, faHandHoldingMedical, } from '@fortawesome/free-solid-svg-icons'; import VanillaTilt from 'vanilla-tilt'; import { version } from '@client/config'; import FormLink from '@client/components/form/link.vue'; @@ -125,7 +124,6 @@ export default defineComponent({ easterEggReady: false, easterEggEmojis: [], easterEggEngine: null, - faInfoCircle, faCode, faLanguage, faHandHoldingMedical, } }, diff --git a/src/client/pages/about.vue b/src/client/pages/about.vue index 4084256cf4..4f70998eee 100644 --- a/src/client/pages/about.vue +++ b/src/client/pages/about.vue @@ -40,7 +40,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import { version, instanceName } from '@client/config'; import FormLink from '@client/components/form/link.vue'; import FormBase from '@client/components/form/base.vue'; @@ -62,12 +61,11 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.instanceInfo, - icon: faInfoCircle + icon: 'fas fa-info-circle' }, version, instanceName, stats: null, - faInfoCircle } }, diff --git a/src/client/pages/advanced-theme-editor.vue b/src/client/pages/advanced-theme-editor.vue index fff525a32d..b40d9808ca 100644 --- a/src/client/pages/advanced-theme-editor.vue +++ b/src/client/pages/advanced-theme-editor.vue @@ -34,7 +34,7 @@ </div> <div> <div class="type" @click="chooseType($event, i)"> - {{ getTypeOf(v) }} <Fa :icon="faChevronDown"/> + {{ getTypeOf(v) }} <i class="fas fa-chevron-down"></i> </div> <!-- default --> <div v-if="v === null" v-text="baseProps[k]" class="default-value" /> @@ -92,7 +92,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPalette, faChevronDown, faKeyboard } from '@fortawesome/free-solid-svg-icons'; import * as JSON5 from 'json5'; import { toUnicode } from 'punycode/'; @@ -125,7 +124,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.themeEditor, - icon: faPalette, + icon: 'fas fa-palette', }, theme: [] as ThemeViewModel, name: '', @@ -135,7 +134,6 @@ export default defineComponent({ themeToImport: '', changed: false, lightTheme, darkTheme, themeProps, - faPalette, faChevronDown, faKeyboard, } }, diff --git a/src/client/pages/announcements.vue b/src/client/pages/announcements.vue index 4e5f0e7f9c..a7ccb03588 100644 --- a/src/client/pages/announcements.vue +++ b/src/client/pages/announcements.vue @@ -8,7 +8,7 @@ <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> </div> <div class="_footer" v-if="$i && !announcement.isRead"> - <MkButton @click="read(items, announcement, i)" primary><Fa :icon="faCheck"/> {{ $ts.gotIt }}</MkButton> + <MkButton @click="read(items, announcement, i)" primary><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton> </div> </section> </MkPagination> @@ -17,7 +17,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCheck, faBroadcastTower } from '@fortawesome/free-solid-svg-icons'; import MkPagination from '@client/components/ui/pagination.vue'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; @@ -33,13 +32,12 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.announcements, - icon: faBroadcastTower + icon: 'fas fa-broadcast-tower' }, pagination: { endpoint: 'announcements', limit: 10, }, - faCheck, }; }, diff --git a/src/client/pages/api-console.vue b/src/client/pages/api-console.vue index 669e814778..b153d10396 100644 --- a/src/client/pages/api-console.vue +++ b/src/client/pages/api-console.vue @@ -12,7 +12,7 @@ </MkSwitch> <MkButton primary full @click="send" :disabled="sending"> <template v-if="sending"><MkEllipsis/></template> - <template v-else><Fa :icon="faPaperPlane"/> Send</template> + <template v-else><i class="fas fa-paper-plane"></i> Send</template> </MkButton> </div> <div v-if="res" class="_block" style="padding: 24px;"> @@ -25,7 +25,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faTerminal, faPaperPlane } from '@fortawesome/free-solid-svg-icons'; import * as JSON5 from 'json5'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -43,7 +42,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: 'API console', - icon: faTerminal + icon: 'fas fa-terminal' }, endpoint: '', @@ -53,7 +52,6 @@ export default defineComponent({ endpoints: [], withCredential: true, - faPaperPlane }; }, diff --git a/src/client/pages/channel-editor.vue b/src/client/pages/channel-editor.vue index 7216aaec4a..bc772d34fa 100644 --- a/src/client/pages/channel-editor.vue +++ b/src/client/pages/channel-editor.vue @@ -7,15 +7,15 @@ <MkTextarea v-model:value="description">{{ $ts.description }}</MkTextarea> <div class="banner"> - <MkButton v-if="bannerId == null" @click="setBannerImage"><Fa :icon="faPlus"/> {{ $ts._channel.setBanner }}</MkButton> + <MkButton v-if="bannerId == null" @click="setBannerImage"><i class="fas fa-plus"></i> {{ $ts._channel.setBanner }}</MkButton> <div v-else-if="bannerUrl"> <img :src="bannerUrl" style="width: 100%;"/> - <MkButton @click="removeBannerImage()"><Fa :icon="faTrashAlt"/> {{ $ts._channel.removeBanner }}</MkButton> + <MkButton @click="removeBannerImage()"><i class="fas fa-trash-alt"></i> {{ $ts._channel.removeBanner }}</MkButton> </div> </div> </div> <div class="_footer"> - <MkButton @click="save()" primary><Fa :icon="faSave"/> {{ channelId ? $ts.save : $ts.create }}</MkButton> + <MkButton @click="save()" primary><i class="fas fa-save"></i> {{ channelId ? $ts.save : $ts.create }}</MkButton> </div> </div> </div> @@ -23,8 +23,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faPlus, faSatelliteDish } from '@fortawesome/free-solid-svg-icons'; -import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import MkTextarea from '@client/components/ui/textarea.vue'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -48,17 +46,16 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: computed(() => this.channelId ? { title: this.$ts._channel.edit, - icon: faSatelliteDish, + icon: 'fas fa-satellite-dish', } : { title: this.$ts._channel.create, - icon: faSatelliteDish, + icon: 'fas fa-satellite-dish', }), channel: null, name: null, description: null, bannerUrl: null, bannerId: null, - faSave, faTrashAlt, faPlus,faSatelliteDish, }; }, diff --git a/src/client/pages/channel.vue b/src/client/pages/channel.vue index f98bb41a38..1504264af5 100644 --- a/src/client/pages/channel.vue +++ b/src/client/pages/channel.vue @@ -3,15 +3,15 @@ <div class="wpgynlbz _content _panel _gap" :class="{ hide: !showBanner }"> <XChannelFollowButton :channel="channel" :full="true" class="subscribe"/> <button class="_button toggle" @click="() => showBanner = !showBanner"> - <template v-if="showBanner"><Fa :icon="faAngleUp"/></template> - <template v-else><Fa :icon="faAngleDown"/></template> + <template v-if="showBanner"><i class="fas fa-angle-up"></i></template> + <template v-else><i class="fas fa-angle-down"></i></template> </button> <div class="hideOverlay" v-if="!showBanner"> </div> <div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" class="banner"> <div class="status"> - <div><Fa :icon="faUsers" fixed-width/><I18n :src="$ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div> - <div><Fa :icon="faPencilAlt" fixed-width/><I18n :src="$ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div> + <div><i class="fas fa-users fa-fw"></i><I18n :src="$ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div> + <div><i class="fas fa-pencil-alt fa-fw"></i><I18n :src="$ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div> </div> <div class="fade"></div> </div> @@ -28,8 +28,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faSatelliteDish, faUsers, faPencilAlt, faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons'; -import { } from '@fortawesome/free-regular-svg-icons'; import MkContainer from '@client/components/ui/container.vue'; import XPostForm from '@client/components/post-form.vue'; import XTimeline from '@client/components/timeline.vue'; @@ -56,7 +54,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: computed(() => this.channel ? { title: this.channel.name, - icon: faSatelliteDish, + icon: 'fas fa-satellite-dish', } : null), channel: null, showBanner: true, @@ -67,7 +65,6 @@ export default defineComponent({ channelId: this.channelId, }) }, - faSatelliteDish, faUsers, faPencilAlt, faAngleUp, faAngleDown, }; }, @@ -111,7 +108,7 @@ export default defineComponent({ background: rgba(0, 0, 0, 0.5); border-radius: 100%; - > [data-icon] { + > i { vertical-align: middle; } } diff --git a/src/client/pages/channels.vue b/src/client/pages/channels.vue index ebf1e7b871..7e3302959b 100644 --- a/src/client/pages/channels.vue +++ b/src/client/pages/channels.vue @@ -2,9 +2,9 @@ <div> <div class="_section" style="padding: 0;" v-if="$i"> <MkTab class="_content" v-model:value="tab"> - <option value="featured"><Fa :icon="faFireAlt"/> {{ $ts._channel.featured }}</option> - <option value="following"><Fa :icon="faHeart"/> {{ $ts._channel.following }}</option> - <option value="owned"><Fa :icon="faEdit"/> {{ $ts._channel.owned }}</option> + <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._channel.featured }}</option> + <option value="following"><i class="fas fa-heart"></i> {{ $ts._channel.following }}</option> + <option value="owned"><i class="fas fa-edit"></i> {{ $ts._channel.owned }}</option> </MkTab> </div> @@ -22,7 +22,7 @@ </div> <div class="_content grwlizim owned" v-if="tab === 'owned'"> - <MkButton class="new" @click="create()"><Fa :icon="faPlus"/></MkButton> + <MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton> <MkPagination :pagination="ownedPagination" #default="{items}"> <MkChannelPreview v-for="channel in items" class="_gap" :channel="channel" :key="channel.id"/> </MkPagination> @@ -33,8 +33,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faSatelliteDish, faPlus, faEdit, faFireAlt } from '@fortawesome/free-solid-svg-icons'; -import { faHeart } from '@fortawesome/free-regular-svg-icons'; import MkChannelPreview from '@client/components/channel-preview.vue'; import MkPagination from '@client/components/ui/pagination.vue'; import MkButton from '@client/components/ui/button.vue'; @@ -49,9 +47,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.channel, - icon: faSatelliteDish, + icon: 'fas fa-satellite-dish', action: { - icon: faPlus, + icon: 'fas fa-plus', handler: this.create } }, @@ -68,7 +66,6 @@ export default defineComponent({ endpoint: 'channels/owned', limit: 5, }, - faSatelliteDish, faPlus, faEdit, faHeart, faFireAlt }; }, methods: { diff --git a/src/client/pages/clip.vue b/src/client/pages/clip.vue index ca3e051d51..8777975557 100644 --- a/src/client/pages/clip.vue +++ b/src/client/pages/clip.vue @@ -15,7 +15,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faEllipsisH, faPaperclip, faPencilAlt, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import MkContainer from '@client/components/ui/container.vue'; import XPostForm from '@client/components/post-form.vue'; import XNotes from '@client/components/notes.vue'; @@ -40,9 +39,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: computed(() => this.clip ? { title: this.clip.name, - icon: faPaperclip, + icon: 'fas fa-paperclip', action: { - icon: faEllipsisH, + icon: 'fas fa-ellipsis-h', handler: this.menu } } : null), @@ -81,7 +80,7 @@ export default defineComponent({ methods: { menu(ev) { os.modalMenu([this.isOwned ? { - icon: faPencilAlt, + icon: 'fas fa-pencil-alt', text: this.$ts.edit, action: async () => { const { canceled, result } = await os.form(this.clip.name, { @@ -111,7 +110,7 @@ export default defineComponent({ }); } } : undefined, this.isOwned ? { - icon: faTrashAlt, + icon: 'fas fa-trash-alt', text: this.$ts.delete, danger: true, action: async () => { diff --git a/src/client/pages/doc.vue b/src/client/pages/doc.vue index cf3628dafb..a4cf25033e 100644 --- a/src/client/pages/doc.vue +++ b/src/client/pages/doc.vue @@ -10,7 +10,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons' import MarkdownIt from 'markdown-it'; import MarkdownItAnchor from 'markdown-it-anchor'; import { url, lang } from '@client/config'; @@ -41,7 +40,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: computed(() => this.title ? { title: this.title, - icon: faQuestionCircle, + icon: 'fas fa-question-circle', } : null), title: null, body: null, diff --git a/src/client/pages/docs.vue b/src/client/pages/docs.vue index 92eab86716..e51528f83d 100644 --- a/src/client/pages/docs.vue +++ b/src/client/pages/docs.vue @@ -14,7 +14,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons' import { url, lang } from '@client/config'; import * as symbols from '@client/symbols'; @@ -23,10 +22,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.help, - icon: faQuestionCircle + icon: 'fas fa-question-circle' }, docs: [], - faQuestionCircle } }, diff --git a/src/client/pages/drive.vue b/src/client/pages/drive.vue index 33bbfbc50f..753114f725 100644 --- a/src/client/pages/drive.vue +++ b/src/client/pages/drive.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faCloud } from '@fortawesome/free-solid-svg-icons'; import XDrive from '@client/components/drive.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -20,7 +19,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: computed(() => this.folder ? this.folder.name : this.$ts.drive), - icon: faCloud, + icon: 'fas fa-cloud', menu: () => this.$refs.drive.getMenu() }, folder: null, diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue index dc0803237b..7bcb09d8c5 100644 --- a/src/client/pages/explore.vue +++ b/src/client/pages/explore.vue @@ -2,7 +2,7 @@ <div class="lznhrdub _root"> <div> <div class="_isolated"> - <MkInput v-model:value="query" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $ts.searchUser }}</span></MkInput> + <MkInput v-model:value="query" :debounce="true" type="search"><template #icon><i class="fas fa-search"></i></template><span>{{ $ts.searchUser }}</span></MkInput> </div> <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> @@ -14,19 +14,19 @@ <template v-if="tag == null"> <MkFolder class="_gap" persist-key="explore-pinned-users"> - <template #header><Fa :icon="faBookmark" fixed-width style="margin-right: 0.5em;"/>{{ $ts.pinnedUsers }}</template> + <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template> <XUserList :pagination="pinnedUsers"/> </MkFolder> <MkFolder class="_gap" persist-key="explore-popular-users"> - <template #header><Fa :icon="faChartLine" fixed-width style="margin-right: 0.5em;"/>{{ $ts.popularUsers }}</template> + <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> <XUserList :pagination="popularUsers"/> </MkFolder> <MkFolder class="_gap" persist-key="explore-recently-updated-users"> - <template #header><Fa :icon="faCommentAlt" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyUpdatedUsers }}</template> + <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> <XUserList :pagination="recentlyUpdatedUsers"/> </MkFolder> <MkFolder class="_gap" persist-key="explore-recently-registered-users"> - <template #header><Fa :icon="faPlus" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyRegisteredUsers }}</template> + <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template> <XUserList :pagination="recentlyRegisteredUsers"/> </MkFolder> </template> @@ -37,7 +37,7 @@ </div> <MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap"> - <template #header><Fa :icon="faHashtag" fixed-width style="margin-right: 0.5em;"/>{{ $ts.popularTags }}</template> + <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template> <div class="vxjfqztj"> <MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA> @@ -46,21 +46,21 @@ </MkFolder> <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap"> - <template #header><Fa :icon="faHashtag" fixed-width style="margin-right: 0.5em;"/>{{ tag }}</template> + <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> <XUserList :pagination="tagUsers"/> </MkFolder> <template v-if="tag == null"> <MkFolder class="_gap"> - <template #header><Fa :icon="faChartLine" fixed-width style="margin-right: 0.5em;"/>{{ $ts.popularUsers }}</template> + <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> <XUserList :pagination="popularUsersF"/> </MkFolder> <MkFolder class="_gap"> - <template #header><Fa :icon="faCommentAlt" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyUpdatedUsers }}</template> + <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> <XUserList :pagination="recentlyUpdatedUsersF"/> </MkFolder> <MkFolder class="_gap"> - <template #header><Fa :icon="faRocket" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyDiscoveredUsers }}</template> + <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template> <XUserList :pagination="recentlyRegisteredUsersF"/> </MkFolder> </template> @@ -70,8 +70,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faChartLine, faPlus, faHashtag, faRocket, faSearch } from '@fortawesome/free-solid-svg-icons'; -import { faBookmark, faCommentAlt } from '@fortawesome/free-regular-svg-icons'; import XUserList from '@client/components/user-list.vue'; import MkFolder from '@client/components/ui/folder.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -97,7 +95,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.explore, - icon: faHashtag + icon: 'fas fa-hashtag' }, pinnedUsers: { endpoint: 'pinned-users' }, popularUsers: { endpoint: 'users', limit: 10, noPaging: true, params: { @@ -139,7 +137,6 @@ export default defineComponent({ stats: null, query: null, num: number, - faBookmark, faChartLine, faCommentAlt, faPlus, faHashtag, faRocket, faSearch, }; }, diff --git a/src/client/pages/favorites.vue b/src/client/pages/favorites.vue index 7ecd327137..408ab222b5 100644 --- a/src/client/pages/favorites.vue +++ b/src/client/pages/favorites.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faStar } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import XNotes from '@client/components/notes.vue'; import * as os from '@client/os'; @@ -21,7 +20,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.favorites, - icon: faStar + icon: 'fas fa-star' }, pagination: { endpoint: 'i/favorites', diff --git a/src/client/pages/featured.vue b/src/client/pages/featured.vue index cd7343f583..21818ba617 100644 --- a/src/client/pages/featured.vue +++ b/src/client/pages/featured.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faFireAlt } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import XNotes from '@client/components/notes.vue'; import * as symbols from '@client/symbols'; @@ -20,14 +19,13 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.featured, - icon: faFireAlt + icon: 'fas fa-fire-alt' }, pagination: { endpoint: 'notes/featured', limit: 10, offsetMode: true }, - faFireAlt }; }, diff --git a/src/client/pages/follow-requests.vue b/src/client/pages/follow-requests.vue index 31c00d63cd..9f27a6baa8 100644 --- a/src/client/pages/follow-requests.vue +++ b/src/client/pages/follow-requests.vue @@ -19,8 +19,8 @@ <Mfm :text="req.follower.description" :is-note="false" :author="req.follower" :i="$i" :custom-emojis="req.follower.emojis" :plain="true" :nowrap="true"/> </div> <div class="actions"> - <button class="_button" @click="accept(req.follower)"><Fa :icon="faCheck"/></button> - <button class="_button" @click="reject(req.follower)"><Fa :icon="faTimes"/></button> + <button class="_button" @click="accept(req.follower)"><i class="fas fa-check"></i></button> + <button class="_button" @click="reject(req.follower)"><i class="fas fa-times"></i></button> </div> </div> </div> @@ -31,7 +31,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faUserClock, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'; import MkPagination from '@client/components/ui/pagination.vue'; import { userPage, acct } from '../filters/user'; import * as os from '@client/os'; @@ -46,13 +45,12 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.followRequests, - icon: faUserClock, + icon: 'fas fa-user-clock', }, pagination: { endpoint: 'following/requests/list', limit: 10, }, - faCheck, faTimes, faUserClock }; }, diff --git a/src/client/pages/instance-info.vue b/src/client/pages/instance-info.vue index a3cd402993..662b82ddb1 100644 --- a/src/client/pages/instance-info.vue +++ b/src/client/pages/instance-info.vue @@ -99,9 +99,12 @@ <span>Raw</span> </FormObjectView> <FormGroup> + <template #label>Well-known resources</template> <FormLink :to="`https://${host}/.well-known/host-meta`" external>host-meta</FormLink> <FormLink :to="`https://${host}/.well-known/host-meta.json`" external>host-meta.json</FormLink> <FormLink :to="`https://${host}/.well-known/nodeinfo`" external>nodeinfo</FormLink> + <FormLink :to="`https://${host}/robots.txt`" external>robots.txt</FormLink> + <FormLink :to="`https://${host}/manifest.json`" external>manifest.json</FormLink> </FormGroup> <FormSuspense :p="dnsPromiseFactory" v-slot="{ result: dns }"> <FormGroup> @@ -130,7 +133,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faExternalLinkAlt, faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import Chart from 'chart.js'; import FormObjectView from '@client/components/form/object-view.vue'; import FormTextarea from '@client/components/form/textarea.vue'; @@ -182,10 +184,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.instanceInfo, - icon: faInfoCircle, + icon: 'fas fa-info-circle', actions: [{ text: `https://${this.host}`, - icon: faExternalLinkAlt, + icon: 'fas fa-external-link-alt', handler: () => { window.open(`https://${this.host}`, '_blank'); } diff --git a/src/client/pages/instance/abuses.vue b/src/client/pages/instance/abuses.vue index c8355b0683..73196027dc 100644 --- a/src/client/pages/instance/abuses.vue +++ b/src/client/pages/instance/abuses.vue @@ -1,5 +1,5 @@ <template> -<div class=""> +<div class="lcixvhis"> <div class="_section reports"> <div class="_content"> <div class="inputs" style="display: flex;"> @@ -63,8 +63,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPlus, faUsers, faSearch, faBookmark, faMicrophoneSlash, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; -import { faSnowflake, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons'; import parseAcct from '@/misc/acct/parse'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -82,11 +80,13 @@ export default defineComponent({ MkPagination, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.abuseReports, - icon: faExclamationCircle + icon: 'fas fa-exclamation-circle' }, searchUsername: '', searchHost: '', @@ -102,7 +102,6 @@ export default defineComponent({ targetUserOrigin: this.targetUserOrigin, }), }, - faPlus, faUsers, faSearch, faBookmark, farBookmark, faMicrophoneSlash, faSnowflake } }, @@ -120,6 +119,10 @@ export default defineComponent({ }, }, + mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + methods: { acct, @@ -135,6 +138,10 @@ export default defineComponent({ </script> <style lang="scss" scoped> +.lcixvhis { + margin: var(--margin); +} + .bcekxzvu { > .target { display: flex; diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue index f9d58a29c4..ac0e9d5135 100644 --- a/src/client/pages/instance/announcements.vue +++ b/src/client/pages/instance/announcements.vue @@ -1,35 +1,29 @@ <template> <div class="ztgjmzrw"> - <div class="_section"> - <div class="_content"> - <MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton> - <section class="_card _gap announcements" v-for="announcement in announcements"> - <div class="_content announcement"> - <MkInput v-model:value="announcement.title"> - <span>{{ $ts.title }}</span> - </MkInput> - <MkTextarea v-model:value="announcement.text"> - <span>{{ $ts.text }}</span> - </MkTextarea> - <MkInput v-model:value="announcement.imageUrl"> - <span>{{ $ts.imageUrl }}</span> - </MkInput> - <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p> - <div class="buttons"> - <MkButton class="button" inline @click="save(announcement)" primary><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - <MkButton class="button" inline @click="remove(announcement)"><Fa :icon="faTrashAlt"/> {{ $ts.remove }}</MkButton> - </div> - </div> - </section> + <MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton> + <section class="_card _gap announcements" v-for="announcement in announcements"> + <div class="_content announcement"> + <MkInput v-model:value="announcement.title"> + <span>{{ $ts.title }}</span> + </MkInput> + <MkTextarea v-model:value="announcement.text"> + <span>{{ $ts.text }}</span> + </MkTextarea> + <MkInput v-model:value="announcement.imageUrl"> + <span>{{ $ts.imageUrl }}</span> + </MkInput> + <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p> + <div class="buttons"> + <MkButton class="button" inline @click="save(announcement)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> + </div> </div> - </div> + </section> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faBroadcastTower, faPlus } from '@fortawesome/free-solid-svg-icons'; -import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkTextarea from '@client/components/ui/textarea.vue'; @@ -43,14 +37,15 @@ export default defineComponent({ MkTextarea, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.announcements, - icon: faBroadcastTower + icon: 'fas fa-broadcast-tower' }, announcements: [], - faBroadcastTower, faSave, faTrashAlt, faPlus } }, @@ -60,6 +55,10 @@ export default defineComponent({ }); }, + mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + methods: { add() { this.announcements.unshift({ @@ -112,3 +111,9 @@ export default defineComponent({ } }); </script> + +<style lang="scss" scoped> +.ztgjmzrw { + margin: var(--margin); +} +</style> diff --git a/src/client/pages/instance/bot-protection.vue b/src/client/pages/instance/bot-protection.vue new file mode 100644 index 0000000000..449b8a233d --- /dev/null +++ b/src/client/pages/instance/bot-protection.vue @@ -0,0 +1,138 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormRadios v-model="provider"> + <template #desc><i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}</template> + <option :value="null">{{ $ts.none }} ({{ $ts.notRecommended }})</option> + <option value="hcaptcha">hCaptcha</option> + <option value="recaptcha">reCAPTCHA</option> + </FormRadios> + + <template v-if="provider === 'hcaptcha'"> + <div class="_formItem _formNoConcat" v-sticky-container> + <div class="_formLabel">hCaptcha</div> + <div class="main"> + <FormInput v-model:value="hcaptchaSiteKey"> + <template #prefix><i class="fas fa-key"></i></template> + <span>{{ $ts.hcaptchaSiteKey }}</span> + </FormInput> + <FormInput v-model:value="hcaptchaSecretKey"> + <template #prefix><i class="fas fa-key"></i></template> + <span>{{ $ts.hcaptchaSecretKey }}</span> + </FormInput> + </div> + </div> + <div class="_formItem _formNoConcat" v-sticky-container> + <div class="_formLabel">{{ $ts.preview }}</div> + <div class="_formPanel" style="padding: var(--formContentHMargin);"> + <MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/> + </div> + </div> + </template> + <template v-else-if="provider === 'recaptcha'"> + <div class="_formItem _formNoConcat" v-sticky-container> + <div class="_formLabel">reCAPTCHA</div> + <div class="main"> + <FormInput v-model:value="recaptchaSiteKey"> + <template #prefix><i class="fas fa-key"></i></template> + <span>{{ $ts.recaptchaSiteKey }}</span> + </FormInput> + <FormInput v-model:value="recaptchaSecretKey"> + <template #prefix><i class="fas fa-key"></i></template> + <span>{{ $ts.recaptchaSecretKey }}</span> + </FormInput> + </div> + </div> + <div v-if="recaptchaSiteKey" class="_formItem _formNoConcat" v-sticky-container> + <div class="_formLabel">{{ $ts.preview }}</div> + <div class="_formPanel" style="padding: var(--formContentHMargin);"> + <MkCaptcha provider="recaptcha" :sitekey="recaptchaSiteKey"/> + </div> + </div> + </template> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineAsyncComponent, defineComponent } from 'vue'; +import FormRadios from '@client/components/form/radios.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormRadios, + FormInput, + FormBase, + FormGroup, + FormButton, + FormInfo, + FormSuspense, + MkCaptcha: defineAsyncComponent(() => import('@client/components/captcha.vue')), + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.botProtection, + icon: 'fas fa-shield-alt' + }, + provider: null, + enableHcaptcha: false, + hcaptchaSiteKey: null, + hcaptchaSecretKey: null, + enableRecaptcha: false, + recaptchaSiteKey: null, + recaptchaSecretKey: null, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableHcaptcha = meta.enableHcaptcha; + this.hcaptchaSiteKey = meta.hcaptchaSiteKey; + this.hcaptchaSecretKey = meta.hcaptchaSecretKey; + this.enableRecaptcha = meta.enableRecaptcha; + this.recaptchaSiteKey = meta.recaptchaSiteKey; + this.recaptchaSecretKey = meta.recaptchaSecretKey; + + this.provider = this.enableHcaptcha ? 'hcaptcha' : this.enableRecaptcha ? 'recaptcha' : null; + + this.$watch(() => this.provider, () => { + this.enableHcaptcha = this.provider === 'hcaptcha'; + this.enableRecaptcha = this.provider === 'recaptcha'; + }); + }, + + save() { + os.apiWithDialog('admin/update-meta', { + enableHcaptcha: this.enableHcaptcha, + hcaptchaSiteKey: this.hcaptchaSiteKey, + hcaptchaSecretKey: this.hcaptchaSecretKey, + enableRecaptcha: this.enableRecaptcha, + recaptchaSiteKey: this.recaptchaSiteKey, + recaptchaSecretKey: this.recaptchaSecretKey, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/database.vue b/src/client/pages/instance/database.vue new file mode 100644 index 0000000000..a41d61ce2b --- /dev/null +++ b/src/client/pages/instance/database.vue @@ -0,0 +1,60 @@ +<template> +<FormBase> + <FormSuspense :p="databasePromiseFactory" v-slot="{ result: database }"> + <FormGroup v-for="table in database" :key="table[0]"> + <template #label>{{ table[0] }}</template> + <FormKeyValueView> + <template #key>Size</template> + <template #value>{{ bytes(table[1].size) }}</template> + </FormKeyValueView> + <FormKeyValueView> + <template #key>Records</template> + <template #value>{{ number(table[1].count) }}</template> + </FormKeyValueView> + </FormGroup> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormLink from '@client/components/form/link.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import bytes from '@client/filters/bytes'; +import number from '@client/filters/number'; + +export default defineComponent({ + components: { + FormSuspense, + FormKeyValueView, + FormBase, + FormGroup, + FormLink, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.database, + icon: 'fas fa-database' + }, + databasePromiseFactory: () => os.api('admin/get-table-stats', {}).then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)), + } + }, + + mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + bytes, number, + } +}); +</script> diff --git a/src/client/pages/instance/email-settings.vue b/src/client/pages/instance/email-settings.vue new file mode 100644 index 0000000000..9965a1420f --- /dev/null +++ b/src/client/pages/instance/email-settings.vue @@ -0,0 +1,127 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormSwitch v-model:value="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch> + + <template v-if="enableEmail"> + <FormInput v-model:value="email" type="email"> + <span>{{ $ts.emailAddress }}</span> + </FormInput> + + <div class="_formItem _formNoConcat" v-sticky-container> + <div class="_formLabel">{{ $ts.smtpConfig }}</div> + <div class="main"> + <FormInput v-model:value="smtpHost"> + <span>{{ $ts.smtpHost }}</span> + </FormInput> + <FormInput v-model:value="smtpPort" type="number"> + <span>{{ $ts.smtpPort }}</span> + </FormInput> + <FormInput v-model:value="smtpUser"> + <span>{{ $ts.smtpUser }}</span> + </FormInput> + <FormInput v-model:value="smtpPass" type="password"> + <span>{{ $ts.smtpPass }}</span> + </FormInput> + <FormInfo>{{ $ts.emptyToDisableSmtpAuth }}</FormInfo> + <FormSwitch v-model:value="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch> + </div> + </div> + + <FormButton @click="testEmail">{{ $ts.testEmail }}</FormButton> + </template> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormGroup, + FormButton, + FormInfo, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.emailServer, + icon: 'fas fa-envelope' + }, + enableEmail: false, + email: null, + smtpSecure: false, + smtpHost: '', + smtpPort: 0, + smtpUser: '', + smtpPass: '', + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableEmail = meta.enableEmail; + this.email = meta.email; + this.smtpSecure = meta.smtpSecure; + this.smtpHost = meta.smtpHost; + this.smtpPort = meta.smtpPort; + this.smtpUser = meta.smtpUser; + this.smtpPass = meta.smtpPass; + }, + + async testEmail() { + const { canceled, result: destination } = await os.dialog({ + title: this.$ts.destination, + input: { + placeholder: this.$instance.maintainerEmail + } + }); + if (canceled) return; + os.apiWithDialog('admin/send-email', { + to: destination, + subject: 'Test email', + text: 'Yo' + }); + }, + + save() { + os.apiWithDialog('admin/update-meta', { + enableEmail: this.enableEmail, + email: this.email, + smtpSecure: this.smtpSecure, + smtpHost: this.smtpHost, + smtpPort: this.smtpPort, + smtpUser: this.smtpUser, + smtpPass: this.smtpPass, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/emoji-edit-dialog.vue b/src/client/pages/instance/emoji-edit-dialog.vue index 34eca47b4e..f7a3671584 100644 --- a/src/client/pages/instance/emoji-edit-dialog.vue +++ b/src/client/pages/instance/emoji-edit-dialog.vue @@ -8,22 +8,23 @@ > <template #header>:{{ emoji.name }}:</template> - <div class="yigymqpb _section"> - <img :src="emoji.url" class="img"/> - <MkInput v-model:value="name"><span>{{ $ts.name }}</span></MkInput> - <MkInput v-model:value="category" :datalist="categories"><span>{{ $ts.category }}</span></MkInput> - <MkInput v-model:value="aliases"> - <span>{{ $ts.tags }}</span> - <template #desc>{{ $ts.setMultipleBySeparatingWithSpace }}</template> - </MkInput> - <MkButton danger @click="del()"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton> + <div class="_monolithic_"> + <div class="yigymqpb _section"> + <img :src="emoji.url" class="img"/> + <MkInput v-model:value="name"><span>{{ $ts.name }}</span></MkInput> + <MkInput v-model:value="category" :datalist="categories"><span>{{ $ts.category }}</span></MkInput> + <MkInput v-model:value="aliases"> + <span>{{ $ts.tags }}</span> + <template #desc>{{ $ts.setMultipleBySeparatingWithSpace }}</template> + </MkInput> + <MkButton danger @click="del()"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> + </div> </div> </XModalWindow> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import XModalWindow from '@client/components/ui/modal-window.vue'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -51,7 +52,6 @@ export default defineComponent({ category: this.emoji.category, aliases: this.emoji.aliases?.join(' '), categories: [], - faTrashAlt, } }, diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue index 722ed0063f..fd641703cb 100644 --- a/src/client/pages/instance/emojis.vue +++ b/src/client/pages/instance/emojis.vue @@ -1,58 +1,52 @@ <template> -<div class="mk-instance-emojis"> - <div class="_section" style="padding: 0;"> - <MkTab v-model:value="tab"> - <option value="local">{{ $ts.local }}</option> - <option value="remote">{{ $ts.remote }}</option> - </MkTab> - </div> +<div class="ogwlenmc"> + <MkTab v-model:value="tab"> + <option value="local">{{ $ts.local }}</option> + <option value="remote">{{ $ts.remote }}</option> + </MkTab> - <div class="_section"> - <div class="local" v-if="tab === 'local'"> - <MkButton primary @click="add" style="margin: 0 auto var(--margin) auto;"><Fa :icon="faPlus"/> {{ $ts.addEmoji }}</MkButton> - <MkInput v-model:value="query" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $ts.search }}</span></MkInput> - <MkPagination :pagination="pagination" ref="emojis"> - <template #empty><span>{{ $ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="emojis"> - <button class="emoji _panel _button" v-for="emoji in items" :key="emoji.id" @click="edit(emoji)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name">{{ emoji.name }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> - </div> - </template> - </MkPagination> - </div> + <div class="local" v-if="tab === 'local'"> + <MkButton primary @click="add" style="margin: var(--margin) auto;"><i class="fas fa-plus"></i> {{ $ts.addEmoji }}</MkButton> + <MkInput v-model:value="query" :debounce="true" type="search" style="margin: var(--margin);"><template #icon><i class="fas fa-search"></i></template><span>{{ $ts.search }}</span></MkInput> + <MkPagination :pagination="pagination" ref="emojis"> + <template #empty><span>{{ $ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <button class="emoji _panel _button" v-for="emoji in items" :key="emoji.id" @click="edit(emoji)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + </div> + </template> + </MkPagination> + </div> - <div class="remote" v-else-if="tab === 'remote'"> - <MkInput v-model:value="queryRemote" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $ts.search }}</span></MkInput> - <MkInput v-model:value="host" :debounce="true"><span>{{ $ts.host }}</span></MkInput> - <MkPagination :pagination="remotePagination" ref="remoteEmojis"> - <template #empty><span>{{ $ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="emojis"> - <div class="emoji _panel _button" v-for="emoji in items" :key="emoji.id" @click="remoteMenu(emoji, $event)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name">{{ emoji.name }}</div> - <div class="info">{{ emoji.host }}</div> - </div> + <div class="remote" v-else-if="tab === 'remote'"> + <MkInput v-model:value="queryRemote" :debounce="true" type="search" style="margin: var(--margin);"><template #icon><i class="fas fa-search"></i></template><span>{{ $ts.search }}</span></MkInput> + <MkInput v-model:value="host" :debounce="true" style="margin: var(--margin);"><span>{{ $ts.host }}</span></MkInput> + <MkPagination :pagination="remotePagination" ref="remoteEmojis"> + <template #empty><span>{{ $ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <div class="emoji _panel _button" v-for="emoji in items" :key="emoji.id" @click="remoteMenu(emoji, $event)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.host }}</div> </div> </div> - </template> - </MkPagination> - </div> + </div> + </template> + </MkPagination> </div> </div> </template> <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faPlus, faSave, faSearch } from '@fortawesome/free-solid-svg-icons'; -import { faTrashAlt, faLaugh } from '@fortawesome/free-regular-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkPagination from '@client/components/ui/pagination.vue'; @@ -69,13 +63,15 @@ export default defineComponent({ MkPagination, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.customEmojis, - icon: faLaugh, + icon: 'fas fa-laugh', action: { - icon: faPlus, + icon: 'fas fa-plus', handler: this.add } }, @@ -98,10 +94,13 @@ export default defineComponent({ host: (this.host && this.host !== '') ? this.host : null })) }, - faTrashAlt, faPlus, faLaugh, faSave, faSearch, } }, + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + methods: { async add(e) { const files = await selectFile(e.currentTarget || e.target, null, true); @@ -144,7 +143,7 @@ export default defineComponent({ text: ':' + emoji.name + ':', }, { text: this.$ts.import, - icon: faPlus, + icon: 'fas fa-plus', action: () => { this.im(emoji) } }], ev.currentTarget || ev.target); } @@ -153,85 +152,86 @@ export default defineComponent({ </script> <style lang="scss" scoped> -.mk-instance-emojis { - > ._section { - > .local { - .emojis { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); - grid-gap: var(--margin); - - > .emoji { - display: flex; - align-items: center; - padding: 12px; - text-align: left; +.ogwlenmc { + > .local { + .ldhfsamy { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + grid-gap: 12px; + margin: var(--margin); + + > .emoji { + display: flex; + align-items: center; + padding: 12px; + text-align: left; - &:hover { - color: var(--accent); - } + &:hover { + color: var(--accent); + } - > .img { - width: 42px; - height: 42px; - } + > .img { + width: 42px; + height: 42px; + } - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; + > .body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; - > .name { - text-overflow: ellipsis; - overflow: hidden; - } + > .name { + text-overflow: ellipsis; + overflow: hidden; + } - > .info { - opacity: 0.5; - text-overflow: ellipsis; - overflow: hidden; - } + > .info { + opacity: 0.5; + text-overflow: ellipsis; + overflow: hidden; } } } } + } - > .remote { - .emojis { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); - grid-gap: var(--margin); + > .remote { + .ldhfsamy { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + grid-gap: 12px; + margin: var(--margin); - > .emoji { - display: flex; - align-items: center; - padding: 12px; - text-align: left; + > .emoji { + display: flex; + align-items: center; + padding: 12px; + text-align: left; - &:hover { - color: var(--accent); - } + &:hover { + color: var(--accent); + } - > .img { - width: 32px; - height: 32px; - } + > .img { + width: 32px; + height: 32px; + } - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; + > .body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; - > .name { - text-overflow: ellipsis; - overflow: hidden; - } + > .name { + text-overflow: ellipsis; + overflow: hidden; + } - > .info { - opacity: 0.5; - text-overflow: ellipsis; - overflow: hidden; - } + > .info { + opacity: 0.5; + font-size: 90%; + text-overflow: ellipsis; + overflow: hidden; } } } diff --git a/src/client/pages/instance/federation.vue b/src/client/pages/instance/federation.vue index 2a820e5baf..96f72fed44 100644 --- a/src/client/pages/instance/federation.vue +++ b/src/client/pages/instance/federation.vue @@ -1,66 +1,60 @@ <template> -<div> - <div class="_section"> - <div class="_content"> - <MkInput v-model:value="host" :debounce="true"><span>{{ $ts.host }}</span></MkInput> - <div class="inputs" style="display: flex;"> - <MkSelect v-model:value="state" style="margin: 0; flex: 1;"> - <template #label>{{ $ts.state }}</template> - <option value="all">{{ $ts.all }}</option> - <option value="federating">{{ $ts.federating }}</option> - <option value="subscribing">{{ $ts.subscribing }}</option> - <option value="publishing">{{ $ts.publishing }}</option> - <option value="suspended">{{ $ts.suspended }}</option> - <option value="blocked">{{ $ts.blocked }}</option> - <option value="notResponding">{{ $ts.notResponding }}</option> - </MkSelect> - <MkSelect v-model:value="sort" style="margin: 0; flex: 1;"> - <template #label>{{ $ts.sort }}</template> - <option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option> - <option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option> - <option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option> - <option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option> - <option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option> - <option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option> - <option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option> - <option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option> - <option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option> - <option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option> - <option value="+caughtAt">{{ $ts.caughtAt }} ({{ $ts.descendingOrder }})</option> - <option value="-caughtAt">{{ $ts.caughtAt }} ({{ $ts.ascendingOrder }})</option> - <option value="+lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.descendingOrder }})</option> - <option value="-lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.ascendingOrder }})</option> - <option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option> - <option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option> - <option value="+driveFiles">{{ $ts.driveFiles }} ({{ $ts.descendingOrder }})</option> - <option value="-driveFiles">{{ $ts.driveFiles }} ({{ $ts.ascendingOrder }})</option> - </MkSelect> - </div> +<div class="enuoauvw"> + <div class="query"> + <MkInput v-model:value="host" :debounce="true"><span>{{ $ts.host }}</span></MkInput> + <div class="inputs" style="display: flex;"> + <MkSelect v-model:value="state" style="margin: 0; flex: 1;"> + <template #label>{{ $ts.state }}</template> + <option value="all">{{ $ts.all }}</option> + <option value="federating">{{ $ts.federating }}</option> + <option value="subscribing">{{ $ts.subscribing }}</option> + <option value="publishing">{{ $ts.publishing }}</option> + <option value="suspended">{{ $ts.suspended }}</option> + <option value="blocked">{{ $ts.blocked }}</option> + <option value="notResponding">{{ $ts.notResponding }}</option> + </MkSelect> + <MkSelect v-model:value="sort" style="margin: 0; flex: 1;"> + <template #label>{{ $ts.sort }}</template> + <option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option> + <option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option> + <option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option> + <option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option> + <option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option> + <option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option> + <option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option> + <option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option> + <option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option> + <option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option> + <option value="+caughtAt">{{ $ts.caughtAt }} ({{ $ts.descendingOrder }})</option> + <option value="-caughtAt">{{ $ts.caughtAt }} ({{ $ts.ascendingOrder }})</option> + <option value="+lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.descendingOrder }})</option> + <option value="-lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.ascendingOrder }})</option> + <option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option> + <option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option> + <option value="+driveFiles">{{ $ts.driveFiles }} ({{ $ts.descendingOrder }})</option> + <option value="-driveFiles">{{ $ts.driveFiles }} ({{ $ts.ascendingOrder }})</option> + </MkSelect> </div> </div> - <div class="_section"> - <div class="_content"> - <MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state"> - <div class="ppgwaixt _panel" v-for="instance in items" :key="instance.id" @click="info(instance)"> - <div class="host"><Fa :icon="faCircle" class="indicator" :class="getStatus(instance)"/><b>{{ instance.host }}</b></div> - <div class="status"> - <span class="sub" v-if="instance.followersCount > 0"><Fa :icon="faCaretDown" class="icon"/>Sub</span> - <span class="sub" v-else><Fa :icon="faCaretDown" class="icon"/>-</span> - <span class="pub" v-if="instance.followingCount > 0"><Fa :icon="faCaretUp" class="icon"/>Pub</span> - <span class="pub" v-else><Fa :icon="faCaretUp" class="icon"/>-</span> - <span class="lastCommunicatedAt"><Fa :icon="faExchangeAlt" class="icon"/><MkTime :time="instance.lastCommunicatedAt"/></span> - <span class="latestStatus"><Fa :icon="faTrafficLight" class="icon"/>{{ instance.latestStatus || '-' }}</span> - </div> - </div> - </MkPagination> + + <MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state"> + <div class="ppgwaixt _block" v-for="instance in items" :key="instance.id" @click="info(instance)"> + <div class="host"><i class="fas fa-circle indicator" :class="getStatus(instance)"></i><b>{{ instance.host }}</b></div> + <div class="status"> + <span class="sub" v-if="instance.followersCount > 0"><i class="fas fa-caret-down icon"></i>Sub</span> + <span class="sub" v-else><i class="fas fa-caret-down icon"></i>-</span> + <span class="pub" v-if="instance.followingCount > 0"><i class="fas fa-caret-up icon"></i>Pub</span> + <span class="pub" v-else><i class="fas fa-caret-up icon"></i>-</span> + <span class="lastCommunicatedAt"><i class="fas fa-exchange-alt icon"></i><MkTime :time="instance.lastCommunicatedAt"/></span> + <span class="latestStatus"><i class="fas fa-traffic-light icon"></i>{{ instance.latestStatus || '-' }}</span> + </div> </div> - </div> + </MkPagination> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faGlobe, faCircle, faExchangeAlt, faCaretDown, faCaretUp, faTrafficLight } from '@fortawesome/free-solid-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -77,11 +71,13 @@ export default defineComponent({ MkPagination, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.federation, - icon: faGlobe + icon: 'fas fa-globe' }, host: '', state: 'federating', @@ -103,7 +99,6 @@ export default defineComponent({ {}) }) }, - faGlobe, faCircle, faExchangeAlt, faCaretDown, faCaretUp, faTrafficLight } }, @@ -116,6 +111,10 @@ export default defineComponent({ } }, + mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + methods: { getStatus(instance) { if (instance.isSuspended) return 'off'; @@ -133,6 +132,12 @@ export default defineComponent({ </script> <style lang="scss" scoped> +.enuoauvw { + > .query { + margin: var(--margin); + } +} + .ppgwaixt { cursor: pointer; padding: 16px; diff --git a/src/client/pages/instance/file-dialog.vue b/src/client/pages/instance/file-dialog.vue index 85c03e3dce..74a755fa15 100644 --- a/src/client/pages/instance/file-dialog.vue +++ b/src/client/pages/instance/file-dialog.vue @@ -21,8 +21,8 @@ </div> <div class="_section"> <div class="_content"> - <MkButton full @click="showUser"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts.user }}</MkButton> - <MkButton full danger @click="del"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton> + <MkButton full @click="showUser"><i class="fas fa-external-link-square-alt"></i> {{ $ts.user }}</MkButton> + <MkButton full danger @click="del"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> </div> </div> <div class="_section" v-if="info"> @@ -36,8 +36,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faTimes, faBookmark, faKey, faSync, faMicrophoneSlash, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons'; -import { faSnowflake, faTrashAlt, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkSwitch from '@client/components/ui/switch.vue'; import XModalWindow from '@client/components/ui/modal-window.vue'; @@ -67,7 +65,6 @@ export default defineComponent({ file: null, info: null, isSensitive: false, - faTimes, faBookmark, farBookmark, faKey, faSync, faMicrophoneSlash, faSnowflake, faTrashAlt, faExternalLinkSquareAlt }; }, @@ -85,9 +82,7 @@ export default defineComponent({ }, showUser() { - os.popup(import('./user-dialog.vue'), { - userId: this.file.userId - }, {}, 'closed'); + os.pageWindow(`/user-info/${this.file.userId}`); }, async del() { diff --git a/src/client/pages/instance/files-settings.vue b/src/client/pages/instance/files-settings.vue new file mode 100644 index 0000000000..614c7d4dbb --- /dev/null +++ b/src/client/pages/instance/files-settings.vue @@ -0,0 +1,92 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormSwitch v-model:value="cacheRemoteFiles"> + {{ $ts.cacheRemoteFiles }} + <template #desc>{{ $ts.cacheRemoteFilesDescription }}</template> + </FormSwitch> + + <FormSwitch v-model:value="proxyRemoteFiles"> + {{ $ts.proxyRemoteFiles }} + <template #desc>{{ $ts.proxyRemoteFilesDescription }}</template> + </FormSwitch> + + <FormInput v-model:value="localDriveCapacityMb" type="number"> + <span>{{ $ts.driveCapacityPerLocalAccount }}</span> + <template #suffix>MB</template> + <template #desc>{{ $ts.inMb }}</template> + </FormInput> + + <FormInput v-model:value="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles"> + <span>{{ $ts.driveCapacityPerRemoteAccount }}</span> + <template #suffix>MB</template> + <template #desc>{{ $ts.inMb }}</template> + </FormInput> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormGroup, + FormButton, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.files, + icon: 'fas fa-cloud' + }, + cacheRemoteFiles: false, + proxyRemoteFiles: false, + localDriveCapacityMb: 0, + remoteDriveCapacityMb: 0, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.cacheRemoteFiles = meta.cacheRemoteFiles; + this.proxyRemoteFiles = meta.proxyRemoteFiles; + this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb; + this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb; + }, + save() { + os.apiWithDialog('admin/update-meta', { + cacheRemoteFiles: this.cacheRemoteFiles, + proxyRemoteFiles: this.proxyRemoteFiles, + localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), + remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10), + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/files.vue b/src/client/pages/instance/files.vue index e7de050df8..427c5b411a 100644 --- a/src/client/pages/instance/files.vue +++ b/src/client/pages/instance/files.vue @@ -2,17 +2,17 @@ <div class="xrmjdkdw"> <div class="_section"> <div class="_content"> - <MkButton primary @click="clear()"><Fa :icon="faTrashAlt"/> {{ $ts.clearCachedFiles }}</MkButton> + <MkButton primary @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearCachedFiles }}</MkButton> </div> </div> <div class="_section lookup"> - <div class="_title"><Fa :icon="faSearch"/> {{ $ts.lookup }}</div> + <div class="_title"><i class="fas fa-search"></i> {{ $ts.lookup }}</div> <div class="_content"> <MkInput class="target" v-model:value="q" type="text" @enter="find()"> <span>{{ $ts.fileIdOrUrl }}</span> </MkInput> - <MkButton @click="find()" primary><Fa :icon="faSearch"/> {{ $ts.lookup }}</MkButton> + <MkButton @click="find()" primary><i class="fas fa-search"></i> {{ $ts.lookup }}</MkButton> </div> </div> @@ -62,8 +62,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCloud, faSearch } from '@fortawesome/free-solid-svg-icons'; -import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -82,11 +80,13 @@ export default defineComponent({ MkDriveFileThumbnail, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.files, - icon: faCloud + icon: 'fas fa-cloud' }, q: null, origin: 'local', @@ -101,7 +101,6 @@ export default defineComponent({ hostname: (this.hostname && this.hostname !== '') ? this.hostname : null, }), }, - faTrashAlt, faCloud, faSearch, } }, @@ -117,6 +116,10 @@ export default defineComponent({ }, }, + mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + methods: { clear() { os.dialog({ @@ -156,6 +159,8 @@ export default defineComponent({ <style lang="scss" scoped> .xrmjdkdw { + margin: var(--margin); + .urempief { margin-top: var(--margin); diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index 731acd8f00..5972a02de0 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -1,173 +1,243 @@ <template> -<div v-if="meta" v-show="page === 'index'" class="xhexznfu _section"> - <MkFolder> - <template #header><Fa :icon="faTachometerAlt"/> {{ $ts.overview }}</template> - - <div class="sboqnrfi" :style="{ gridTemplateRows: overviewHeight }"> - <MkInstanceStats :chart-limit="300" :detailed="true" class="_gap" ref="stats"/> - - <MkContainer :foldable="true" class="_gap"> - <template #header><Fa :icon="faInfoCircle"/>{{ $ts.instanceInfo }}</template> - - <div class="_content"> - <div class="_keyValue"><b>Misskey</b><span>v{{ version }}</span></div> - </div> - <div class="_content" v-if="serverInfo"> - <div class="_keyValue"><b>Node.js</b><span>{{ serverInfo.node }}</span></div> - <div class="_keyValue"><b>PostgreSQL</b><span>v{{ serverInfo.psql }}</span></div> - <div class="_keyValue"><b>Redis</b><span>v{{ serverInfo.redis }}</span></div> - </div> - </MkContainer> - - <MkContainer :foldable="true" :scrollable="true" class="_gap" style="height: 300px;"> - <template #header><Fa :icon="faDatabase"/>{{ $ts.database }}</template> - - <div class="_content" v-if="dbInfo"> - <table style="border-collapse: collapse; width: 100%;"> - <tr style="opacity: 0.7;"> - <th style="text-align: left; padding: 0 8px 8px 0;">Table</th> - <th style="text-align: left; padding: 0 8px 8px 0;">Records</th> - <th style="text-align: left; padding: 0 0 8px 0;">Size</th> - </tr> - <tr v-for="table in dbInfo" :key="table[0]"> - <th style="text-align: left; padding: 0 8px 0 0; word-break: break-all;">{{ table[0] }}</th> - <td style="padding: 0 8px 0 0;">{{ number(table[1].count) }}</td> - <td style="padding: 0; opacity: 0.7;">{{ bytes(table[1].size) }}</td> - </tr> - </table> +<div class="hiyeyicy" :class="{ wide: !narrow }" ref="el"> + <div class="nav" v-if="!narrow || page == null"> + <FormBase> + <FormGroup> + <div class="_formItem"> + <div class="_formPanel lxpfedzu"> + <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> + </div> </div> - </MkContainer> - </div> - </MkFolder> -</div> -<div v-if="page === 'logs'" class="_section"> - <MkFolder> - <template #header><Fa :icon="faStream"/> {{ $ts.logs }}</template> - - <div class="_keyValue" v-for="log in modLogs"> - <b>{{ log.type }}</b><span>by {{ log.user.username }}</span><MkTime :time="log.createdAt" style="opacity: 0.7;"/> - </div> - </MkFolder> -</div> -<div v-if="page === 'metrics'"> - <XMetrics/> + <FormLink :active="page === 'overview'" replace to="/instance/overview"><template #icon><i class="fas fa-tachometer-alt"></i></template>{{ $ts.overview }}</FormLink> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.quickAction }}</template> + <FormButton @click="lookup"><i class="fas fa-search"></i> {{ $ts.lookup }}</FormButton> + <FormButton v-if="$instance.disableRegistration" @click="invite"><i class="fas fa-user"></i> {{ $ts.invite }}</FormButton> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.administration }}</template> + <FormLink :active="page === 'users'" replace to="/instance/users"><template #icon><i class="fas fa-users"></i></template>{{ $ts.users }}</FormLink> + <FormLink :active="page === 'emojis'" replace to="/instance/emojis"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.customEmojis }}</FormLink> + <FormLink :active="page === 'federation'" replace to="/instance/federation"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.federation }}</FormLink> + <FormLink :active="page === 'queue'" replace to="/instance/queue"><template #icon><i class="fas fa-clipboard-list"></i></template>{{ $ts.jobQueue }}</FormLink> + <FormLink :active="page === 'files'" replace to="/instance/files"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink> + <FormLink :active="page === 'announcements'" replace to="/instance/announcements"><template #icon><i class="fas fa-broadcast-tower"></i></template>{{ $ts.announcements }}</FormLink> + <FormLink :active="page === 'abuses'" replace to="/instance/abuses"><template #icon><i class="fas fa-exclamation-circle"></i></template>{{ $ts.abuseReports }}</FormLink> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.settings }}</template> + <FormLink :active="page === 'settings'" replace to="/instance/settings"><template #icon><i class="fas fa-cog"></i></template>{{ $ts.general }}</FormLink> + <FormLink :active="page === 'files-settings'" replace to="/instance/files-settings"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink> + <FormLink :active="page === 'email-settings'" replace to="/instance/email-settings"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.emailServer }}</FormLink> + <FormLink :active="page === 'object-storage'" replace to="/instance/object-storage"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.objectStorage }}</FormLink> + <FormLink :active="page === 'security'" replace to="/instance/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</FormLink> + <FormLink :active="page === 'service-worker'" replace to="/instance/service-worker"><template #icon><i class="fas fa-bolt"></i></template>ServiceWorker</FormLink> + <FormLink :active="page === 'relays'" replace to="/instance/relays"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.relays }}</FormLink> + <FormLink :active="page === 'integrations'" replace to="/instance/integrations"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</FormLink> + <FormLink :active="page === 'instance-block'" replace to="/instance/instance-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.instanceBlocking }}</FormLink> + <FormLink :active="page === 'proxy-account'" replace to="/instance/proxy-account"><template #icon><i class="fas fa-ghost"></i></template>{{ $ts.proxyAccount }}</FormLink> + <FormLink :active="page === 'other-settings'" replace to="/instance/other-settings"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.other }}</FormLink> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.info }}</template> + <FormLink :active="page === 'database'" replace to="/instance/database"><template #icon><i class="fas fa-database"></i></template>{{ $ts.database }}</FormLink> + </FormGroup> + </FormBase> + </div> + <div class="main"> + <component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/> + </div> </div> </template> <script lang="ts"> -import { computed, defineComponent, markRaw } from 'vue'; -import { faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList } from '@fortawesome/free-solid-svg-icons'; -import VueJsonPretty from 'vue-json-pretty'; -import MkInstanceStats from '@client/components/instance-stats.vue'; -import MkButton from '@client/components/ui/button.vue'; -import MkSelect from '@client/components/ui/select.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkContainer from '@client/components/ui/container.vue'; -import MkFolder from '@client/components/ui/folder.vue'; -import { version, url } from '@client/config'; -import bytes from '../../filters/bytes'; -import number from '../../filters/number'; -import MkInstanceInfo from './instance.vue'; -import XMetrics from './index.metrics.vue'; -import * as os from '@client/os'; +import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; +import { i18n } from '@client/i18n'; +import FormLink from '@client/components/form/link.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormButton from '@client/components/form/button.vue'; +import { scroll } from '@client/scripts/scroll'; import * as symbols from '@client/symbols'; +import * as os from '@client/os'; +import { lookupUser } from '@client/scripts/lookup-user'; export default defineComponent({ components: { - MkInstanceStats, - MkButton, - MkSelect, - MkInput, - MkContainer, - MkFolder, - XMetrics, - VueJsonPretty, + FormBase, + FormLink, + FormGroup, + FormButton, }, - data() { - return { - [symbols.PAGE_INFO]: { - tabs: [{ - id: 'index', - title: null, - tooltip: this.$ts.instance, - icon: faServer, - onClick: () => { this.page = 'index'; }, - selected: computed(() => this.page === 'index') - }, { - id: 'metrics', - title: null, - tooltip: this.$ts.metrics, - icon: faHeartbeat, - onClick: () => { this.page = 'metrics'; }, - selected: computed(() => this.page === 'metrics') - }, { - id: 'logs', - title: null, - tooltip: this.$ts.logs, - icon: faStream, - onClick: () => { this.page = 'logs'; }, - selected: computed(() => this.page === 'logs') - }] - }, - page: 'index', - version, - url, - stats: null, - serverInfo: null, - modLogs: [], - dbInfo: null, - faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList, + props: { + initialPage: { + type: String, + required: false } }, - computed: { - meta() { - return this.$instance; - }, - }, + setup(props, context) { + const indexInfo = { + title: i18n.locale.instance, + icon: 'fas fa-cog' + }; + const INFO = ref(indexInfo); + const page = ref(props.initialPage); + const narrow = ref(false); + const view = ref(null); + const el = ref(null); + const onInfo = (viewInfo) => { + INFO.value = viewInfo; + }; + const pageProps = ref({}); + const component = computed(() => { + if (page.value == null) return null; + switch (page.value) { + case 'overview': return defineAsyncComponent(() => import('./overview.vue')); + case 'users': return defineAsyncComponent(() => import('./users.vue')); + case 'emojis': return defineAsyncComponent(() => import('./emojis.vue')); + case 'federation': return defineAsyncComponent(() => import('./federation.vue')); + case 'queue': return defineAsyncComponent(() => import('./queue.vue')); + case 'files': return defineAsyncComponent(() => import('./files.vue')); + case 'announcements': return defineAsyncComponent(() => import('./announcements.vue')); + case 'database': return defineAsyncComponent(() => import('./database.vue')); + case 'abuses': return defineAsyncComponent(() => import('./abuses.vue')); + case 'settings': return defineAsyncComponent(() => import('./settings.vue')); + case 'files-settings': return defineAsyncComponent(() => import('./files-settings.vue')); + case 'email-settings': return defineAsyncComponent(() => import('./email-settings.vue')); + case 'object-storage': return defineAsyncComponent(() => import('./object-storage.vue')); + case 'security': return defineAsyncComponent(() => import('./security.vue')); + case 'bot-protection': return defineAsyncComponent(() => import('./bot-protection.vue')); + case 'service-worker': return defineAsyncComponent(() => import('./service-worker.vue')); + case 'relays': return defineAsyncComponent(() => import('./relays.vue')); + case 'integrations': return defineAsyncComponent(() => import('./integrations.vue')); + case 'integrations/twitter': return defineAsyncComponent(() => import('./integrations-twitter.vue')); + case 'integrations/github': return defineAsyncComponent(() => import('./integrations-github.vue')); + case 'integrations/discord': return defineAsyncComponent(() => import('./integrations-discord.vue')); + case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue')); + case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue')); + case 'other-settings': return defineAsyncComponent(() => import('./other-settings.vue')); + } + }); - mounted() { - this.fetchJobs(); - this.fetchModLogs(); + watch(component, () => { + pageProps.value = {}; - os.api('admin/server-info', {}).then(res => { - this.serverInfo = res; - }); + nextTick(() => { + scroll(el.value, 0); + }); + }, { immediate: true }); - os.api('admin/get-table-stats', {}).then(res => { - this.dbInfo = Object.entries(res).sort((a, b) => b[1].size - a[1].size); + watch(() => props.initialPage, () => { + if (props.initialPage == null && !narrow.value) { + page.value = 'overview'; + } else { + page.value = props.initialPage; + if (props.initialPage == null) { + INFO.value = indexInfo; + } + } }); - }, - methods: { - async showInstanceInfo(q) { - let instance = q; - if (typeof q === 'string') { - instance = await os.api('federation/show-instance', { - host: q - }); + onMounted(() => { + narrow.value = el.value.offsetWidth < 800; + if (!narrow.value) { + page.value = 'overview'; } - os.popup(MkInstanceInfo, { - instance: instance - }, {}, 'closed'); - }, - - fetchJobs() { - os.api('admin/queue/deliver-delayed', {}).then(jobs => { - this.jobs = jobs; - }); - }, + }); - fetchModLogs() { - os.api('admin/show-moderation-logs', {}).then(logs => { - this.modLogs = logs; + const invite = () => { + os.api('admin/invite').then(x => { + os.dialog({ + type: 'info', + text: x.code + }); + }).catch(e => { + os.dialog({ + type: 'error', + text: e + }); }); - }, + }; - bytes, + const lookup = (ev) => { + os.modalMenu([{ + text: i18n.locale.user, + icon: 'fas fa-user', + action: () => { + lookupUser(); + } + }, { + text: i18n.locale.note, + icon: 'fas fa-pencil-alt', + action: () => { + alert('TODO'); + } + }, { + text: i18n.locale.file, + icon: 'fas fa-cloud', + action: () => { + alert('TODO'); + } + }, { + text: i18n.locale.instance, + icon: 'fas fa-globe', + action: () => { + alert('TODO'); + } + }], ev.currentTarget || ev.target); + }; - number, - } + return { + [symbols.PAGE_INFO]: INFO, + page, + narrow, + view, + el, + onInfo, + pageProps, + component, + invite, + lookup, + }; + }, }); </script> + +<style lang="scss" scoped> +.hiyeyicy { + &.wide { + display: flex; + max-width: 1100px; + margin: 0 auto; + height: 100%; + + > .nav { + width: 32%; + box-sizing: border-box; + border-right: solid 0.5px var(--divider); + overflow: auto; + } + + > .main { + flex: 1; + min-width: 0; + overflow: auto; + --baseContentWidth: 100%; + } + } +} + +.lxpfedzu { + padding: 16px; + + > .icon { + display: block; + margin: auto; + height: 42px; + border-radius: 8px; + } +} +</style> diff --git a/src/client/pages/instance/instance-block.vue b/src/client/pages/instance/instance-block.vue new file mode 100644 index 0000000000..ed5740f339 --- /dev/null +++ b/src/client/pages/instance/instance-block.vue @@ -0,0 +1,71 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormTextarea v-model:value="blockedHosts"> + <span>{{ $ts.blockedInstances }}</span> + <template #desc>{{ $ts.blockedInstancesDescription }}</template> + </FormTextarea> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormTextarea from '@client/components/form/textarea.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormGroup, + FormButton, + FormTextarea, + FormInfo, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.instanceBlocking, + icon: 'fas fa-ban' + }, + blockedHosts: '', + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.blockedHosts = meta.blockedHosts.join('\n'); + }, + + save() { + os.apiWithDialog('admin/update-meta', { + blockedHosts: this.blockedHosts.split('\n') || [], + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/instance.vue b/src/client/pages/instance/instance.vue index 1adb3ab9d2..f52e5d866b 100644 --- a/src/client/pages/instance/instance.vue +++ b/src/client/pages/instance/instance.vue @@ -106,11 +106,11 @@ <MkSwitch :value="isBlocked" class="switch" @update:value="changeBlock">{{ $ts.blockThisInstance }}</MkSwitch> <details> <summary>{{ $ts.deleteAllFiles }}</summary> - <MkButton @click="deleteAllFiles()" style="margin: 0.5em 0 0.5em 0;"><Fa :icon="faTrashAlt"/> {{ $ts.deleteAllFiles }}</MkButton> + <MkButton @click="deleteAllFiles()" style="margin: 0.5em 0 0.5em 0;"><i class="fas fa-trash-alt"></i> {{ $ts.deleteAllFiles }}</MkButton> </details> <details> <summary>{{ $ts.removeAllFollowing }}</summary> - <MkButton @click="removeAllFollowing()" style="margin: 0.5em 0 0.5em 0;"><Fa :icon="faMinusCircle"/> {{ $ts.removeAllFollowing }}</MkButton> + <MkButton @click="removeAllFollowing()" style="margin: 0.5em 0 0.5em 0;"><i class="fas fa-minus-circle"></i> {{ $ts.removeAllFollowing }}</MkButton> <MkInfo warn>{{ $t('removeAllFollowingDescription', { host: instance.host }) }}</MkInfo> </details> </div> @@ -125,7 +125,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; import Chart from 'chart.js'; -import { faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown, faMinusCircle, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import XModalWindow from '@client/components/ui/modal-window.vue'; import MkUsersDialog from '@client/components/users-dialog.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -174,7 +173,6 @@ export default defineComponent({ chartInstance: null, chartSrc: 'requests', chartSpan: 'hour', - faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown, faMinusCircle, faTrashAlt }; }, diff --git a/src/client/pages/instance/integrations-discord.vue b/src/client/pages/instance/integrations-discord.vue new file mode 100644 index 0000000000..c7508918f8 --- /dev/null +++ b/src/client/pages/instance/integrations-discord.vue @@ -0,0 +1,85 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormSwitch v-model:value="enableDiscordIntegration"> + {{ $ts.enable }} + </FormSwitch> + + <template v-if="enableDiscordIntegration"> + <FormInfo>Callback URL: {{ `${url}/api/dc/cb` }}</FormInfo> + + <FormInput v-model:value="discordClientId"> + <template #prefix><i class="fas fa-key"></i></template> + Client ID + </FormInput> + + <FormInput v-model:value="discordClientSecret"> + <template #prefix><i class="fas fa-key"></i></template> + Client Secret + </FormInput> + </template> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormInfo, + FormButton, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: 'Discord', + icon: 'fab fa-discord' + }, + enableDiscordIntegration: false, + discordClientId: null, + discordClientSecret: null, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableDiscordIntegration = meta.enableDiscordIntegration; + this.discordClientId = meta.discordClientId; + this.discordClientSecret = meta.discordClientSecret; + }, + save() { + os.apiWithDialog('admin/update-meta', { + enableDiscordIntegration: this.enableDiscordIntegration, + discordClientId: this.discordClientId, + discordClientSecret: this.discordClientSecret, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/integrations-github.vue b/src/client/pages/instance/integrations-github.vue new file mode 100644 index 0000000000..16586b15b4 --- /dev/null +++ b/src/client/pages/instance/integrations-github.vue @@ -0,0 +1,85 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormSwitch v-model:value="enableGithubIntegration"> + {{ $ts.enable }} + </FormSwitch> + + <template v-if="enableGithubIntegration"> + <FormInfo>Callback URL: {{ `${url}/api/gh/cb` }}</FormInfo> + + <FormInput v-model:value="githubClientId"> + <template #prefix><i class="fas fa-key"></i></template> + Client ID + </FormInput> + + <FormInput v-model:value="githubClientSecret"> + <template #prefix><i class="fas fa-key"></i></template> + Client Secret + </FormInput> + </template> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormInfo, + FormButton, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: 'GitHub', + icon: 'fab fa-github' + }, + enableGithubIntegration: false, + githubClientId: null, + githubClientSecret: null, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableGithubIntegration = meta.enableGithubIntegration; + this.githubClientId = meta.githubClientId; + this.githubClientSecret = meta.githubClientSecret; + }, + save() { + os.apiWithDialog('admin/update-meta', { + enableGithubIntegration: this.enableGithubIntegration, + githubClientId: this.githubClientId, + githubClientSecret: this.githubClientSecret, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/integrations-twitter.vue b/src/client/pages/instance/integrations-twitter.vue new file mode 100644 index 0000000000..b08b7f40a5 --- /dev/null +++ b/src/client/pages/instance/integrations-twitter.vue @@ -0,0 +1,85 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormSwitch v-model:value="enableTwitterIntegration"> + {{ $ts.enable }} + </FormSwitch> + + <template v-if="enableTwitterIntegration"> + <FormInfo>Callback URL: {{ `${url}/api/tw/cb` }}</FormInfo> + + <FormInput v-model:value="twitterConsumerKey"> + <template #prefix><i class="fas fa-key"></i></template> + Consumer Key + </FormInput> + + <FormInput v-model:value="twitterConsumerSecret"> + <template #prefix><i class="fas fa-key"></i></template> + Consumer Secret + </FormInput> + </template> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormInfo, + FormButton, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: 'Twitter', + icon: 'fab fa-twitter' + }, + enableTwitterIntegration: false, + twitterConsumerKey: null, + twitterConsumerSecret: null, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableTwitterIntegration = meta.enableTwitterIntegration; + this.twitterConsumerKey = meta.twitterConsumerKey; + this.twitterConsumerSecret = meta.twitterConsumerSecret; + }, + save() { + os.apiWithDialog('admin/update-meta', { + enableTwitterIntegration: this.enableTwitterIntegration, + twitterConsumerKey: this.twitterConsumerKey, + twitterConsumerSecret: this.twitterConsumerSecret, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/integrations.vue b/src/client/pages/instance/integrations.vue new file mode 100644 index 0000000000..7debedc367 --- /dev/null +++ b/src/client/pages/instance/integrations.vue @@ -0,0 +1,73 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormLink to="/instance/integrations/twitter"> + <i class="fab fa-twitter"></i> Twitter + <template #suffix>{{ enableTwitterIntegration ? $ts.enabled : $ts.disabled }}</template> + </FormLink> + <FormLink to="/instance/integrations/github"> + <i class="fab fa-github"></i> GitHub + <template #suffix>{{ enableGithubIntegration ? $ts.enabled : $ts.disabled }}</template> + </FormLink> + <FormLink to="/instance/integrations/discord"> + <i class="fab fa-discord"></i> Discord + <template #suffix>{{ enableDiscordIntegration ? $ts.enabled : $ts.disabled }}</template> + </FormLink> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormLink from '@client/components/form/link.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormTextarea from '@client/components/form/textarea.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormLink, + FormInput, + FormBase, + FormGroup, + FormButton, + FormTextarea, + FormInfo, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.integration, + icon: 'fas fa-share-alt' + }, + enableTwitterIntegration: false, + enableGithubIntegration: false, + enableDiscordIntegration: false, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableTwitterIntegration = meta.enableTwitterIntegration; + this.enableGithubIntegration = meta.enableGithubIntegration; + this.enableDiscordIntegration = meta.enableDiscordIntegration; + }, + } +}); +</script> diff --git a/src/client/pages/instance/logs.vue b/src/client/pages/instance/logs.vue index f27546a401..7b634259d3 100644 --- a/src/client/pages/instance/logs.vue +++ b/src/client/pages/instance/logs.vue @@ -24,14 +24,12 @@ </code> </div> - <MkButton @click="deleteAllLogs()" primary><Fa :icon="faTrashAlt"/> {{ $ts.deleteAll }}</MkButton> + <MkButton @click="deleteAllLogs()" primary><i class="fas fa-trash-alt"></i> {{ $ts.deleteAll }}</MkButton> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faStream } from '@fortawesome/free-solid-svg-icons'; -import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -51,12 +49,11 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.serverLogs, - icon: faStream + icon: 'fas fa-stream' }, logs: [], logLevel: 'all', logDomain: '', - faTrashAlt, } }, diff --git a/src/client/pages/instance/index.metrics.vue b/src/client/pages/instance/metrics.vue index 17ebf5d0d6..18cfe5eee2 100644 --- a/src/client/pages/instance/index.metrics.vue +++ b/src/client/pages/instance/metrics.vue @@ -1,107 +1,57 @@ <template> -<div> - <MkFolder> - <template #header><Fa :icon="faHeartbeat"/> {{ $ts.metrics }}</template> - <div class="_section" style="padding: 0 var(--margin);"> - <div class="_content"> - <MkContainer :foldable="false" class="_gap"> - <template #header><Fa :icon="faMicrochip"/>{{ $ts.cpuAndMemory }}</template> - <!-- - <template #func> - <button class="_button" @click="resume" :disabled="!paused"><Fa :icon="faPlay"/></button> - <button class="_button" @click="pause" :disabled="paused"><Fa :icon="faPause"/></button> - </template> - --> - - <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> - <canvas :ref="cpumem"></canvas> - </div> - <div class="_content" v-if="serverInfo"> - <div class="_table"> - <div class="_row"> - <div class="_cell"><div class="_label">MEM total</div>{{ bytes(serverInfo.mem.total) }}</div> - <div class="_cell"><div class="_label">MEM used</div>{{ bytes(memUsage) }} ({{ (memUsage / serverInfo.mem.total * 100).toFixed(0) }}%)</div> - <div class="_cell"><div class="_label">MEM free</div>{{ bytes(serverInfo.mem.total - memUsage) }} ({{ ((serverInfo.mem.total - memUsage) / serverInfo.mem.total * 100).toFixed(0) }}%)</div> - </div> - </div> - </div> - </MkContainer> - - <MkContainer :foldable="false" class="_gap"> - <template #header><Fa :icon="faHdd"/> {{ $ts.disk }}</template> - <!-- - <template #func> - <button class="_button" @click="resume" :disabled="!paused"><Fa :icon="faPlay"/></button> - <button class="_button" @click="pause" :disabled="paused"><Fa :icon="faPause"/></button> - </template> - --> - - <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> - <canvas :ref="disk"></canvas> - </div> - <div class="_content" v-if="serverInfo"> - <div class="_table"> - <div class="_row"> - <div class="_cell"><div class="_label">Disk total</div>{{ bytes(serverInfo.fs.total) }}</div> - <div class="_cell"><div class="_label">Disk used</div>{{ bytes(serverInfo.fs.used) }} ({{ (serverInfo.fs.used / serverInfo.fs.total * 100).toFixed(0) }}%)</div> - <div class="_cell"><div class="_label">Disk free</div>{{ bytes(serverInfo.fs.total - serverInfo.fs.used) }} ({{ ((serverInfo.fs.total - serverInfo.fs.used) / serverInfo.fs.total * 100).toFixed(0) }}%)</div> - </div> - </div> - </div> - </MkContainer> - - <MkContainer :foldable="false" class="_gap"> - <template #header><Fa :icon="faExchangeAlt"/> {{ $ts.network }}</template> - <!-- - <template #func> - <button class="_button" @click="resume" :disabled="!paused"><Fa :icon="faPlay"/></button> - <button class="_button" @click="pause" :disabled="paused"><Fa :icon="faPause"/></button> - </template> - --> - - <div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> - <canvas :ref="net"></canvas> - </div> - <div class="_content" v-if="serverInfo"> - <div class="_table"> - <div class="_row"> - <div class="_cell"><div class="_label">Interface</div>{{ serverInfo.net.interface }}</div> - </div> - </div> - </div> - </MkContainer> +<div class="_formItem"> + <div class="_formLabel"><i class="fas fa-microchip"></i> {{ $ts.cpuAndMemory }}</div> + <div class="_formPanel xhexznfu"> + <div> + <canvas :ref="cpumem"></canvas> + </div> + <div v-if="serverInfo"> + <div class="_table"> + <div class="_row"> + <div class="_cell"><div class="_label">MEM total</div>{{ bytes(serverInfo.mem.total) }}</div> + <div class="_cell"><div class="_label">MEM used</div>{{ bytes(memUsage) }} ({{ (memUsage / serverInfo.mem.total * 100).toFixed(0) }}%)</div> + <div class="_cell"><div class="_label">MEM free</div>{{ bytes(serverInfo.mem.total - memUsage) }} ({{ ((serverInfo.mem.total - memUsage) / serverInfo.mem.total * 100).toFixed(0) }}%)</div> + </div> </div> </div> - </MkFolder> - - <MkFolder> - <template #header><Fa :icon="faClipboardList"/> {{ $ts.jobQueue }}</template> - - <div class="vkyrmkwb" :style="{ gridTemplateRows: queueHeight }"> - <MkContainer :foldable="false" :scrollable="true" :resize-base-el="() => $el"> - <template #header><Fa :icon="faExclamationTriangle"/> {{ $ts.delayed }}</template> - - <div class="_content"> - <div class="_keyValue" v-for="job in jobs" :key="job[0]"> - <button class="_button" @click="showInstanceInfo(job[0])">{{ job[0] }}</button> - <div style="text-align: right;">{{ number(job[1]) }} jobs</div> - </div> + </div> +</div> +<div class="_formItem"> + <div class="_formLabel"><i class="fas fa-hdd"></i> {{ $ts.disk }}</div> + <div class="_formPanel xhexznfu"> + <div> + <canvas :ref="disk"></canvas> + </div> + <div v-if="serverInfo"> + <div class="_table"> + <div class="_row"> + <div class="_cell"><div class="_label">Disk total</div>{{ bytes(serverInfo.fs.total) }}</div> + <div class="_cell"><div class="_label">Disk used</div>{{ bytes(serverInfo.fs.used) }} ({{ (serverInfo.fs.used / serverInfo.fs.total * 100).toFixed(0) }}%)</div> + <div class="_cell"><div class="_label">Disk free</div>{{ bytes(serverInfo.fs.total - serverInfo.fs.used) }} ({{ ((serverInfo.fs.total - serverInfo.fs.used) / serverInfo.fs.total * 100).toFixed(0) }}%)</div> </div> - </MkContainer> - <XQueue :connection="queueConnection" domain="inbox" ref="queue" class="queue"> - <template #title><Fa :icon="faExchangeAlt"/> In</template> - </XQueue> - <XQueue :connection="queueConnection" domain="deliver" class="queue"> - <template #title><Fa :icon="faExchangeAlt"/> Out</template> - </XQueue> + </div> + </div> + </div> +</div> +<div class="_formItem"> + <div class="_formLabel"><i class="fas fa-exchange-alt"></i> {{ $ts.network }}</div> + <div class="_formPanel xhexznfu"> + <div> + <canvas :ref="net"></canvas> + </div> + <div v-if="serverInfo"> + <div class="_table"> + <div class="_row"> + <div class="_cell"><div class="_label">Interface</div>{{ serverInfo.net.interface }}</div> + </div> + </div> </div> - </MkFolder> + </div> </div> </template> <script lang="ts"> import { defineComponent, markRaw } from 'vue'; -import { faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList } from '@fortawesome/free-solid-svg-icons'; import Chart from 'chart.js'; import MkButton from '@client/components/ui/button.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -153,7 +103,6 @@ export default defineComponent({ overviewHeight: '1fr', queueHeight: '1fr', paused: false, - faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList, } }, @@ -190,9 +139,11 @@ export default defineComponent({ }, beforeUnmount() { - this.connection.off('stats', this.onStats); - this.connection.off('statsLog', this.onStatsLog); - this.connection.dispose(); + if (this.connection) { + this.connection.off('stats', this.onStats); + this.connection.off('statsLog', this.onStatsLog); + this.connection.dispose(); + } this.queueConnection.dispose(); }, @@ -234,9 +185,9 @@ export default defineComponent({ aspectRatio: 3, layout: { padding: { - left: 0, - right: 0, - top: 8, + left: 16, + right: 16, + top: 16, bottom: 0 } }, @@ -306,9 +257,9 @@ export default defineComponent({ aspectRatio: 3, layout: { padding: { - left: 0, - right: 0, - top: 8, + left: 16, + right: 16, + top: 16, bottom: 0 } }, @@ -377,9 +328,9 @@ export default defineComponent({ aspectRatio: 3, layout: { padding: { - left: 0, - right: 0, - top: 8, + left: 16, + right: 16, + top: 16, bottom: 0 } }, @@ -496,81 +447,9 @@ export default defineComponent({ <style lang="scss" scoped> .xhexznfu { - &.min-width_1000px { - .sboqnrfi { - display: grid; - grid-template-columns: 3.2fr 1fr; - grid-template-rows: 1fr; - gap: 16px 16px; - - > .stats { - height: min-content; - } - - > .column { - display: flex; - flex-direction: column; - - > .info { - flex-shrink: 0; - flex-grow: 0; - } - - > .db { - flex: 1; - flex-grow: 0; - height: 100%; - } - - > .fed { - flex: 1; - flex-grow: 0; - height: 100%; - } - - > *:not(:last-child) { - margin-bottom: var(--margin); - } - } - } - - .segusily { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-template-rows: 1fr; - gap: 16px 16px; - padding: 0 16px; - } - - .vkyrmkwb { - display: grid; - grid-template-columns: 0.5fr 1fr 1fr; - grid-template-rows: 1fr; - gap: 16px 16px; - margin-bottom: var(--margin); - - > .queue { - height: min-content; - } - - > * { - margin-bottom: 0; - } - } - - .uwuemslx { - display: grid; - grid-template-columns: 2fr 3fr; - grid-template-rows: 1fr; - gap: 16px 16px; - height: 400px; - } - } - - .vkyrmkwb { - > * { - margin-bottom: var(--margin); - } + > div:nth-child(2) { + padding: 16px; + border-top: solid 0.5px var(--divider); } } </style> diff --git a/src/client/pages/instance/object-storage.vue b/src/client/pages/instance/object-storage.vue new file mode 100644 index 0000000000..814aeb6e48 --- /dev/null +++ b/src/client/pages/instance/object-storage.vue @@ -0,0 +1,154 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormSwitch v-model:value="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch> + + <template v-if="useObjectStorage"> + <FormInput v-model:value="objectStorageBaseUrl"> + <span>{{ $ts.objectStorageBaseUrl }}</span> + <template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template> + </FormInput> + + <FormInput v-model:value="objectStorageBucket"> + <span>{{ $ts.objectStorageBucket }}</span> + <template #desc>{{ $ts.objectStorageBucketDesc }}</template> + </FormInput> + + <FormInput v-model:value="objectStoragePrefix"> + <span>{{ $ts.objectStoragePrefix }}</span> + <template #desc>{{ $ts.objectStoragePrefixDesc }}</template> + </FormInput> + + <FormInput v-model:value="objectStorageEndpoint"> + <span>{{ $ts.objectStorageEndpoint }}</span> + <template #desc>{{ $ts.objectStorageEndpointDesc }}</template> + </FormInput> + + <FormInput v-model:value="objectStorageRegion"> + <span>{{ $ts.objectStorageRegion }}</span> + <template #desc>{{ $ts.objectStorageRegionDesc }}</template> + </FormInput> + + <FormInput v-model:value="objectStorageAccessKey"> + <template #prefix><i class="fas fa-key"></i></template> + <span>Access key</span> + </FormInput> + + <FormInput v-model:value="objectStorageSecretKey"> + <template #prefix><i class="fas fa-key"></i></template> + <span>Secret key</span> + </FormInput> + + <FormSwitch v-model:value="objectStorageUseSSL"> + {{ $ts.objectStorageUseSSL }} + <template #desc>{{ $ts.objectStorageUseSSLDesc }}</template> + </FormSwitch> + + <FormSwitch v-model:value="objectStorageUseProxy"> + {{ $ts.objectStorageUseProxy }} + <template #desc>{{ $ts.objectStorageUseProxyDesc }}</template> + </FormSwitch> + + <FormSwitch v-model:value="objectStorageSetPublicRead"> + {{ $ts.objectStorageSetPublicRead }} + </FormSwitch> + + <FormSwitch v-model:value="objectStorageS3ForcePathStyle"> + s3ForcePathStyle + </FormSwitch> + </template> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormGroup, + FormButton, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.objectStorage, + icon: 'fas fa-cloud' + }, + useObjectStorage: false, + objectStorageBaseUrl: null, + objectStorageBucket: null, + objectStoragePrefix: null, + objectStorageEndpoint: null, + objectStorageRegion: null, + objectStoragePort: null, + objectStorageAccessKey: null, + objectStorageSecretKey: null, + objectStorageUseSSL: false, + objectStorageUseProxy: false, + objectStorageSetPublicRead: false, + objectStorageS3ForcePathStyle: true, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.useObjectStorage = meta.useObjectStorage; + this.objectStorageBaseUrl = meta.objectStorageBaseUrl; + this.objectStorageBucket = meta.objectStorageBucket; + this.objectStoragePrefix = meta.objectStoragePrefix; + this.objectStorageEndpoint = meta.objectStorageEndpoint; + this.objectStorageRegion = meta.objectStorageRegion; + this.objectStoragePort = meta.objectStoragePort; + this.objectStorageAccessKey = meta.objectStorageAccessKey; + this.objectStorageSecretKey = meta.objectStorageSecretKey; + this.objectStorageUseSSL = meta.objectStorageUseSSL; + this.objectStorageUseProxy = meta.objectStorageUseProxy; + this.objectStorageSetPublicRead = meta.objectStorageSetPublicRead; + this.objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle; + }, + save() { + os.apiWithDialog('admin/update-meta', { + useObjectStorage: this.useObjectStorage, + objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null, + objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null, + objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null, + objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null, + objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null, + objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null, + objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null, + objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null, + objectStorageUseSSL: this.objectStorageUseSSL, + objectStorageUseProxy: this.objectStorageUseProxy, + objectStorageSetPublicRead: this.objectStorageSetPublicRead, + objectStorageS3ForcePathStyle: this.objectStorageS3ForcePathStyle, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/other-settings.vue b/src/client/pages/instance/other-settings.vue new file mode 100644 index 0000000000..b3954149a8 --- /dev/null +++ b/src/client/pages/instance/other-settings.vue @@ -0,0 +1,68 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormGroup> + <FormInput v-model:value="summalyProxy"> + <template #prefix><i class="fas fa-link"></i></template> + Summaly Proxy URL + </FormInput> + </FormGroup> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormGroup, + FormButton, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.other, + icon: 'fas fa-cogs' + }, + summalyProxy: '', + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.summalyProxy = meta.summalyProxy; + }, + save() { + os.apiWithDialog('admin/update-meta', { + summalyProxy: this.summalyProxy, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/overview.vue b/src/client/pages/instance/overview.vue new file mode 100644 index 0000000000..dca2529e1b --- /dev/null +++ b/src/client/pages/instance/overview.vue @@ -0,0 +1,135 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormInfo v-if="noMaintainerInformation" warn>{{ $ts.noMaintainerInformationWarning }} <MkA to="/instance/settings" class="_link">{{ $ts.configure }}</MkA></FormInfo> + <FormInfo v-if="noBotProtection" warn>{{ $ts.noBotProtectionWarning }} <MkA to="/instance/bot-protection" class="_link">{{ $ts.configure }}</MkA></FormInfo> + + <FormSuspense :p="fetchStats" v-slot="{ result: stats }"> + <FormGroup> + <FormKeyValueView> + <template #key>Users</template> + <template #value>{{ number(stats.originalUsersCount) }}</template> + </FormKeyValueView> + <FormKeyValueView> + <template #key>Notes</template> + <template #value>{{ number(stats.originalNotesCount) }}</template> + </FormKeyValueView> + </FormGroup> + </FormSuspense> + + <div class="_formItem"> + <div class="_formPanel"> + <MkInstanceStats :chart-limit="300" :detailed="true"/> + </div> + </div> + + <XMetrics/> + + <FormSuspense :p="fetchServerInfo" v-slot="{ result: serverInfo }"> + <FormGroup> + <FormKeyValueView> + <template #key>Node.js</template> + <template #value>{{ serverInfo.node }}</template> + </FormKeyValueView> + <FormKeyValueView> + <template #key>PostgreSQL</template> + <template #value>{{ serverInfo.psql }}</template> + </FormKeyValueView> + <FormKeyValueView> + <template #key>Redis</template> + <template #value>{{ serverInfo.redis }}</template> + </FormKeyValueView> + </FormGroup> + </FormSuspense> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { computed, defineComponent, markRaw } from 'vue'; +import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormTextarea from '@client/components/form/textarea.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import MkInstanceStats from '@client/components/instance-stats.vue'; +import MkButton from '@client/components/ui/button.vue'; +import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/ui/input.vue'; +import MkContainer from '@client/components/ui/container.vue'; +import MkFolder from '@client/components/ui/folder.vue'; +import { version, url } from '@client/config'; +import bytes from '../../filters/bytes'; +import number from '../../filters/number'; +import MkInstanceInfo from './instance.vue'; +import XMetrics from './metrics.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; + +export default defineComponent({ + components: { + FormBase, + FormSuspense, + FormGroup, + FormInfo, + FormKeyValueView, + MkInstanceStats, + XMetrics, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.overview, + icon: 'fas fa-tachometer-alt' + }, + page: 'index', + version, + url, + stats: null, + fetchStats: () => os.api('stats', {}), + fetchServerInfo: () => os.api('admin/server-info', {}), + fetchJobs: () => os.api('admin/queue/deliver-delayed', {}), + fetchModLogs: () => os.api('admin/show-moderation-logs', {}), + noMaintainerInformation: false, + noBotProtection: false, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + this.meta = await os.api('meta', { detail: true }); + + const isEmpty = (x: any) => x == null || x == ''; + + this.noMaintainerInformation = isEmpty(this.meta.maintainerName) || isEmpty(this.meta.maintainerEmail); + this.noBotProtection = !this.meta.enableHcaptcha && !this.meta.enableRecaptcha; + }, + + async showInstanceInfo(q) { + let instance = q; + if (typeof q === 'string') { + instance = await os.api('federation/show-instance', { + host: q + }); + } + os.popup(MkInstanceInfo, { + instance: instance + }, {}, 'closed'); + }, + + bytes, + + number, + } +}); +</script> diff --git a/src/client/pages/instance/proxy-account.vue b/src/client/pages/instance/proxy-account.vue new file mode 100644 index 0000000000..3e2df8dcb4 --- /dev/null +++ b/src/client/pages/instance/proxy-account.vue @@ -0,0 +1,86 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormGroup> + <FormKeyValueView> + <template #key>{{ $ts.proxyAccount }}</template> + <template #value>{{ proxyAccount ? `@${proxyAccount.username}` : $ts.none }}</template> + </FormKeyValueView> + <template #caption>{{ $ts.proxyAccountDescription }}</template> + </FormGroup> + + <FormButton @click="chooseProxyAccount" primary>{{ $ts.selectAccount }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormTextarea from '@client/components/form/textarea.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormKeyValueView, + FormInput, + FormBase, + FormGroup, + FormButton, + FormTextarea, + FormInfo, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.proxyAccount, + icon: 'fas fa-ghost' + }, + proxyAccount: null, + proxyAccountId: null, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.proxyAccountId = meta.proxyAccountId; + if (this.proxyAccountId) { + this.proxyAccount = await os.api('users/show', { userId: this.proxyAccountId }); + } + }, + + chooseProxyAccount() { + os.selectUser().then(user => { + this.proxyAccount = user; + this.proxyAccountId = user.id; + this.save(); + }); + }, + + save() { + os.apiWithDialog('admin/update-meta', { + proxyAccountId: this.proxyAccountId, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/queue.chart.vue b/src/client/pages/instance/queue.chart.vue index 0eb70debfb..446c979209 100644 --- a/src/client/pages/instance/queue.chart.vue +++ b/src/client/pages/instance/queue.chart.vue @@ -1,27 +1,29 @@ <template> -<section class="_section"> - <div class="_title"><slot name="title"></slot></div> - <div class="_content _table"> - <div class="_row"> - <div class="_cell"><div class="_label">Process</div>{{ number(activeSincePrevTick) }}</div> - <div class="_cell"><div class="_label">Active</div>{{ number(active) }}</div> - <div class="_cell"><div class="_label">Waiting</div>{{ number(waiting) }}</div> - <div class="_cell"><div class="_label">Delayed</div>{{ number(delayed) }}</div> +<div class="_formItem"> + <div class="_formLabel"><slot name="title"></slot></div> + <div class="_formPanel pumxzjhg"> + <div class="_table status"> + <div class="_row"> + <div class="_cell"><div class="_label">Process</div>{{ number(activeSincePrevTick) }}</div> + <div class="_cell"><div class="_label">Active</div>{{ number(active) }}</div> + <div class="_cell"><div class="_label">Waiting</div>{{ number(waiting) }}</div> + <div class="_cell"><div class="_label">Delayed</div>{{ number(delayed) }}</div> + </div> </div> - </div> - <div class="_content" style="margin-bottom: -8px;"> - <canvas ref="chart"></canvas> - </div> - <div class="_content" style="max-height: 180px; overflow: auto;"> - <div v-if="jobs.length > 0"> - <div v-for="job in jobs" :key="job[0]"> - <span>{{ job[0] }}</span> - <span style="margin-left: 8px; opacity: 0.7;">({{ number(job[1]) }} jobs)</span> + <div class=""> + <canvas ref="chart"></canvas> + </div> + <div class="jobs"> + <div v-if="jobs.length > 0"> + <div v-for="job in jobs" :key="job[0]"> + <span>{{ job[0] }}</span> + <span style="margin-left: 8px; opacity: 0.7;">({{ number(job[1]) }} jobs)</span> + </div> </div> + <span v-else style="opacity: 0.5;">{{ $ts.noJobs }}</span> </div> - <span v-else style="opacity: 0.5;">{{ $ts.noJobs }}</span> </div> -</section> +</div> </template> <script lang="ts"> @@ -110,10 +112,10 @@ export default defineComponent({ aspectRatio: 3, layout: { padding: { - left: 0, - right: 0, - top: 8, - bottom: 0 + left: 16, + right: 16, + top: 16, + bottom: 12 } }, legend: { @@ -198,3 +200,19 @@ export default defineComponent({ } }); </script> + +<style lang="scss" scoped> +.pumxzjhg { + > .status { + padding: 16px; + border-bottom: solid 0.5px var(--divider); + } + + > .jobs { + padding: 16px; + border-top: solid 0.5px var(--divider); + max-height: 180px; + overflow: auto; + } +} +</style> diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue index 249babcf41..2dccf48d31 100644 --- a/src/client/pages/instance/queue.vue +++ b/src/client/pages/instance/queue.vue @@ -1,46 +1,47 @@ <template> -<div> +<FormBase> <XQueue :connection="connection" domain="inbox"> - <template #title><Fa :icon="faExchangeAlt"/> In</template> + <template #title>In</template> </XQueue> <XQueue :connection="connection" domain="deliver"> - <template #title><Fa :icon="faExchangeAlt"/> Out</template> + <template #title>Out</template> </XQueue> - <section class="_section"> - <div class="_content"> - <MkButton @click="clear()"><Fa :icon="faTrashAlt"/> {{ $ts.clearQueue }}</MkButton> - </div> - </section> -</div> + <FormButton @click="clear()" danger><i class="fas fa-trash-alt"></i> {{ $ts.clearQueue }}</FormButton> +</FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faExchangeAlt } from '@fortawesome/free-solid-svg-icons'; -import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import XQueue from './queue.chart.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormButton from '@client/components/form/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; export default defineComponent({ components: { + FormBase, + FormButton, MkButton, XQueue, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.jobQueue, - icon: faExchangeAlt, + icon: 'fas fa-clipboard-list', }, connection: os.stream.useSharedConnection('queueStats'), - faExchangeAlt, faTrashAlt } }, mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + this.$nextTick(() => { this.connection.send('requestLog', { id: Math.random().toString().substr(2, 8), diff --git a/src/client/pages/instance/relays.vue b/src/client/pages/instance/relays.vue index d97a07cbb2..a3e4e7d1da 100644 --- a/src/client/pages/instance/relays.vue +++ b/src/client/pages/instance/relays.vue @@ -1,50 +1,44 @@ <template> -<div class="relaycxt"> - <section class="_section add"> - <div class="_title"><Fa :icon="faPlus"/> {{ $ts.addRelay }}</div> - <div class="_content"> - <MkInput v-model:value="inbox"> - <span>{{ $ts.inboxUrl }}</span> - </MkInput> - <MkButton @click="add(inbox)" primary><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton> - </div> - </section> +<FormBase class="relaycxt"> + <FormButton @click="addRelay" primary><i class="fas fa-plus"></i> {{ $ts.addRelay }}</FormButton> - <section class="_section relays"> - <div class="_title"><Fa :icon="faProjectDiagram"/> {{ $ts.addedRelays }}</div> - <div class="_content relay" v-for="relay in relays" :key="relay.inbox"> + <div class="_formItem" v-for="relay in relays" :key="relay.inbox"> + <div class="_formPanel" style="padding: 16px;"> <div>{{ relay.inbox }}</div> <div>{{ $t(`_relayStatus.${relay.status}`) }}</div> - <MkButton class="button" inline @click="remove(relay.inbox)"><Fa :icon="faTrashAlt"/> {{ $ts.remove }}</MkButton> + <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> </div> - </section> -</div> + </div> +</FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faPlus, faProjectDiagram } from '@fortawesome/free-solid-svg-icons'; -import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormButton from '@client/components/form/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; export default defineComponent({ components: { + FormBase, + FormButton, MkButton, MkInput, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.relays, - icon: faProjectDiagram, + icon: 'fas fa-globe', }, relays: [], inbox: '', - faPlus, faProjectDiagram, faSave, faTrashAlt } }, @@ -52,8 +46,19 @@ export default defineComponent({ this.refresh(); }, + mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + methods: { - add(inbox: string) { + async addRelay() { + const { canceled, result: inbox } = await os.dialog({ + title: this.$ts.addRelay, + input: { + placeholder: this.$ts.inboxUrl + } + }); + if (canceled) return; os.api('admin/relays/add', { inbox }).then((relay: any) => { @@ -89,9 +94,5 @@ export default defineComponent({ </script> <style lang="scss" scoped> -._content.relay { - div { - margin: 0.5em 0; - } -} + </style> diff --git a/src/client/pages/instance/security.vue b/src/client/pages/instance/security.vue new file mode 100644 index 0000000000..e3397a113b --- /dev/null +++ b/src/client/pages/instance/security.vue @@ -0,0 +1,77 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormLink to="/instance/bot-protection"> + <i class="fas fa-shield-alt"></i> {{ $ts.botProtection }} + <template #suffix v-if="enableHcaptcha">hCaptcha</template> + <template #suffix v-else-if="enableRecaptcha">reCAPTCHA</template> + <template #suffix v-else>{{ $ts.none }} ({{ $ts.notRecommended }})</template> + </FormLink> + + <FormSwitch v-model:value="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineAsyncComponent, defineComponent } from 'vue'; +import FormLink from '@client/components/form/link.vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormLink, + FormSwitch, + FormBase, + FormGroup, + FormButton, + FormInfo, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.security, + icon: 'fas fa-lock' + }, + enableHcaptcha: false, + enableRecaptcha: false, + enableRegistration: false, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableHcaptcha = meta.enableHcaptcha; + this.enableRecaptcha = meta.enableRecaptcha; + this.enableRegistration = !meta.disableRegistration; + }, + + save() { + os.apiWithDialog('admin/update-meta', { + disableRegistration: !this.enableRegistration, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/service-worker.vue b/src/client/pages/instance/service-worker.vue new file mode 100644 index 0000000000..a52932bb75 --- /dev/null +++ b/src/client/pages/instance/service-worker.vue @@ -0,0 +1,84 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormSwitch v-model:value="enableServiceWorker"> + {{ $ts.enableServiceworker }} + <template #desc>{{ $ts.serviceworkerInfo }}</template> + </FormSwitch> + + <template v-if="enableServiceWorker"> + <FormInput v-model:value="swPublicKey"> + <template #prefix><i class="fas fa-key"></i></template> + Public key + </FormInput> + + <FormInput v-model:value="swPrivateKey"> + <template #prefix><i class="fas fa-key"></i></template> + Private key + </FormInput> + </template> + + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; + +export default defineComponent({ + components: { + FormSwitch, + FormInput, + FormBase, + FormGroup, + FormButton, + FormSuspense, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: 'ServiceWorker', + icon: 'fas fa-bolt' + }, + enableServiceWorker: false, + swPublicKey: null, + swPrivateKey: null, + } + }, + + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + async init() { + const meta = await os.api('meta', { detail: true }); + this.enableServiceWorker = meta.enableServiceWorker; + this.swPublicKey = meta.swPublickey; + this.swPrivateKey = meta.swPrivateKey; + }, + save() { + os.apiWithDialog('admin/update-meta', { + enableServiceWorker: this.enableServiceWorker, + swPublicKey: this.swPublicKey, + swPrivateKey: this.swPrivateKey, + }).then(() => { + fetchInstance(); + }); + } + } +}); +</script> diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue index 038ecfd5ac..66f01c42c7 100644 --- a/src/client/pages/instance/settings.vue +++ b/src/client/pages/instance/settings.vue @@ -1,585 +1,132 @@ <template> -<div v-if="meta" class="_section"> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faInfoCircle"/> {{ $ts.basicInfo }}</div> - <div class="_content"> - <MkInput v-model:value="name">{{ $ts.instanceName }}</MkInput> - <MkTextarea v-model:value="description">{{ $ts.instanceDescription }}</MkTextarea> - <MkInput v-model:value="iconUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.iconUrl }}</MkInput> - <MkInput v-model:value="bannerUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.bannerUrl }}</MkInput> - <MkInput v-model:value="backgroundImageUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.backgroundImageUrl }}</MkInput> - <MkInput v-model:value="logoImageUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.logoImageUrl }}</MkInput> - <MkInput v-model:value="tosUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.tosUrl }}</MkInput> - <MkInput v-model:value="maintainerName">{{ $ts.maintainerName }}</MkInput> - <MkInput v-model:value="maintainerEmail" type="email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $ts.maintainerEmail }}</MkInput> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> +<FormBase> + <FormSuspense :p="init"> + <FormInput v-model:value="name"> + <span>{{ $ts.instanceName }}</span> + </FormInput> - <MkInput v-model:value="pinnedClipId">{{ $ts.pinnedClipId }}</MkInput> + <FormTextarea v-model:value="description"> + <span>{{ $ts.instanceDescription }}</span> + </FormTextarea> - <section class="_card _gap"> - <div class="_content"> - <MkInput v-model:value="maxNoteTextLength" type="number" :save="() => save()"><template #icon><Fa :icon="faPencilAlt"/></template>{{ $ts.maxNoteTextLength }}</MkInput> - </div> - <div class="_content"> - <MkSwitch v-model:value="enableLocalTimeline" @update:value="save()">{{ $ts.enableLocalTimeline }}</MkSwitch> - <MkSwitch v-model:value="enableGlobalTimeline" @update:value="save()">{{ $ts.enableGlobalTimeline }}</MkSwitch> - <MkInfo>{{ $ts.disablingTimelinesInfo }}</MkInfo> - </div> - <div class="_content"> - <MkSwitch v-model:value="useStarForReactionFallback" @update:value="save()">{{ $ts.useStarForReactionFallback }}</MkSwitch> - </div> - </section> + <FormInput v-model:value="iconUrl"> + <template #prefix><i class="fas fa-link"></i></template> + <span>{{ $ts.iconUrl }}</span> + </FormInput> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faUser"/> {{ $ts.registration }}</div> - <div class="_content"> - <MkSwitch v-model:value="enableRegistration" @update:value="save()">{{ $ts.enableRegistration }}</MkSwitch> - <MkButton v-if="!enableRegistration" @click="invite">{{ $ts.invite }}</MkButton> - </div> - </section> + <FormInput v-model:value="bannerUrl"> + <template #prefix><i class="fas fa-link"></i></template> + <span>{{ $ts.bannerUrl }}</span> + </FormInput> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faShieldAlt"/> {{ $ts.hcaptcha }}</div> - <div class="_content"> - <MkSwitch v-model:value="enableHcaptcha">{{ $ts.enableHcaptcha }}</MkSwitch> - <template v-if="enableHcaptcha"> - <MkInput v-model:value="hcaptchaSiteKey" :disabled="!enableHcaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.hcaptchaSiteKey }}</MkInput> - <MkInput v-model:value="hcaptchaSecretKey" :disabled="!enableHcaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.hcaptchaSecretKey }}</MkInput> - </template> - </div> - <div class="_content" v-if="enableHcaptcha"> - <header>{{ $ts.preview }}</header> - <captcha v-if="enableHcaptcha" provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> + <FormInput v-model:value="tosUrl"> + <template #prefix><i class="fas fa-link"></i></template> + <span>{{ $ts.tosUrl }}</span> + </FormInput> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faShieldAlt"/> {{ $ts.recaptcha }}</div> - <div class="_content"> - <MkSwitch v-model:value="enableRecaptcha" ref="enableRecaptcha">{{ $ts.enableRecaptcha }}</MkSwitch> - <template v-if="enableRecaptcha"> - <MkInput v-model:value="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.recaptchaSiteKey }}</MkInput> - <MkInput v-model:value="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.recaptchaSecretKey }}</MkInput> - </template> - </div> - <div class="_content" v-if="enableRecaptcha && recaptchaSiteKey"> - <header>{{ $ts.preview }}</header> - <captcha v-if="enableRecaptcha" provider="grecaptcha" :sitekey="recaptchaSiteKey"/> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> + <FormInput v-model:value="maintainerName"> + <span>{{ $ts.maintainerName }}</span> + </FormInput> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faEnvelope" /> {{ $ts.emailConfig }}</div> - <div class="_content"> - <MkSwitch v-model:value="enableEmail" @update:value="save()">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></MkSwitch> - <MkInput v-model:value="email" type="email" :disabled="!enableEmail">{{ $ts.email }}</MkInput> - <div><b>{{ $ts.smtpConfig }}</b></div> - <div class="_inputs"> - <MkInput v-model:value="smtpHost" :disabled="!enableEmail">{{ $ts.smtpHost }}</MkInput> - <MkInput v-model:value="smtpPort" type="number" :disabled="!enableEmail">{{ $ts.smtpPort }}</MkInput> - </div> - <div class="_inputs"> - <MkInput v-model:value="smtpUser" :disabled="!enableEmail">{{ $ts.smtpUser }}</MkInput> - <MkInput v-model:value="smtpPass" type="password" :disabled="!enableEmail">{{ $ts.smtpPass }}</MkInput> - </div> - <MkInfo>{{ $ts.emptyToDisableSmtpAuth }}</MkInfo> - <MkSwitch v-model:value="smtpSecure" :disabled="!enableEmail">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></MkSwitch> - <div> - <MkButton :disabled="!enableEmail" primary inline @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - <MkButton :disabled="!enableEmail" inline @click="testEmail()">{{ $ts.testEmail }}</MkButton> - </div> - </div> - </section> + <FormInput v-model:value="maintainerEmail" type="email"> + <template #prefix><i class="fas fa-envelope"></i></template> + <span>{{ $ts.maintainerEmail }}</span> + </FormInput> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faBolt"/> {{ $ts.serviceworker }}</div> - <div class="_content"> - <MkSwitch v-model:value="enableServiceWorker">{{ $ts.enableServiceworker }}<template #desc>{{ $ts.serviceworkerInfo }}</template></MkSwitch> - <template v-if="enableServiceWorker"> - <div class="_inputs"> - <MkInput v-model:value="swPublicKey" :disabled="!enableServiceWorker"><template #icon><Fa :icon="faKey"/></template>Public key</MkInput> - <MkInput v-model:value="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><Fa :icon="faKey"/></template>Private key</MkInput> - </div> - </template> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> + <FormInput v-model:value="maxNoteTextLength" type="number"> + <template #prefix><i class="fas fa-pencil-alt"></i></template> + <span>{{ $ts.maxNoteTextLength }}</span> + </FormInput> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faThumbtack"/> {{ $ts.pinnedUsers }}</div> - <div class="_content"> - <MkTextarea v-model:value="pinnedUsers"> - <template #desc>{{ $ts.pinnedUsersDescription }} <button class="_textButton" @click="addPinUser">{{ $ts.addUser }}</button></template> - </MkTextarea> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> + <FormSwitch v-model:value="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch> + <FormSwitch v-model:value="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch> + <FormInfo>{{ $ts.disablingTimelinesInfo }}</FormInfo> - <section class="_card _gap"> - <div class="_title"><Fa :icon="faThumbtack"/> {{ $ts.pinnedPages }}</div> - <div class="_content"> - <MkTextarea v-model:value="pinnedPages"> - <template #desc>{{ $ts.pinnedPagesDescription }}</template> - </MkTextarea> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> - - <section class="_card _gap"> - <div class="_title"><Fa :icon="faCloud"/> {{ $ts.files }}</div> - <div class="_content"> - <MkSwitch v-model:value="cacheRemoteFiles">{{ $ts.cacheRemoteFiles }}<template #desc>{{ $ts.cacheRemoteFilesDescription }}</template></MkSwitch> - <MkSwitch v-model:value="proxyRemoteFiles">{{ $ts.proxyRemoteFiles }}<template #desc>{{ $ts.proxyRemoteFilesDescription }}</template></MkSwitch> - <MkInput v-model:value="localDriveCapacityMb" type="number">{{ $ts.driveCapacityPerLocalAccount }}<template #suffix>MB</template><template #desc>{{ $ts.inMb }}</template></MkInput> - <MkInput v-model:value="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $ts.driveCapacityPerRemoteAccount }}<template #suffix>MB</template><template #desc>{{ $ts.inMb }}</template></MkInput> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> - - <section class="_card _gap"> - <div class="_title"><Fa :icon="faCloud"/> {{ $ts.objectStorage }}</div> - <div class="_content"> - <MkSwitch v-model:value="useObjectStorage">{{ $ts.useObjectStorage }}</MkSwitch> - <template v-if="useObjectStorage"> - <MkInput v-model:value="objectStorageBaseUrl" :disabled="!useObjectStorage">{{ $ts.objectStorageBaseUrl }}<template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template></MkInput> - <div class="_inputs"> - <MkInput v-model:value="objectStorageBucket" :disabled="!useObjectStorage">{{ $ts.objectStorageBucket }}<template #desc>{{ $ts.objectStorageBucketDesc }}</template></MkInput> - <MkInput v-model:value="objectStoragePrefix" :disabled="!useObjectStorage">{{ $ts.objectStoragePrefix }}<template #desc>{{ $ts.objectStoragePrefixDesc }}</template></MkInput> - </div> - <MkInput v-model:value="objectStorageEndpoint" :disabled="!useObjectStorage">{{ $ts.objectStorageEndpoint }}<template #desc>{{ $ts.objectStorageEndpointDesc }}</template></MkInput> - <div class="_inputs"> - <MkInput v-model:value="objectStorageRegion" :disabled="!useObjectStorage">{{ $ts.objectStorageRegion }}<template #desc>{{ $ts.objectStorageRegionDesc }}</template></MkInput> - </div> - <div class="_inputs"> - <MkInput v-model:value="objectStorageAccessKey" :disabled="!useObjectStorage"><template #icon><Fa :icon="faKey"/></template>Access key</MkInput> - <MkInput v-model:value="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><Fa :icon="faKey"/></template>Secret key</MkInput> - </div> - <MkSwitch v-model:value="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $ts.objectStorageUseSSL }}<template #desc>{{ $ts.objectStorageUseSSLDesc }}</template></MkSwitch> - <MkSwitch v-model:value="objectStorageUseProxy" :disabled="!useObjectStorage">{{ $ts.objectStorageUseProxy }}<template #desc>{{ $ts.objectStorageUseProxyDesc }}</template></MkSwitch> - <MkSwitch v-model:value="objectStorageSetPublicRead" :disabled="!useObjectStorage">{{ $ts.objectStorageSetPublicRead }}</MkSwitch> - <MkSwitch v-model:value="objectStorageS3ForcePathStyle" :disabled="!useObjectStorage">s3ForcePathStyle</MkSwitch> - </template> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> - - <section class="_card _gap"> - <div class="_title"><Fa :icon="faGhost"/> {{ $ts.proxyAccount }}</div> - <div class="_content"> - <MkInput :value="proxyAccount ? proxyAccount.username : null" disabled><template #prefix>@</template>{{ $ts.proxyAccount }}<template #desc>{{ $ts.proxyAccountDescription }}</template></MkInput> - <MkButton primary @click="chooseProxyAccount">{{ $ts.chooseProxyAccount }}</MkButton> - </div> - </section> - - <section class="_card _gap"> - <div class="_title"><Fa :icon="faBan"/> {{ $ts.blockedInstances }}</div> - <div class="_content"> - <MkTextarea v-model:value="blockedHosts"> - <template #desc>{{ $ts.blockedInstancesDescription }}</template> - </MkTextarea> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> - - <section class="_card _gap"> - <div class="_title"><Fa :icon="faShareAlt"/> {{ $ts.integration }}</div> - <div class="_content"> - <header><Fa :icon="faTwitter"/> Twitter</header> - <MkSwitch v-model:value="enableTwitterIntegration">{{ $ts.enable }}</MkSwitch> - <template v-if="enableTwitterIntegration"> - <MkInfo>Callback URL: {{ `${url}/api/tw/cb` }}</MkInfo> - <MkInput v-model:value="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><Fa :icon="faKey"/></template>Consumer Key</MkInput> - <MkInput v-model:value="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><template #icon><Fa :icon="faKey"/></template>Consumer Secret</MkInput> - </template> - </div> - <div class="_content"> - <header><Fa :icon="faGithub"/> GitHub</header> - <MkSwitch v-model:value="enableGithubIntegration">{{ $ts.enable }}</MkSwitch> - <template v-if="enableGithubIntegration"> - <MkInfo>Callback URL: {{ `${url}/api/gh/cb` }}</MkInfo> - <MkInput v-model:value="githubClientId" :disabled="!enableGithubIntegration"><template #icon><Fa :icon="faKey"/></template>Client ID</MkInput> - <MkInput v-model:value="githubClientSecret" :disabled="!enableGithubIntegration"><template #icon><Fa :icon="faKey"/></template>Client Secret</MkInput> - </template> - </div> - <div class="_content"> - <header><Fa :icon="faDiscord"/> Discord</header> - <MkSwitch v-model:value="enableDiscordIntegration">{{ $ts.enable }}</MkSwitch> - <template v-if="enableDiscordIntegration"> - <MkInfo>Callback URL: {{ `${url}/api/dc/cb` }}</MkInfo> - <MkInput v-model:value="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><Fa :icon="faKey"/></template>Client ID</MkInput> - <MkInput v-model:value="discordClientSecret" :disabled="!enableDiscordIntegration"><template #icon><Fa :icon="faKey"/></template>Client Secret</MkInput> - </template> - </div> - <div class="_footer"> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> - - <section class="_card _gap"> - <div class="_title"><Fa :icon="faArchway" /> Summaly Proxy</div> - <div class="_content"> - <MkInput v-model:value="summalyProxy">URL</MkInput> - <MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - </div> - </section> -</div> + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + </FormSuspense> +</FormBase> </template> <script lang="ts"> -import { defineComponent, defineAsyncComponent } from 'vue'; -import { faPencilAlt, faShareAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faThumbtack, faUser, faShieldAlt, faKey, faBolt, faArchway } from '@fortawesome/free-solid-svg-icons'; -import { faTrashAlt, faEnvelope } from '@fortawesome/free-regular-svg-icons'; -import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'; -import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import MkInfo from '@client/components/ui/info.vue'; -import { url } from '@client/config'; -import getAcct from '@/misc/acct/render'; +import { defineComponent } from 'vue'; +import FormSwitch from '@client/components/form/switch.vue'; +import FormInput from '@client/components/form/input.vue'; +import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormTextarea from '@client/components/form/textarea.vue'; +import FormInfo from '@client/components/form/info.vue'; +import FormSuspense from '@client/components/form/suspense.vue'; import * as os from '@client/os'; -import { fetchInstance } from '@client/instance'; import * as symbols from '@client/symbols'; +import { fetchInstance } from '@client/instance'; export default defineComponent({ components: { - MkButton, - MkInput, - MkTextarea, - MkSwitch, - MkInfo, - Captcha: defineAsyncComponent(() => import('@client/components/captcha.vue')), + FormSwitch, + FormInput, + FormBase, + FormGroup, + FormButton, + FormTextarea, + FormInfo, + FormSuspense, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { - title: this.$ts.instance, - icon: faCog, + title: this.$ts.general, + icon: 'fas fa-cog' }, - meta: null, - url, - proxyAccount: null, - proxyAccountId: null, - cacheRemoteFiles: false, - proxyRemoteFiles: false, - localDriveCapacityMb: 0, - remoteDriveCapacityMb: 0, - blockedHosts: '', - pinnedUsers: '', - pinnedPages: '', - pinnedClipId: null, - maintainerName: null, - maintainerEmail: null, name: null, description: null, tosUrl: null as string | null, - enableEmail: false, - email: null, - bannerUrl: null, + maintainerName: null, + maintainerEmail: null, iconUrl: null, - logoImageUrl: null, - backgroundImageUrl: null, + bannerUrl: null, maxNoteTextLength: 0, - enableRegistration: false, enableLocalTimeline: false, enableGlobalTimeline: false, - enableHcaptcha: false, - hcaptchaSiteKey: null, - hcaptchaSecretKey: null, - enableRecaptcha: false, - recaptchaSiteKey: null, - recaptchaSecretKey: null, - enableServiceWorker: false, - swPublicKey: null, - swPrivateKey: null, - useObjectStorage: false, - objectStorageBaseUrl: null, - objectStorageBucket: null, - objectStoragePrefix: null, - objectStorageEndpoint: null, - objectStorageRegion: null, - objectStoragePort: null, - objectStorageAccessKey: null, - objectStorageSecretKey: null, - objectStorageUseSSL: false, - objectStorageUseProxy: false, - objectStorageSetPublicRead: false, - objectStorageS3ForcePathStyle: true, - enableTwitterIntegration: false, - twitterConsumerKey: null, - twitterConsumerSecret: null, - enableGithubIntegration: false, - githubClientId: null, - githubClientSecret: null, - enableDiscordIntegration: false, - discordClientId: null, - discordClientSecret: null, - useStarForReactionFallback: false, - smtpSecure: false, - smtpHost: '', - smtpPort: 0, - smtpUser: '', - smtpPass: '', - summalyProxy: '', - faPencilAlt, faTwitter, faDiscord, faGithub, faShareAlt, faTrashAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faEnvelope, faThumbtack, faUser, faShieldAlt, faKey, faBolt, faArchway - } - }, - - async created() { - this.meta = await os.api('meta', { detail: true }); - - this.name = this.meta.name; - this.description = this.meta.description; - this.tosUrl = this.meta.tosUrl; - this.bannerUrl = this.meta.bannerUrl; - this.iconUrl = this.meta.iconUrl; - this.logoImageUrl = this.meta.logoImageUrl; - this.backgroundImageUrl = this.meta.backgroundImageUrl; - this.enableEmail = this.meta.enableEmail; - this.email = this.meta.email; - this.maintainerName = this.meta.maintainerName; - this.maintainerEmail = this.meta.maintainerEmail; - this.maxNoteTextLength = this.meta.maxNoteTextLength; - this.enableRegistration = !this.meta.disableRegistration; - this.enableLocalTimeline = !this.meta.disableLocalTimeline; - this.enableGlobalTimeline = !this.meta.disableGlobalTimeline; - this.enableHcaptcha = this.meta.enableHcaptcha; - this.hcaptchaSiteKey = this.meta.hcaptchaSiteKey; - this.hcaptchaSecretKey = this.meta.hcaptchaSecretKey; - this.enableRecaptcha = this.meta.enableRecaptcha; - this.recaptchaSiteKey = this.meta.recaptchaSiteKey; - this.recaptchaSecretKey = this.meta.recaptchaSecretKey; - this.proxyAccountId = this.meta.proxyAccountId; - this.cacheRemoteFiles = this.meta.cacheRemoteFiles; - this.proxyRemoteFiles = this.meta.proxyRemoteFiles; - this.localDriveCapacityMb = this.meta.driveCapacityPerLocalUserMb; - this.remoteDriveCapacityMb = this.meta.driveCapacityPerRemoteUserMb; - this.blockedHosts = this.meta.blockedHosts.join('\n'); - this.pinnedUsers = this.meta.pinnedUsers.join('\n'); - this.pinnedPages = this.meta.pinnedPages.join('\n'); - this.pinnedClipId = this.meta.pinnedClipId; - this.enableServiceWorker = this.meta.enableServiceWorker; - this.swPublicKey = this.meta.swPublickey; - this.swPrivateKey = this.meta.swPrivateKey; - this.useObjectStorage = this.meta.useObjectStorage; - this.objectStorageBaseUrl = this.meta.objectStorageBaseUrl; - this.objectStorageBucket = this.meta.objectStorageBucket; - this.objectStoragePrefix = this.meta.objectStoragePrefix; - this.objectStorageEndpoint = this.meta.objectStorageEndpoint; - this.objectStorageRegion = this.meta.objectStorageRegion; - this.objectStoragePort = this.meta.objectStoragePort; - this.objectStorageAccessKey = this.meta.objectStorageAccessKey; - this.objectStorageSecretKey = this.meta.objectStorageSecretKey; - this.objectStorageUseSSL = this.meta.objectStorageUseSSL; - this.objectStorageUseProxy = this.meta.objectStorageUseProxy; - this.objectStorageSetPublicRead = this.meta.objectStorageSetPublicRead; - this.objectStorageS3ForcePathStyle = this.meta.objectStorageS3ForcePathStyle; - this.enableTwitterIntegration = this.meta.enableTwitterIntegration; - this.twitterConsumerKey = this.meta.twitterConsumerKey; - this.twitterConsumerSecret = this.meta.twitterConsumerSecret; - this.enableGithubIntegration = this.meta.enableGithubIntegration; - this.githubClientId = this.meta.githubClientId; - this.githubClientSecret = this.meta.githubClientSecret; - this.enableDiscordIntegration = this.meta.enableDiscordIntegration; - this.discordClientId = this.meta.discordClientId; - this.discordClientSecret = this.meta.discordClientSecret; - this.useStarForReactionFallback = this.meta.useStarForReactionFallback; - this.smtpSecure = this.meta.smtpSecure; - this.smtpHost = this.meta.smtpHost; - this.smtpPort = this.meta.smtpPort; - this.smtpUser = this.meta.smtpUser; - this.smtpPass = this.meta.smtpPass; - this.summalyProxy = this.meta.summalyProxy; - - if (this.proxyAccountId) { - os.api('users/show', { userId: this.proxyAccountId }).then(proxyAccount => { - this.proxyAccount = proxyAccount; - }); } }, - mounted() { - this.$watch('enableHcaptcha', () => { - if (this.enableHcaptcha && this.enableRecaptcha) { - os.dialog({ - type: 'question', // warning だと間違って cancel するかもしれない - showCancelButton: true, - title: this.$ts.settingGuide, - text: this.$ts.avoidMultiCaptchaConfirm, - }).then(({ canceled }) => { - if (canceled) { - return; - } - - this.enableRecaptcha = false; - }); - } - }); - - this.$watch('enableRecaptcha', () => { - if (this.enableRecaptcha && this.enableHcaptcha) { - os.dialog({ - type: 'question', // warning だと間違って cancel するかもしれない - showCancelButton: true, - title: this.$ts.settingGuide, - text: this.$ts.avoidMultiCaptchaConfirm, - }).then(({ canceled }) => { - if (canceled) { - return; - } - - this.enableHcaptcha = false; - }); - } - }); + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); }, methods: { - invite() { - os.api('admin/invite').then(x => { - os.dialog({ - type: 'info', - text: x.code - }); - }).catch(e => { - os.dialog({ - type: 'error', - text: e - }); - }); - }, - - addPinUser() { - os.selectUser().then(user => { - this.pinnedUsers = this.pinnedUsers.trim(); - this.pinnedUsers += '\n@' + getAcct(user); - this.pinnedUsers = this.pinnedUsers.trim(); - }); + async init() { + const meta = await os.api('meta', { detail: true }); + this.name = meta.name; + this.description = meta.description; + this.tosUrl = meta.tosUrl; + this.iconUrl = meta.iconUrl; + this.bannerUrl = meta.bannerUrl; + this.maintainerName = meta.maintainerName; + this.maintainerEmail = meta.maintainerEmail; + this.maxNoteTextLength = meta.maxNoteTextLength; + this.enableLocalTimeline = !meta.disableLocalTimeline; + this.enableGlobalTimeline = !meta.disableGlobalTimeline; }, - chooseProxyAccount() { - os.selectUser().then(user => { - this.proxyAccount = user; - this.proxyAccountId = user.id; - this.save(true); - }); - }, - - async testEmail() { - os.api('admin/send-email', { - to: this.maintainerEmail, - subject: 'Test email', - text: 'Yo' - }).then(x => { - os.dialog({ - type: 'success', - splash: true - }); - }).catch(e => { - os.dialog({ - type: 'error', - text: e - }); - }); - }, - - save(withDialog = false) { - os.api('admin/update-meta', { + save() { + os.apiWithDialog('admin/update-meta', { name: this.name, description: this.description, tosUrl: this.tosUrl, - bannerUrl: this.bannerUrl, iconUrl: this.iconUrl, - logoImageUrl: this.logoImageUrl, - backgroundImageUrl: this.backgroundImageUrl, + bannerUrl: this.bannerUrl, maintainerName: this.maintainerName, maintainerEmail: this.maintainerEmail, maxNoteTextLength: this.maxNoteTextLength, - disableRegistration: !this.enableRegistration, disableLocalTimeline: !this.enableLocalTimeline, disableGlobalTimeline: !this.enableGlobalTimeline, - enableHcaptcha: this.enableHcaptcha, - hcaptchaSiteKey: this.hcaptchaSiteKey, - hcaptchaSecretKey: this.hcaptchaSecretKey, - enableRecaptcha: this.enableRecaptcha, - recaptchaSiteKey: this.recaptchaSiteKey, - recaptchaSecretKey: this.recaptchaSecretKey, - proxyAccountId: this.proxyAccountId, - cacheRemoteFiles: this.cacheRemoteFiles, - proxyRemoteFiles: this.proxyRemoteFiles, - localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), - remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10), - blockedHosts: this.blockedHosts.split('\n') || [], - pinnedUsers: this.pinnedUsers ? this.pinnedUsers.split('\n') : [], - pinnedPages: this.pinnedPages ? this.pinnedPages.split('\n') : [], - pinnedClipId: (this.pinnedClipId && this.pinnedClipId) != '' ? this.pinnedClipId : null, - enableServiceWorker: this.enableServiceWorker, - swPublicKey: this.swPublicKey, - swPrivateKey: this.swPrivateKey, - useObjectStorage: this.useObjectStorage, - objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null, - objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null, - objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null, - objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null, - objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null, - objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null, - objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null, - objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null, - objectStorageUseSSL: this.objectStorageUseSSL, - objectStorageUseProxy: this.objectStorageUseProxy, - objectStorageSetPublicRead: this.objectStorageSetPublicRead, - objectStorageS3ForcePathStyle: this.objectStorageS3ForcePathStyle, - enableTwitterIntegration: this.enableTwitterIntegration, - twitterConsumerKey: this.twitterConsumerKey, - twitterConsumerSecret: this.twitterConsumerSecret, - enableGithubIntegration: this.enableGithubIntegration, - githubClientId: this.githubClientId, - githubClientSecret: this.githubClientSecret, - enableDiscordIntegration: this.enableDiscordIntegration, - discordClientId: this.discordClientId, - discordClientSecret: this.discordClientSecret, - enableEmail: this.enableEmail, - email: this.email, - smtpSecure: this.smtpSecure, - smtpHost: this.smtpHost, - smtpPort: this.smtpPort, - smtpUser: this.smtpUser, - smtpPass: this.smtpPass, - summalyProxy: this.summalyProxy, - useStarForReactionFallback: this.useStarForReactionFallback, }).then(() => { fetchInstance(); - if (withDialog) { - os.success(); - } - }).catch(e => { - os.dialog({ - type: 'error', - text: e - }); }); } } diff --git a/src/client/pages/instance/user-dialog.vue b/src/client/pages/instance/user-dialog.vue deleted file mode 100644 index a6bab5ecb8..0000000000 --- a/src/client/pages/instance/user-dialog.vue +++ /dev/null @@ -1,233 +0,0 @@ -<template> -<XModalWindow ref="dialog" - :width="370" - @close="$refs.dialog.close()" - @closed="$emit('closed')" -> - <template #header v-if="user"><MkUserName class="name" :user="user"/></template> - <div class="vrcsvlkm" v-if="user && info"> - <div class="_section"> - <div class="banner" :style="bannerStyle"> - <MkAvatar class="avatar" :user="user" :show-indicator="true"/> - </div> - </div> - <div class="_section"> - <div class="title"> - <span class="acct">@{{ acct(user) }}</span> - </div> - <div class="status"> - <span class="staff" v-if="user.isAdmin"><Fa :icon="faBookmark"/></span> - <span class="staff" v-if="user.isModerator"><Fa :icon="farBookmark"/></span> - <span class="punished" v-if="user.isSilenced"><Fa :icon="faMicrophoneSlash"/></span> - <span class="punished" v-if="user.isSuspended"><Fa :icon="faSnowflake"/></span> - </div> - </div> - <div class="_section"> - <div class="_content"> - <MkSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $ts.moderator }}</MkSwitch> - <MkSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $ts.silence }}</MkSwitch> - <MkSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $ts.suspend }}</MkSwitch> - </div> - </div> - <div class="_section"> - <div class="_content"> - <MkButton full @click="openProfile"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts.profile }}</MkButton> - <MkButton full v-if="user.host != null" @click="updateRemoteUser"><Fa :icon="faSync"/> {{ $ts.updateRemoteUser }}</MkButton> - <MkButton full @click="resetPassword"><Fa :icon="faKey"/> {{ $ts.resetPassword }}</MkButton> - <MkButton full @click="deleteAllFiles" danger><Fa :icon="faTrashAlt"/> {{ $ts.deleteAllFiles }}</MkButton> - </div> - </div> - <div class="_section"> - <details class="_content rawdata"> - <pre><code>{{ JSON.stringify(info, null, 2) }}</code></pre> - </details> - </div> - </div> -</XModalWindow> -</template> - -<script lang="ts"> -import { computed, defineComponent } from 'vue'; -import { faTimes, faBookmark, faKey, faSync, faMicrophoneSlash, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons'; -import { faSnowflake, faTrashAlt, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons'; -import MkButton from '@client/components/ui/button.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import XModalWindow from '@client/components/ui/modal-window.vue'; -import Progress from '@client/scripts/loading'; -import { acct, userPage } from '../../filters/user'; -import * as os from '@client/os'; - -export default defineComponent({ - components: { - MkButton, - MkSwitch, - XModalWindow, - }, - - props: { - userId: { - required: true, - } - }, - - emits: ['closed'], - - data() { - return { - user: null, - info: null, - moderator: false, - silenced: false, - suspended: false, - faTimes, faBookmark, farBookmark, faKey, faSync, faMicrophoneSlash, faSnowflake, faTrashAlt, faExternalLinkSquareAlt - }; - }, - - computed: { - bannerStyle(): any { - if (this.user.bannerUrl == null) return {}; - return { - backgroundImage: `url(${ this.user.bannerUrl })` - }; - }, - }, - - created() { - this.fetch(); - }, - - methods: { - async fetch() { - Progress.start(); - this.user = await os.api('users/show', { userId: this.userId }); - this.info = await os.api('admin/show-user', { userId: this.userId }); - this.moderator = this.info.isModerator; - this.silenced = this.info.isSilenced; - this.suspended = this.info.isSuspended; - Progress.done(); - }, - - /** 処理対象ユーザーの情報を更新する */ - async refreshUser() { - this.user = await os.api('users/show', { userId: this.user.id }); - this.info = await os.api('admin/show-user', { userId: this.user.id }); - }, - - openProfile() { - window.open(userPage(this.user, null, true), '_blank'); - }, - - async updateRemoteUser() { - await os.api('admin/update-remote-user', { userId: this.user.id }).then(res => { - os.success(); - }); - await this.refreshUser(); - }, - - async resetPassword() { - os.apiWithDialog('admin/reset-password', { - userId: this.user.id, - }, undefined, ({ password }) => { - os.dialog({ - type: 'success', - text: this.$t('newPasswordIs', { password }) - }); - }); - }, - - async toggleSilence(v) { - const confirm = await os.dialog({ - type: 'warning', - showCancelButton: true, - text: v ? this.$ts.silenceConfirm : this.$ts.unsilenceConfirm, - }); - if (confirm.canceled) { - this.silenced = !v; - } else { - await os.api(v ? 'admin/silence-user' : 'admin/unsilence-user', { userId: this.user.id }); - await this.refreshUser(); - } - }, - - async toggleSuspend(v) { - const confirm = await os.dialog({ - type: 'warning', - showCancelButton: true, - text: v ? this.$ts.suspendConfirm : this.$ts.unsuspendConfirm, - }); - if (confirm.canceled) { - this.suspended = !v; - } else { - await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: this.user.id }); - await this.refreshUser(); - } - }, - - async toggleModerator(v) { - await os.api(v ? 'admin/moderators/add' : 'admin/moderators/remove', { userId: this.user.id }); - await this.refreshUser(); - }, - - async deleteAllFiles() { - const confirm = await os.dialog({ - type: 'warning', - showCancelButton: true, - text: this.$ts.deleteAllFilesConfirm, - }); - if (confirm.canceled) return; - const process = async () => { - await os.api('admin/delete-all-files-of-a-user', { userId: this.user.id }); - os.success(); - }; - await process().catch(e => { - os.dialog({ - type: 'error', - text: e.toString() - }); - }); - await this.refreshUser(); - }, - - acct - } -}); -</script> - -<style lang="scss" scoped> -.vrcsvlkm { - > ._section { - > .banner { - position: relative; - height: 100px; - background-color: #4c5e6d; - background-size: cover; - background-position: center; - border-radius: 8px; - - > .avatar { - position: absolute; - top: 60px; - width: 64px; - height: 64px; - left: 0; - right: 0; - margin: 0 auto; - border: solid 4px var(--panel); - } - } - - > .title { - text-align: center; - } - - > .status { - text-align: center; - margin-top: 8px; - } - - > .rawdata { - overflow: auto; - } - } -} -</style> diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue index ea09b1bda0..2808b70fba 100644 --- a/src/client/pages/instance/users.vue +++ b/src/client/pages/instance/users.vue @@ -1,88 +1,71 @@ <template> -<div class="mk-instance-users"> - <div class="_section"> - <div class="_content"> - <MkButton inline primary @click="addUser()"><Fa :icon="faPlus"/> {{ $ts.addUser }}</MkButton> - </div> +<div class="lknzcolw"> + <div class="actions"> + <MkButton inline primary @click="addUser()"><i class="fas fa-plus"></i> {{ $ts.addUser }}</MkButton> + <MkButton inline primary @click="lookupUser()"><i class="fas fa-search"></i> {{ $ts.lookup }}</MkButton> </div> - <div class="_section lookup"> - <div class="_title"><Fa :icon="faSearch"/> {{ $ts.lookup }}</div> - <div class="_content"> - <MkInput class="target" v-model:value="target" type="text" @enter="showUser()"> - <span>{{ $ts.usernameOrUserId }}</span> + <div class="users"> + <div class="inputs" style="display: flex;"> + <MkSelect v-model:value="sort" style="margin: 0; flex: 1;"> + <template #label>{{ $ts.sort }}</template> + <option value="-createdAt">{{ $ts.registeredDate }} ({{ $ts.ascendingOrder }})</option> + <option value="+createdAt">{{ $ts.registeredDate }} ({{ $ts.descendingOrder }})</option> + <option value="-updatedAt">{{ $ts.lastUsed }} ({{ $ts.ascendingOrder }})</option> + <option value="+updatedAt">{{ $ts.lastUsed }} ({{ $ts.descendingOrder }})</option> + </MkSelect> + <MkSelect v-model:value="state" style="margin: 0; flex: 1;"> + <template #label>{{ $ts.state }}</template> + <option value="all">{{ $ts.all }}</option> + <option value="available">{{ $ts.normal }}</option> + <option value="admin">{{ $ts.administrator }}</option> + <option value="moderator">{{ $ts.moderator }}</option> + <option value="silenced">{{ $ts.silence }}</option> + <option value="suspended">{{ $ts.suspend }}</option> + </MkSelect> + <MkSelect v-model:value="origin" style="margin: 0; flex: 1;"> + <template #label>{{ $ts.instance }}</template> + <option value="combined">{{ $ts.all }}</option> + <option value="local">{{ $ts.local }}</option> + <option value="remote">{{ $ts.remote }}</option> + </MkSelect> + </div> + <div class="inputs" style="display: flex; padding-top: 1.2em;"> + <MkInput v-model:value="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.users.reload()"> + <span>{{ $ts.username }}</span> + </MkInput> + <MkInput v-model:value="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.users.reload()" :disabled="pagination.params().origin === 'local'"> + <span>{{ $ts.host }}</span> </MkInput> - <MkButton @click="showUser()" primary><Fa :icon="faSearch"/> {{ $ts.lookup }}</MkButton> </div> - </div> - <div class="_section users"> - <div class="_title"><Fa :icon="faUsers"/> {{ $ts.users }}</div> - <div class="_content"> - <div class="inputs" style="display: flex;"> - <MkSelect v-model:value="sort" style="margin: 0; flex: 1;"> - <template #label>{{ $ts.sort }}</template> - <option value="-createdAt">{{ $ts.registeredDate }} ({{ $ts.ascendingOrder }})</option> - <option value="+createdAt">{{ $ts.registeredDate }} ({{ $ts.descendingOrder }})</option> - <option value="-updatedAt">{{ $ts.lastUsed }} ({{ $ts.ascendingOrder }})</option> - <option value="+updatedAt">{{ $ts.lastUsed }} ({{ $ts.descendingOrder }})</option> - </MkSelect> - <MkSelect v-model:value="state" style="margin: 0; flex: 1;"> - <template #label>{{ $ts.state }}</template> - <option value="all">{{ $ts.all }}</option> - <option value="available">{{ $ts.normal }}</option> - <option value="admin">{{ $ts.administrator }}</option> - <option value="moderator">{{ $ts.moderator }}</option> - <option value="silenced">{{ $ts.silence }}</option> - <option value="suspended">{{ $ts.suspend }}</option> - </MkSelect> - <MkSelect v-model:value="origin" style="margin: 0; flex: 1;"> - <template #label>{{ $ts.instance }}</template> - <option value="combined">{{ $ts.all }}</option> - <option value="local">{{ $ts.local }}</option> - <option value="remote">{{ $ts.remote }}</option> - </MkSelect> - </div> - <div class="inputs" style="display: flex; padding-top: 1.2em;"> - <MkInput v-model:value="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.users.reload()"> - <span>{{ $ts.username }}</span> - </MkInput> - <MkInput v-model:value="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.users.reload()" :disabled="pagination.params().origin === 'local'"> - <span>{{ $ts.host }}</span> - </MkInput> - </div> - - <MkPagination :pagination="pagination" #default="{items}" class="users" ref="users"> - <button class="user _panel _button _gap" v-for="user in items" :key="user.id" @click="show(user)"> - <MkAvatar class="avatar" :user="user" :disable-link="true" :show-indicator="true"/> - <div class="body"> - <header> - <MkUserName class="name" :user="user"/> - <span class="acct">@{{ acct(user) }}</span> - <span class="staff" v-if="user.isAdmin"><Fa :icon="faBookmark"/></span> - <span class="staff" v-if="user.isModerator"><Fa :icon="farBookmark"/></span> - <span class="punished" v-if="user.isSilenced"><Fa :icon="faMicrophoneSlash"/></span> - <span class="punished" v-if="user.isSuspended"><Fa :icon="faSnowflake"/></span> - </header> - <div> - <span>{{ $ts.lastUsed }}: <MkTime v-if="user.updatedAt" :time="user.updatedAt" mode="detail"/></span> - </div> - <div> - <span>{{ $ts.registeredDate }}: <MkTime :time="user.createdAt" mode="detail"/></span> - </div> + <MkPagination :pagination="pagination" #default="{items}" class="users" ref="users"> + <button class="user _panel _button _gap" v-for="user in items" :key="user.id" @click="show(user)"> + <MkAvatar class="avatar" :user="user" :disable-link="true" :show-indicator="true"/> + <div class="body"> + <header> + <MkUserName class="name" :user="user"/> + <span class="acct">@{{ acct(user) }}</span> + <span class="staff" v-if="user.isAdmin"><i class="fas fa-bookmark"></i></span> + <span class="staff" v-if="user.isModerator"><i class="far fa-bookmark"></i></span> + <span class="punished" v-if="user.isSilenced"><i class="fas fa-microphone-slash"></i></span> + <span class="punished" v-if="user.isSuspended"><i class="fas fa-snowflake"></i></span> + </header> + <div> + <span>{{ $ts.lastUsed }}: <MkTime v-if="user.updatedAt" :time="user.updatedAt" mode="detail"/></span> </div> - </button> - </MkPagination> - </div> + <div> + <span>{{ $ts.registeredDate }}: <MkTime :time="user.createdAt" mode="detail"/></span> + </div> + </div> + </button> + </MkPagination> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faPlus, faUsers, faSearch, faBookmark, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons'; -import { faSnowflake, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons'; -import parseAcct from '@/misc/acct/parse'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -90,6 +73,7 @@ import MkPagination from '@client/components/ui/pagination.vue'; import { acct } from '../../filters/user'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; +import { lookupUser } from '@client/scripts/lookup-user'; export default defineComponent({ components: { @@ -99,17 +83,18 @@ export default defineComponent({ MkPagination, }, + emits: ['info'], + data() { return { [symbols.PAGE_INFO]: { title: this.$ts.users, - icon: faUsers, + icon: 'fas fa-users', action: { - icon: faSearch, + icon: 'fas fa-search', handler: this.searchUser } }, - target: '', sort: '+createdAt', state: 'all', origin: 'local', @@ -127,7 +112,6 @@ export default defineComponent({ }), offsetMode: true }, - faPlus, faUsers, faSearch, faBookmark, farBookmark, faMicrophoneSlash, faSnowflake } }, @@ -143,40 +127,12 @@ export default defineComponent({ }, }, - methods: { - /** テキストエリアのユーザーを解決する */ - fetchUser() { - return new Promise((res) => { - const usernamePromise = os.api('users/show', parseAcct(this.target)); - const idPromise = os.api('users/show', { userId: this.target }); - let _notFound = false; - const notFound = () => { - if (_notFound) { - os.dialog({ - type: 'error', - text: this.$ts.noSuchUser - }); - } else { - _notFound = true; - } - }; - usernamePromise.then(res).catch(e => { - if (e.code === 'NO_SUCH_USER') { - notFound(); - } - }); - idPromise.then(res).catch(e => { - notFound(); - }); - }); - }, + async mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, - /** テキストエリアから処理対象ユーザーを設定する */ - async showUser() { - const user = await this.fetchUser(); - this.show(user); - this.target = ''; - }, + methods: { + lookupUser, searchUser() { os.selectUser().then(user => { @@ -206,9 +162,7 @@ export default defineComponent({ }, show(user) { - os.popup(import('./user-dialog.vue'), { - userId: user.id - }, {}, 'closed'); + os.pageWindow(`/user-info/${user.id}`); }, acct @@ -217,57 +171,61 @@ export default defineComponent({ </script> <style lang="scss" scoped> -.mk-instance-users { +.lknzcolw { + > .actions { + margin: var(--margin); + } + > .users { - > ._content { - > .users { - margin-top: var(--margin); + margin: var(--margin); + + > .users { + margin-top: var(--margin); - > .user { - display: flex; - width: 100%; - box-sizing: border-box; - text-align: left; - align-items: center; - padding: 16px; + > .user { + display: flex; + width: 100%; + box-sizing: border-box; + text-align: left; + align-items: center; + padding: 16px; - &:hover { - color: var(--accent); - } + &:hover { + color: var(--accent); + } - > .avatar { - width: 60px; - height: 60px; - } + > .avatar { + width: 60px; + height: 60px; + } - > .body { - margin-left: 0.3em; - padding: 0 8px; - flex: 1; + > .body { + margin-left: 0.3em; + padding: 0 8px; + flex: 1; - @media (max-width: 500px) { - font-size: 14px; - } + @media (max-width: 500px) { + font-size: 14px; + } - > header { - > .name { - font-weight: bold; - } + > header { + > .name { + font-weight: bold; + } - > .acct { - margin-left: 8px; - opacity: 0.7; - } + > .acct { + margin-left: 8px; + opacity: 0.7; + } - > .staff { - margin-left: 0.5em; - color: var(--badge); - } + > .staff { + margin-left: 0.5em; + color: var(--badge); + } - > .punished { - margin-left: 0.5em; - color: #4dabf7; - } + > .punished { + margin-left: 0.5em; + color: #4dabf7; } } } diff --git a/src/client/pages/mentions.vue b/src/client/pages/mentions.vue index 042c3a498b..a12993ebb8 100644 --- a/src/client/pages/mentions.vue +++ b/src/client/pages/mentions.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faAt } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import XNotes from '@client/components/notes.vue'; import * as symbols from '@client/symbols'; @@ -20,7 +19,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.mentions, - icon: faAt + icon: 'fas fa-at' }, pagination: { endpoint: 'notes/mentions', diff --git a/src/client/pages/messages.vue b/src/client/pages/messages.vue index 09d92e51ba..6ac9746d4e 100644 --- a/src/client/pages/messages.vue +++ b/src/client/pages/messages.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faEnvelope } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import XNotes from '@client/components/notes.vue'; import * as symbols from '@client/symbols'; @@ -20,7 +19,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.directNotes, - icon: faEnvelope + icon: 'fas fa-envelope' }, pagination: { endpoint: 'notes/mentions', @@ -29,7 +28,6 @@ export default defineComponent({ visibility: 'specified' }) }, - faEnvelope }; }, diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue index 1e316e9090..9f3323f629 100644 --- a/src/client/pages/messaging/index.vue +++ b/src/client/pages/messaging/index.vue @@ -1,6 +1,6 @@ <template> <div class="yweeujhr _root" v-size="{ max: [400] }"> - <MkButton @click="start" primary class="start"><Fa :icon="faPlus"/> {{ $ts.startMessaging }}</MkButton> + <MkButton @click="start" primary class="start"><i class="fas fa-plus"></i> {{ $ts.startMessaging }}</MkButton> <div class="history" v-if="messages.length > 0"> <MkA v-for="(message, i) in messages" @@ -38,7 +38,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faUser, faUsers, faComments, faPlus } from '@fortawesome/free-solid-svg-icons'; import getAcct from '@/misc/acct/render'; import MkButton from '@client/components/ui/button.vue'; import { acct } from '../../filters/user'; @@ -54,13 +53,12 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.messaging, - icon: faComments + icon: 'fas fa-comments' }, fetching: true, moreFetching: false, messages: [], connection: null, - faUser, faUsers, faComments, faPlus }; }, @@ -120,11 +118,11 @@ export default defineComponent({ start(ev) { os.modalMenu([{ text: this.$ts.messagingWithUser, - icon: faUser, + icon: 'fas fa-user', action: () => { this.startUser() } }, { text: this.$ts.messagingWithGroup, - icon: faUsers, + icon: 'fas fa-users', action: () => { this.startGroup() } }], ev.currentTarget || ev.target); }, diff --git a/src/client/pages/messaging/messaging-room.form.vue b/src/client/pages/messaging/messaging-room.form.vue index c547e18850..31c42e4ab3 100644 --- a/src/client/pages/messaging/messaging-room.form.vue +++ b/src/client/pages/messaging/messaging-room.form.vue @@ -13,17 +13,16 @@ ></textarea> <div class="file" @click="file = null" v-if="file">{{ file.name }}</div> <button class="send _button" @click="send" :disabled="!canSend || sending" :title="$ts.send"> - <template v-if="!sending"><Fa :icon="faPaperPlane"/></template><template v-if="sending"><Fa icon="spinner .spin"/></template> + <template v-if="!sending"><i class="fas fa-paper-plane"></i></template><template v-if="sending"><i class="fas fa-spinner fa-pulse fa-fw"></i></template> </button> - <button class="_button" @click="chooseFile"><Fa :icon="faPhotoVideo"/></button> - <button class="_button" @click="insertEmoji"><Fa :icon="faLaughSquint"/></button> + <button class="_button" @click="chooseFile"><i class="fas fa-photo-video"></i></button> + <button class="_button" @click="insertEmoji"><i class="fas fa-laugh-squint"></i></button> <input ref="file" type="file" @change="onChangeFile"/> </div> </template> <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; -import { faPaperPlane, faPhotoVideo, faLaughSquint } from '@fortawesome/free-solid-svg-icons'; import insertTextAtCursor from 'insert-text-at-cursor'; import * as autosize from 'autosize'; import { formatTimeString } from '@/misc/format-time-string'; @@ -51,7 +50,6 @@ export default defineComponent({ typing: throttle(3000, () => { os.stream.send('typingOnMessaging', this.user ? { partner: this.user.id } : { group: this.group.id }); }), - faPaperPlane, faPhotoVideo, faLaughSquint }; }, computed: { diff --git a/src/client/pages/messaging/messaging-room.message.vue b/src/client/pages/messaging/messaging-room.message.vue index 1228baff68..dfac83ad6a 100644 --- a/src/client/pages/messaging/messaging-room.message.vue +++ b/src/client/pages/messaging/messaging-room.message.vue @@ -29,7 +29,7 @@ <span class="read" v-if="isMe && message.isRead">{{ $ts.messageRead }}</span> </template> <MkTime :time="message.createdAt"/> - <template v-if="message.is_edited"><Fa icon="pencil-alt"/></template> + <template v-if="message.is_edited"><i class="fas fa-pencil-alt"></i></template> </footer> </div> </div> @@ -221,7 +221,7 @@ export default defineComponent({ margin: 0 8px; } - > [data-icon] { + > i { margin-left: 4px; } } diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue index dae4590213..44bfd6c51d 100644 --- a/src/client/pages/messaging/messaging-room.vue +++ b/src/client/pages/messaging/messaging-room.vue @@ -6,10 +6,10 @@ <div class="_content mk-messaging-room"> <div class="body"> <MkLoading v-if="fetching"/> - <p class="empty" v-if="!fetching && messages.length == 0"><Fa :icon="faInfoCircle"/>{{ $ts.noMessagesYet }}</p> - <p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><Fa :icon="faFlag"/>{{ $ts.noMoreHistory }}</p> + <p class="empty" v-if="!fetching && messages.length == 0"><i class="fas fa-info-circle"></i>{{ $ts.noMessagesYet }}</p> + <p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><i class="fas fa-flag"></i>{{ $ts.noMoreHistory }}</p> <button class="more _button" ref="loadMore" :class="{ fetching: fetchingMoreMessages }" v-show="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages"> - <template v-if="fetchingMoreMessages"><Fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $ts.loading : $ts.loadMore }} + <template v-if="fetchingMoreMessages"><i class="fas fa-spinner fa-pulse fa-fw"></i></template>{{ fetchingMoreMessages ? $ts.loading : $ts.loadMore }} </button> <XList class="messages" :items="messages" v-slot="{ item: message }" direction="up" reversed> <XMessage :message="message" :is-group="group != null" :key="message.id"/> @@ -26,7 +26,7 @@ </div> <transition name="fade"> <div class="new-message" v-show="showIndicator"> - <button class="_buttonPrimary" @click="onIndicatorClick"><i><Fa :icon="faArrowCircleDown"/></i>{{ $ts.newMessageExists }}</button> + <button class="_buttonPrimary" @click="onIndicatorClick"><i class="fas fa-arrow-circle-down"></i>{{ $ts.newMessageExists }}</button> </div> </transition> <XForm v-if="!fetching" :user="user" :group="group" ref="form"/> @@ -37,8 +37,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faArrowCircleDown, faFlag, faUsers, faInfoCircle, faEllipsisH, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; -import { faWindowMaximize } from '@fortawesome/free-regular-svg-icons'; import XList from '@client/components/date-separated-list.vue'; import XMessage from './messaging-room.message.vue'; import XForm from './messaging-room.form.vue'; @@ -75,14 +73,14 @@ const Component = defineComponent({ userName: this.user, avatar: this.user, action: { - icon: faEllipsisH, + icon: 'fas fa-ellipsis-h', handler: this.menu, }, } : { title: this.group.name, - icon: faUsers, + icon: 'fas fa-users', action: { - icon: faEllipsisH, + icon: 'fas fa-ellipsis-h', handler: this.menu, }, } : null), @@ -103,7 +101,6 @@ const Component = defineComponent({ && this.existMoreMessages && this.fetchMoreMessages() ), - faArrowCircleDown, faFlag, faInfoCircle }; }, @@ -325,14 +322,14 @@ const Component = defineComponent({ os.modalMenu([this.inWindow ? undefined : { text: this.$ts.openInWindow, - icon: faWindowMaximize, + icon: 'fas fa-window-maximize', action: () => { os.pageWindow(path); this.$router.back(); }, }, this.inWindow ? undefined : { text: this.$ts.popout, - icon: faExternalLinkAlt, + icon: 'fas fa-external-link-alt', action: () => { popout(path); this.$router.back(); @@ -356,7 +353,7 @@ export default Component; font-size: 0.8em; opacity: 0.5; - [data-icon] { + i { margin-right: 4px; } } @@ -370,7 +367,7 @@ export default Component; color: var(--messagingRoomInfo); opacity: 0.5; - [data-icon] { + i { margin-right: 4px; } } @@ -396,7 +393,7 @@ export default Component; cursor: wait; } - > [data-icon] { + > i { margin-right: 4px; } } diff --git a/src/client/pages/mfm-cheat-sheet.vue b/src/client/pages/mfm-cheat-sheet.vue index 36f40dbcab..5227855236 100644 --- a/src/client/pages/mfm-cheat-sheet.vue +++ b/src/client/pages/mfm-cheat-sheet.vue @@ -266,7 +266,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faQuestionCircle } from '@fortawesome/free-regular-svg-icons'; import MkTextarea from '@client/components/ui/textarea.vue'; import * as symbols from '@client/symbols'; @@ -279,7 +278,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._mfm.cheatSheet, - icon: faQuestionCircle, + icon: 'fas fa-question-circle', }, preview_mention: '@example', preview_hashtag: '#test', diff --git a/src/client/pages/my-antennas/index.antenna.vue b/src/client/pages/my-antennas/index.antenna.vue index d7050d08c4..8cad6fa553 100644 --- a/src/client/pages/my-antennas/index.antenna.vue +++ b/src/client/pages/my-antennas/index.antenna.vue @@ -39,15 +39,14 @@ <MkSwitch v-model:value="notify">{{ $ts.notifyAntenna }}</MkSwitch> </div> <div class="_footer"> - <MkButton inline @click="saveAntenna()" primary><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - <MkButton inline @click="deleteAntenna()" v-if="antenna.id != null"><Fa :icon="faTrash"/> {{ $ts.delete }}</MkButton> + <MkButton inline @click="saveAntenna()" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton inline @click="deleteAntenna()" v-if="antenna.id != null"><i class="fas fa-trash"></i> {{ $ts.delete }}</MkButton> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faSave, faTrash } from '@fortawesome/free-solid-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkTextarea from '@client/components/ui/textarea.vue'; @@ -83,7 +82,6 @@ export default defineComponent({ notify: false, userLists: null, userGroups: null, - faSave, faTrash }; }, diff --git a/src/client/pages/my-antennas/index.vue b/src/client/pages/my-antennas/index.vue index dfb752b831..57c55cefdd 100644 --- a/src/client/pages/my-antennas/index.vue +++ b/src/client/pages/my-antennas/index.vue @@ -1,6 +1,6 @@ <template> <div class="ieepwinx _section"> - <MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton> + <MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton> <div class="_content"> <XAntenna v-if="draft" :antenna="draft" @created="onAntennaCreated" style="margin-bottom: var(--margin);"/> @@ -14,7 +14,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faSatellite, faPlus } from '@fortawesome/free-solid-svg-icons'; import MkPagination from '@client/components/ui/pagination.vue'; import MkButton from '@client/components/ui/button.vue'; import XAntenna from './index.antenna.vue'; @@ -31,9 +30,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.manageAntennas, - icon: faSatellite, + icon: 'fas fa-satellite', action: { - icon: faPlus, + icon: 'fas fa-plus', handler: this.create } }, @@ -42,7 +41,6 @@ export default defineComponent({ limit: 10, }, draft: null, - faSatellite, faPlus }; }, diff --git a/src/client/pages/my-clips/index.vue b/src/client/pages/my-clips/index.vue index 09cd7f828a..c4ca474748 100644 --- a/src/client/pages/my-clips/index.vue +++ b/src/client/pages/my-clips/index.vue @@ -1,6 +1,6 @@ <template> <div class="_section qtcaoidl"> - <MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton> + <MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton> <div class="_content"> <MkPagination :pagination="pagination" #default="{items}" ref="list" class="list"> @@ -15,7 +15,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPlus, faPaperclip } from '@fortawesome/free-solid-svg-icons'; import MkPagination from '@client/components/ui/pagination.vue'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; @@ -31,9 +30,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.clip, - icon: faPaperclip, + icon: 'fas fa-paperclip', action: { - icon: faPlus, + icon: 'fas fa-plus', handler: this.create } }, @@ -42,7 +41,6 @@ export default defineComponent({ limit: 10, }, draft: null, - faPlus }; }, diff --git a/src/client/pages/my-groups/group.vue b/src/client/pages/my-groups/group.vue index 90a60e5e2b..bd5537cbfa 100644 --- a/src/client/pages/my-groups/group.vue +++ b/src/client/pages/my-groups/group.vue @@ -23,7 +23,7 @@ <MkAcct :user="user" class="acct"/> </div> <div class="action"> - <button class="_button" @click="removeUser(user)"><Fa :icon="faTimes"/></button> + <button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button> </div> </div> </div> @@ -35,7 +35,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faTimes, faUsers } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; @@ -57,11 +56,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: computed(() => this.group ? { title: this.group.name, - icon: faUsers, + icon: 'fas fa-users', } : null), group: null, users: [], - faTimes, faUsers }; }, diff --git a/src/client/pages/my-groups/index.vue b/src/client/pages/my-groups/index.vue index 5125ce3f4f..9f153ff9cc 100644 --- a/src/client/pages/my-groups/index.vue +++ b/src/client/pages/my-groups/index.vue @@ -4,13 +4,13 @@ <MkTab v-model:value="tab"> <option value="owned">{{ $ts.ownedGroups }}</option> <option value="joined">{{ $ts.joinedGroups }}</option> - <option value="invites"><Fa :icon="faEnvelopeOpenText"/> {{ $ts.invites }}</option> + <option value="invites"><i class="fas fa-envelope-open-text"></i> {{ $ts.invites }}</option> </MkTab> </div> <div class="_section"> <div class="_content" v-if="tab === 'owned'"> - <MkButton @click="create" primary style="margin: 0 auto var(--margin) auto;"><Fa :icon="faPlus"/> {{ $ts.createGroup }}</MkButton> + <MkButton @click="create" primary style="margin: 0 auto var(--margin) auto;"><i class="fas fa-plus"></i> {{ $ts.createGroup }}</MkButton> <MkPagination :pagination="ownedPagination" #default="{items}" ref="owned"> <div class="_card" v-for="group in items" :key="group.id"> @@ -35,8 +35,8 @@ <div class="_title">{{ invitation.group.name }}</div> <div class="_content"><MkAvatars :user-ids="invitation.group.userIds"/></div> <div class="_footer"> - <MkButton @click="acceptInvite(invitation)" primary inline><Fa :icon="faCheck"/> {{ $ts.accept }}</MkButton> - <MkButton @click="rejectInvite(invitation)" primary inline><Fa :icon="faBan"/> {{ $ts.reject }}</MkButton> + <MkButton @click="acceptInvite(invitation)" primary inline><i class="fas fa-check"></i> {{ $ts.accept }}</MkButton> + <MkButton @click="rejectInvite(invitation)" primary inline><i class="fas fa-ban"></i> {{ $ts.reject }}</MkButton> </div> </div> </MkPagination> @@ -47,7 +47,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faUsers, faPlus, faEnvelopeOpenText } from '@fortawesome/free-solid-svg-icons'; import MkPagination from '@client/components/ui/pagination.vue'; import MkButton from '@client/components/ui/button.vue'; import MkContainer from '@client/components/ui/container.vue'; @@ -69,7 +68,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.groups, - icon: faUsers + icon: 'fas fa-users' }, tab: 'owned', ownedPagination: { @@ -84,7 +83,6 @@ export default defineComponent({ endpoint: 'i/user-group-invites', limit: 10, }, - faUsers, faPlus, faEnvelopeOpenText }; }, diff --git a/src/client/pages/my-lists/index.vue b/src/client/pages/my-lists/index.vue index e680b90d1a..2b60917060 100644 --- a/src/client/pages/my-lists/index.vue +++ b/src/client/pages/my-lists/index.vue @@ -1,6 +1,6 @@ <template> <div class="qkcjvfiv _section"> - <MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $ts.createList }}</MkButton> + <MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton> <MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list"> <div class="list _panel" v-for="(list, i) in items" :key="list.id"> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faListUl, faPlus } from '@fortawesome/free-solid-svg-icons'; import MkPagination from '@client/components/ui/pagination.vue'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; @@ -28,9 +27,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.manageLists, - icon: faListUl, + icon: 'fas fa-list-ul', action: { - icon: faPlus, + icon: 'fas fa-plus', handler: this.create } }, @@ -38,7 +37,6 @@ export default defineComponent({ endpoint: 'users/lists/list', limit: 10, }, - faListUl, faPlus }; }, diff --git a/src/client/pages/my-lists/list.vue b/src/client/pages/my-lists/list.vue index 2892150ffe..049d370b4e 100644 --- a/src/client/pages/my-lists/list.vue +++ b/src/client/pages/my-lists/list.vue @@ -22,7 +22,7 @@ <MkAcct :user="user" class="acct"/> </div> <div class="action"> - <button class="_button" @click="removeUser(user)"><Fa :icon="faTimes"/></button> + <button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button> </div> </div> </div> @@ -34,7 +34,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faTimes, faListUl } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; @@ -49,11 +48,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: computed(() => this.list ? { title: this.list.name, - icon: faListUl, + icon: 'fas fa-list-ul', } : null), list: null, users: [], - faTimes, faListUl }; }, diff --git a/src/client/pages/not-found.vue b/src/client/pages/not-found.vue index b13bdac2b8..5e7fe17f75 100644 --- a/src/client/pages/not-found.vue +++ b/src/client/pages/not-found.vue @@ -9,7 +9,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -18,7 +17,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.notFound, - icon: faExclamationTriangle + icon: 'fas fa-exclamation-triangle' }, } }, diff --git a/src/client/pages/note.vue b/src/client/pages/note.vue index 279dd96661..ce4af4eb4e 100644 --- a/src/client/pages/note.vue +++ b/src/client/pages/note.vue @@ -7,7 +7,7 @@ </div> <div class="main _gap"> - <MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><Fa :icon="faChevronUp"/></MkButton> + <MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton> <div class="_content _gap"> <MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_gap"/> <XNoteDetailed v-model:note="note" :key="note.id" class="_gap"/> @@ -22,7 +22,7 @@ </div> </MkA> </div> - <MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><Fa :icon="faChevronDown"/></MkButton> + <MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="fas fa-chevron-down"></i></MkButton> </div> <div class="_gap" v-if="showPrev"> @@ -37,7 +37,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons'; import XNote from '@client/components/note.vue'; import XNoteDetailed from '@client/components/note-detailed.vue'; import XNotes from '@client/components/notes.vue'; @@ -95,7 +94,6 @@ export default defineComponent({ sinceId: this.note.id, }) }, - faChevronUp, faChevronDown }; }, watch: { diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue index 25605988ed..38797d746e 100644 --- a/src/client/pages/notifications.vue +++ b/src/client/pages/notifications.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBell, faCheck } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import XNotifications from '@client/components/notifications.vue'; import * as os from '@client/os'; @@ -21,10 +20,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.notifications, - icon: faBell, + icon: 'fas fa-bell', actions: [{ text: this.$ts.markAllAsRead, - icon: faCheck, + icon: 'fas fa-check', handler: () => { os.apiWithDialog('notifications/mark-all-as-read'); } diff --git a/src/client/pages/page-editor/els/page-editor.el.button.vue b/src/client/pages/page-editor/els/page-editor.el.button.vue index 1515187676..6e9036faac 100644 --- a/src/client/pages/page-editor/els/page-editor.el.button.vue +++ b/src/client/pages/page-editor/els/page-editor.el.button.vue @@ -1,6 +1,6 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.button }}</template> + <template #header><i class="fas fa-bolt"></i> {{ $ts._pages.blocks.button }}</template> <section class="xfhsjczc"> <MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._button.text }}</span></MkInput> @@ -39,7 +39,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBolt } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkSelect from '@client/components/ui/select.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -62,7 +61,6 @@ export default defineComponent({ data() { return { - faBolt }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.canvas.vue b/src/client/pages/page-editor/els/page-editor.el.canvas.vue index 9d4b4c76d2..59d29b9b71 100644 --- a/src/client/pages/page-editor/els/page-editor.el.canvas.vue +++ b/src/client/pages/page-editor/els/page-editor.el.canvas.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faPaintBrush"/> {{ $ts._pages.blocks.canvas }}</template> + <template #header><i class="fas fa-paint-brush"></i> {{ $ts._pages.blocks.canvas }}</template> <section style="padding: 0 16px 0 16px;"> - <MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._canvas.id }}</span></MkInput> + <MkInput v-model:value="value.name"><template #prefix><i class="fas fa-magic"></i></template><span>{{ $ts._pages.blocks._canvas.id }}</span></MkInput> <MkInput v-model:value="value.width" type="number"><span>{{ $ts._pages.blocks._canvas.width }}</span><template #suffix>px</template></MkInput> <MkInput v-model:value="value.height" type="number"><span>{{ $ts._pages.blocks._canvas.height }}</span><template #suffix>px</template></MkInput> </section> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPaintBrush, faMagic } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkInput from '@client/components/ui/input.vue'; import * as os from '@client/os'; @@ -30,7 +29,6 @@ export default defineComponent({ data() { return { - faPaintBrush, faMagic }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.counter.vue b/src/client/pages/page-editor/els/page-editor.el.counter.vue index e16962aee9..3394817b53 100644 --- a/src/client/pages/page-editor/els/page-editor.el.counter.vue +++ b/src/client/pages/page-editor/els/page-editor.el.counter.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.counter }}</template> + <template #header><i class="fas fa-bolt"></i> {{ $ts._pages.blocks.counter }}</template> <section style="padding: 0 16px 0 16px;"> - <MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._counter.name }}</span></MkInput> + <MkInput v-model:value="value.name"><template #prefix><i class="fas fa-magic"></i></template><span>{{ $ts._pages.blocks._counter.name }}</span></MkInput> <MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._counter.text }}</span></MkInput> <MkInput v-model:value="value.inc" type="number"><span>{{ $ts._pages.blocks._counter.inc }}</span></MkInput> </section> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkInput from '@client/components/ui/input.vue'; import * as os from '@client/os'; @@ -30,7 +29,6 @@ export default defineComponent({ data() { return { - faBolt, faMagic }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.if.vue b/src/client/pages/page-editor/els/page-editor.el.if.vue index 0cbfaa7eb8..7f4ed458aa 100644 --- a/src/client/pages/page-editor/els/page-editor.el.if.vue +++ b/src/client/pages/page-editor/els/page-editor.el.if.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faQuestion"/> {{ $ts._pages.blocks.if }}</template> + <template #header><i class="fas fa-question"></i> {{ $ts._pages.blocks.if }}</template> <template #func> <button @click="add()" class="_button"> - <Fa :icon="faPlus"/> + <i class="fas fa-plus"></i> </button> </template> @@ -27,7 +27,6 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; import { v4 as uuid } from 'uuid'; -import { faPlus, faQuestion } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkSelect from '@client/components/ui/select.vue'; import * as os from '@client/os'; @@ -51,7 +50,6 @@ export default defineComponent({ data() { return { - faPlus, faQuestion }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.image.vue b/src/client/pages/page-editor/els/page-editor.el.image.vue index 1a96e42679..d96879f50d 100644 --- a/src/client/pages/page-editor/els/page-editor.el.image.vue +++ b/src/client/pages/page-editor/els/page-editor.el.image.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faImage"/> {{ $ts._pages.blocks.image }}</template> + <template #header><i class="fas fa-image"></i> {{ $ts._pages.blocks.image }}</template> <template #func> <button @click="choose()"> - <Fa :icon="faFolderOpen"/> + <i class="fas fa-folder-open"></i> </button> </template> @@ -15,8 +15,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPencilAlt } from '@fortawesome/free-solid-svg-icons'; -import { faImage, faFolderOpen } from '@fortawesome/free-regular-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkDriveFileThumbnail from '@client/components/drive-file-thumbnail.vue'; import * as os from '@client/os'; @@ -35,7 +33,6 @@ export default defineComponent({ data() { return { file: null, - faPencilAlt, faImage, faFolderOpen }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.note.vue b/src/client/pages/page-editor/els/page-editor.el.note.vue index 3f7eaf7572..d4801f3059 100644 --- a/src/client/pages/page-editor/els/page-editor.el.note.vue +++ b/src/client/pages/page-editor/els/page-editor.el.note.vue @@ -1,6 +1,6 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faStickyNote"/> {{ $ts._pages.blocks.note }}</template> + <template #header><i class="fas fa-sticky-note"></i> {{ $ts._pages.blocks.note }}</template> <section style="padding: 0 16px 0 16px;"> <MkInput v-model:value="id"> @@ -17,7 +17,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faStickyNote } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkInput from '@client/components/ui/input.vue'; import MkSwitch from '@client/components/ui/switch.vue'; @@ -40,7 +39,6 @@ export default defineComponent({ return { id: this.value.note, note: null, - faStickyNote }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.number-input.vue b/src/client/pages/page-editor/els/page-editor.el.number-input.vue index 76c35d4406..8058d941c1 100644 --- a/src/client/pages/page-editor/els/page-editor.el.number-input.vue +++ b/src/client/pages/page-editor/els/page-editor.el.number-input.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.numberInput }}</template> + <template #header><i class="fas fa-bolt"></i> {{ $ts._pages.blocks.numberInput }}</template> <section style="padding: 0 16px 0 16px;"> - <MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._numberInput.name }}</span></MkInput> + <MkInput v-model:value="value.name"><template #prefix><i class="fas fa-magic"></i></template><span>{{ $ts._pages.blocks._numberInput.name }}</span></MkInput> <MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._numberInput.text }}</span></MkInput> <MkInput v-model:value="value.default" type="number"><span>{{ $ts._pages.blocks._numberInput.default }}</span></MkInput> </section> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkInput from '@client/components/ui/input.vue'; import * as os from '@client/os'; @@ -30,7 +29,6 @@ export default defineComponent({ data() { return { - faBolt, faMagic }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.post.vue b/src/client/pages/page-editor/els/page-editor.el.post.vue index 51c5481d54..1ed7f860c8 100644 --- a/src/client/pages/page-editor/els/page-editor.el.post.vue +++ b/src/client/pages/page-editor/els/page-editor.el.post.vue @@ -1,6 +1,6 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faPaperPlane"/> {{ $ts._pages.blocks.post }}</template> + <template #header><i class="fas fa-paper-plane"></i> {{ $ts._pages.blocks.post }}</template> <section style="padding: 16px;"> <MkTextarea v-model:value="value.text">{{ $ts._pages.blocks._post.text }}</MkTextarea> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPaperPlane } from '@fortawesome/free-regular-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkTextarea from '@client/components/ui/textarea.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -32,7 +31,6 @@ export default defineComponent({ data() { return { - faPaperPlane }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue index 82b09a6290..97715ed69c 100644 --- a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue +++ b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.radioButton }}</template> + <template #header><i class="fas fa-bolt"></i> {{ $ts._pages.blocks.radioButton }}</template> <section style="padding: 0 16px 16px 16px;"> - <MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._radioButton.name }}</span></MkInput> + <MkInput v-model:value="value.name"><template #prefix><i class="fas fa-magic"></i></template><span>{{ $ts._pages.blocks._radioButton.name }}</span></MkInput> <MkInput v-model:value="value.title"><span>{{ $ts._pages.blocks._radioButton.title }}</span></MkInput> <MkTextarea v-model:value="values"><span>{{ $ts._pages.blocks._radioButton.values }}</span></MkTextarea> <MkInput v-model:value="value.default"><span>{{ $ts._pages.blocks._radioButton.default }}</span></MkInput> @@ -13,7 +13,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkTextarea from '@client/components/ui/textarea.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -31,7 +30,6 @@ export default defineComponent({ data() { return { values: '', - faBolt, faMagic }; }, watch: { diff --git a/src/client/pages/page-editor/els/page-editor.el.section.vue b/src/client/pages/page-editor/els/page-editor.el.section.vue index 96f468b13a..16ef2598f9 100644 --- a/src/client/pages/page-editor/els/page-editor.el.section.vue +++ b/src/client/pages/page-editor/els/page-editor.el.section.vue @@ -1,12 +1,12 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faStickyNote"/> {{ value.title }}</template> + <template #header><i class="fas fa-sticky-note"></i> {{ value.title }}</template> <template #func> <button @click="rename()" class="_button"> - <Fa :icon="faPencilAlt"/> + <i class="fas fa-pencil-alt"></i> </button> <button @click="add()" class="_button"> - <Fa :icon="faPlus"/> + <i class="fas fa-plus"></i> </button> </template> @@ -19,8 +19,6 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; import { v4 as uuid } from 'uuid'; -import { faPlus, faPencilAlt } from '@fortawesome/free-solid-svg-icons'; -import { faStickyNote } from '@fortawesome/free-regular-svg-icons'; import XContainer from '../page-editor.container.vue'; import * as os from '@client/os'; @@ -43,7 +41,6 @@ export default defineComponent({ data() { return { - faStickyNote, faPlus, faPencilAlt }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.switch.vue b/src/client/pages/page-editor/els/page-editor.el.switch.vue index 56b9f1561c..564d5e22c3 100644 --- a/src/client/pages/page-editor/els/page-editor.el.switch.vue +++ b/src/client/pages/page-editor/els/page-editor.el.switch.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.switch }}</template> + <template #header><i class="fas fa-bolt"></i> {{ $ts._pages.blocks.switch }}</template> <section class="kjuadyyj"> - <MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._switch.name }}</span></MkInput> + <MkInput v-model:value="value.name"><template #prefix><i class="fas fa-magic"></i></template><span>{{ $ts._pages.blocks._switch.name }}</span></MkInput> <MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._switch.text }}</span></MkInput> <MkSwitch v-model:value="value.default"><span>{{ $ts._pages.blocks._switch.default }}</span></MkSwitch> </section> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkSwitch from '@client/components/ui/switch.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -31,7 +30,6 @@ export default defineComponent({ data() { return { - faBolt, faMagic }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.text-input.vue b/src/client/pages/page-editor/els/page-editor.el.text-input.vue index cb8cb83aa7..4435d9b841 100644 --- a/src/client/pages/page-editor/els/page-editor.el.text-input.vue +++ b/src/client/pages/page-editor/els/page-editor.el.text-input.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.textInput }}</template> + <template #header><i class="fas fa-bolt"></i> {{ $ts._pages.blocks.textInput }}</template> <section style="padding: 0 16px 0 16px;"> - <MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._textInput.name }}</span></MkInput> + <MkInput v-model:value="value.name"><template #prefix><i class="fas fa-magic"></i></template><span>{{ $ts._pages.blocks._textInput.name }}</span></MkInput> <MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._textInput.text }}</span></MkInput> <MkInput v-model:value="value.default" type="text"><span>{{ $ts._pages.blocks._textInput.default }}</span></MkInput> </section> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkInput from '@client/components/ui/input.vue'; import * as os from '@client/os'; @@ -30,7 +29,6 @@ export default defineComponent({ data() { return { - faBolt, faMagic }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.text.vue b/src/client/pages/page-editor/els/page-editor.el.text.vue index bd2c9c46dc..668dd5f52d 100644 --- a/src/client/pages/page-editor/els/page-editor.el.text.vue +++ b/src/client/pages/page-editor/els/page-editor.el.text.vue @@ -1,6 +1,6 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faAlignLeft"/> {{ $ts._pages.blocks.text }}</template> + <template #header><i class="fas fa-align-left"></i> {{ $ts._pages.blocks.text }}</template> <section class="vckmsadr"> <textarea v-model="value.text"></textarea> @@ -10,7 +10,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faAlignLeft } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import * as os from '@client/os'; @@ -27,7 +26,6 @@ export default defineComponent({ data() { return { - faAlignLeft, }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue index 8c4ff23408..cf3b9f93f4 100644 --- a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue +++ b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue @@ -1,9 +1,9 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.textareaInput }}</template> + <template #header><i class="fas fa-bolt"></i> {{ $ts._pages.blocks.textareaInput }}</template> <section style="padding: 0 16px 16px 16px;"> - <MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._textareaInput.name }}</span></MkInput> + <MkInput v-model:value="value.name"><template #prefix><i class="fas fa-magic"></i></template><span>{{ $ts._pages.blocks._textareaInput.name }}</span></MkInput> <MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._textareaInput.text }}</span></MkInput> <MkTextarea v-model:value="value.default"><span>{{ $ts._pages.blocks._textareaInput.default }}</span></MkTextarea> </section> @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import MkTextarea from '@client/components/ui/textarea.vue'; import MkInput from '@client/components/ui/input.vue'; @@ -31,7 +30,6 @@ export default defineComponent({ data() { return { - faBolt, faMagic }; }, diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea.vue b/src/client/pages/page-editor/els/page-editor.el.textarea.vue index 042b283731..a29d5bd3f2 100644 --- a/src/client/pages/page-editor/els/page-editor.el.textarea.vue +++ b/src/client/pages/page-editor/els/page-editor.el.textarea.vue @@ -1,6 +1,6 @@ <template> <XContainer @remove="() => $emit('remove')" :draggable="true"> - <template #header><Fa :icon="faAlignLeft"/> {{ $ts._pages.blocks.textarea }}</template> + <template #header><i class="fas fa-align-left"></i> {{ $ts._pages.blocks.textarea }}</template> <section class="ihymsbbe"> <textarea v-model="value.text"></textarea> @@ -10,7 +10,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faAlignLeft } from '@fortawesome/free-solid-svg-icons'; import XContainer from '../page-editor.container.vue'; import * as os from '@client/os'; @@ -27,7 +26,6 @@ export default defineComponent({ data() { return { - faAlignLeft, }; }, diff --git a/src/client/pages/page-editor/page-editor.container.vue b/src/client/pages/page-editor/page-editor.container.vue index 46e2dca157..afd261fac7 100644 --- a/src/client/pages/page-editor/page-editor.container.vue +++ b/src/client/pages/page-editor/page-editor.container.vue @@ -5,14 +5,14 @@ <div class="buttons"> <slot name="func"></slot> <button v-if="removable" @click="remove()" class="_button"> - <Fa :icon="faTrashAlt"/> + <i class="fas fa-trash-alt"></i> </button> <button v-if="draggable" class="drag-handle _button"> - <Fa :icon="faBars"/> + <i class="fas fa-bars"></i> </button> <button @click="toggleContent(!showBody)" class="_button"> - <template v-if="showBody"><Fa :icon="faAngleUp"/></template> - <template v-else><Fa :icon="faAngleDown"/></template> + <template v-if="showBody"><i class="fas fa-angle-up"></i></template> + <template v-else><i class="fas fa-angle-down"></i></template> </button> </div> </header> @@ -26,8 +26,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBars, faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons'; -import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; export default defineComponent({ props: { @@ -56,7 +54,6 @@ export default defineComponent({ data() { return { showBody: this.expanded, - faTrashAlt, faBars, faAngleUp, faAngleDown }; }, methods: { @@ -105,7 +102,7 @@ export default defineComponent({ font-weight: bold; box-shadow: 0 1px rgba(#000, 0.07); - > [data-icon] { + > i { margin-right: 6px; } diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue index 10d5311cde..65ac731e47 100644 --- a/src/client/pages/page-editor/page-editor.script-block.vue +++ b/src/client/pages/page-editor/page-editor.script-block.vue @@ -1,9 +1,9 @@ <template> <XContainer :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn" :draggable="draggable"> - <template #header><Fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template> + <template #header><i v-if="icon" :class="icon"></i> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template> <template #func> <button @click="changeType()" class="_button"> - <Fa :icon="faPencilAlt"/> + <i class="fas fa-pencil-alt"></i> </button> </template> @@ -57,7 +57,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faPencilAlt, faPlug } from '@fortawesome/free-solid-svg-icons'; import { v4 as uuid } from 'uuid'; import XContainer from './page-editor.container.vue'; import MkTextarea from '@client/components/ui/textarea.vue'; @@ -109,14 +108,13 @@ export default defineComponent({ error: null, warn: null, slots: '', - faPencilAlt }; }, computed: { icon(): any { if (this.value.type === null) return null; - if (this.value.type.startsWith('fn:')) return faPlug; + if (this.value.type.startsWith('fn:')) return 'fas fa-plug'; return blockDefs.find(x => x.type === this.value.type).icon; }, typeText(): any { diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index 4583863a1c..e96e1faaf2 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -1,15 +1,15 @@ <template> <div class="_root"> - <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts._pages.viewPage }}</MkA> + <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkA> <div class="buttons" style="margin: 16px;"> - <MkButton inline @click="save" primary class="save" v-if="!readonly"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><Fa :icon="faCopy"/> {{ $ts.duplicate }}</MkButton> - <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton> + <MkButton inline @click="save" primary class="save" v-if="!readonly"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><i class="fas fa-copy"></i> {{ $ts.duplicate }}</MkButton> + <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> </div> <MkContainer :foldable="true" :expanded="true" class="_gap"> - <template #header><Fa :icon="faCog"/> {{ $ts._pages.pageSetting }}</template> + <template #header><i class="fas fa-cog"></i> {{ $ts._pages.pageSetting }}</template> <div style="padding: 16px;"> <MkInput v-model:value="title"> <span>{{ $ts._pages.title }}</span> @@ -35,26 +35,26 @@ <MkSwitch v-model:value="hideTitleWhenPinned">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> <div class="eyeCatch"> - <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><Fa :icon="faPlus"/> {{ $ts._pages.eyeCatchingImageSet }}</MkButton> + <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="fas fa-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton> <div v-else-if="eyeCatchingImage"> <img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/> - <MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><Fa :icon="faTrashAlt"/> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton> + <MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><i class="fas fa-trash-alt"></i> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton> </div> </div> </div> </MkContainer> <MkContainer :foldable="true" :expanded="true" class="_gap"> - <template #header><Fa :icon="faStickyNote"/> {{ $ts._pages.contents }}</template> + <template #header><i class="fas fa-sticky-note"></i> {{ $ts._pages.contents }}</template> <div style="padding: 16px;"> <XBlocks class="content" v-model:value="content" :hpml="hpml"/> - <MkButton @click="add()" v-if="!readonly"><Fa :icon="faPlus"/></MkButton> + <MkButton @click="add()" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> </div> </MkContainer> <MkContainer :foldable="true" class="_gap"> - <template #header><Fa :icon="faMagic"/> {{ $ts._pages.variables }}</template> + <template #header><i class="fas fa-magic"></i> {{ $ts._pages.variables }}</template> <div class="qmuvgica"> <XDraggable tag="div" class="variables" v-show="variables.length > 0" v-model="variables" item-key="name" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5"> <template #item="{element}"> @@ -70,12 +70,12 @@ </template> </XDraggable> - <MkButton @click="addVariable()" class="add" v-if="!readonly"><Fa :icon="faPlus"/></MkButton> + <MkButton @click="addVariable()" class="add" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> </div> </MkContainer> <MkContainer :foldable="true" :expanded="true" class="_gap"> - <template #header><Fa :icon="faCode"/> {{ $ts.script }}</template> + <template #header><i class="fas fa-code"></i> {{ $ts.script }}</template> <div> <MkTextarea class="_code" v-model:value="script"/> </div> @@ -91,8 +91,6 @@ import 'prismjs/components/prism-clike'; import 'prismjs/components/prism-javascript'; import 'prismjs/themes/prism-okaidia.css'; import 'vue-prism-editor/dist/prismeditor.min.css'; -import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt, faPencilAlt, faCopy } from '@fortawesome/free-solid-svg-icons'; -import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import { v4 as uuid } from 'uuid'; import XVariable from './page-editor.script-block.vue'; import XBlocks from './page-editor.blocks.vue'; @@ -143,7 +141,7 @@ export default defineComponent({ } return { title: title, - icon: faPencilAlt, + icon: 'fas fa-pencil-alt', }; }), author: this.$i, @@ -164,7 +162,6 @@ export default defineComponent({ hpml: null, script: '', url, - faPlus, faICursor, faSave, faStickyNote, faMagic, faCog, faTrashAlt, faExternalLinkSquareAlt, faCode, faCopy }; }, @@ -471,7 +468,7 @@ export default defineComponent({ font-weight: bold; box-shadow: 0 1px rgba(#000, 0.07); - > [data-icon] { + > i { margin-right: 6px; } diff --git a/src/client/pages/page.vue b/src/client/pages/page.vue index d7b570e5f4..f25ed51184 100644 --- a/src/client/pages/page.vue +++ b/src/client/pages/page.vue @@ -14,8 +14,8 @@ <small style="display: block; opacity: 0.7; margin-top: 1em;">@{{ page.user.username }}</small> </div> <div class="like"> - <MkButton class="button" @click="unlike()" v-if="page.isLiked" v-tooltip="$ts._pages.unlike" primary><Fa :icon="faHeartS"/><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> - <MkButton class="button" @click="like()" v-else v-tooltip="$ts._pages.like"><Fa :icon="faHeartR"/><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> + <MkButton class="button" @click="unlike()" v-if="page.isLiked" v-tooltip="$ts._pages.unlike" primary><i class="fas fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> + <MkButton class="button" @click="like()" v-else v-tooltip="$ts._pages.like"><i class="far fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> </div> <div class="links"> <MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ $ts._pages.viewSource }}</MkA> @@ -27,16 +27,14 @@ </div> </div> <div class="footer"> - <div><Fa :icon="faClock"/> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> - <div v-if="page.createdAt != page.updatedAt"><Fa :icon="faClock"/> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> + <div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> + <div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> </div> </div> </template> <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons'; -import { faHeart as faHeartR, faClock } from '@fortawesome/free-regular-svg-icons'; import XPage from '@client/components/page/page.vue'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; @@ -71,7 +69,6 @@ export default defineComponent({ }, } : null), page: null, - faHeartS, faHeartR, faClock, }; }, diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue index 8aea7e6b95..52a860be13 100644 --- a/src/client/pages/pages.vue +++ b/src/client/pages/pages.vue @@ -1,9 +1,9 @@ <template> <div> <MkTab v-model:value="tab" v-if="$i"> - <option value="featured"><Fa :icon="faFireAlt"/> {{ $ts._pages.featured }}</option> - <option value="my"><Fa :icon="faEdit"/> {{ $ts._pages.my }}</option> - <option value="liked"><Fa :icon="faHeart"/> {{ $ts._pages.liked }}</option> + <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option> + <option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option> + <option value="liked"><i class="fas fa-heart"></i> {{ $ts._pages.liked }}</option> </MkTab> <div class="_section"> @@ -14,7 +14,7 @@ </div> <div class="rknalgpo _content my" v-if="tab === 'my'"> - <MkButton class="new" @click="create()"><Fa :icon="faPlus"/></MkButton> + <MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton> <MkPagination :pagination="myPagesPagination" #default="{items}"> <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/> </MkPagination> @@ -31,8 +31,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPlus, faEdit, faFireAlt } from '@fortawesome/free-solid-svg-icons'; -import { faStickyNote, faHeart } from '@fortawesome/free-regular-svg-icons'; import MkPagePreview from '@client/components/page-preview.vue'; import MkPagination from '@client/components/ui/pagination.vue'; import MkButton from '@client/components/ui/button.vue'; @@ -47,9 +45,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.pages, - icon: faStickyNote, + icon: 'fas fa-sticky-note', actions: [{ - icon: faPlus, + icon: 'fas fa-plus', text: this.$ts.create, handler: this.create }] @@ -67,7 +65,6 @@ export default defineComponent({ endpoint: 'i/page-likes', limit: 5, }, - faStickyNote, faPlus, faEdit, faHeart, faFireAlt }; }, methods: { diff --git a/src/client/pages/preview.vue b/src/client/pages/preview.vue index bd4e08db62..3df446e676 100644 --- a/src/client/pages/preview.vue +++ b/src/client/pages/preview.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faEye } from '@fortawesome/free-solid-svg-icons'; import MkSample from '@client/components/sample.vue'; import * as symbols from '@client/symbols'; @@ -19,7 +18,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.preview, - icon: faEye, + icon: 'fas fa-eye', }, } }, diff --git a/src/client/pages/reversi/game.board.vue b/src/client/pages/reversi/game.board.vue index a466527c01..78bcf03413 100644 --- a/src/client/pages/reversi/game.board.vue +++ b/src/client/pages/reversi/game.board.vue @@ -40,8 +40,8 @@ <img v-if="stone === false" :src="whiteUser.avatarUrl" alt="white"> </template> <template v-else> - <fa v-if="stone === true" :icon="fasCircle"/> - <fa v-if="stone === false" :icon="farCircle"/> + <i v-if="stone === true" class="fas fa-circle"></i> + <i v-if="stone === false" class="far fa-circle"></i> </template> </div> </div> @@ -63,12 +63,12 @@ <div class="player" v-if="game.isEnded"> <span>{{ logPos }} / {{ logs.length }}</span> <div class="buttons" v-if="!autoplaying"> - <MkButton inline @click="logPos = 0" :disabled="logPos == 0"><fa :icon="faAngleDoubleLeft"/></MkButton> - <MkButton inline @click="logPos--" :disabled="logPos == 0"><fa :icon="faAngleLeft"/></MkButton> - <MkButton inline @click="logPos++" :disabled="logPos == logs.length"><fa :icon="faAngleRight"/></MkButton> - <MkButton inline @click="logPos = logs.length" :disabled="logPos == logs.length"><fa :icon="faAngleDoubleRight"/></MkButton> + <MkButton inline @click="logPos = 0" :disabled="logPos == 0"><i class="fas fa-angle-double-left"></i></MkButton> + <MkButton inline @click="logPos--" :disabled="logPos == 0"><i class="fas fa-angle-left"></i></MkButton> + <MkButton inline @click="logPos++" :disabled="logPos == logs.length"><i class="fas fa-angle-right"></i></MkButton> + <MkButton inline @click="logPos = logs.length" :disabled="logPos == logs.length"><i class="fas fa-angle-double-right"></i></MkButton> </div> - <MkButton @click="autoplay()" :disabled="autoplaying" style="margin: var(--margin) auto 0 auto;"><fa :icon="faPlay"/></MkButton> + <MkButton @click="autoplay()" :disabled="autoplaying" style="margin: var(--margin) auto 0 auto;"><i class="fas fa-play"></i></MkButton> </div> <div class="info"> @@ -85,9 +85,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faAngleDoubleLeft, faAngleLeft, faAngleRight, faAngleDoubleRight, faPlay } from '@fortawesome/free-solid-svg-icons'; -import { faCircle as fasCircle } from '@fortawesome/free-solid-svg-icons'; -import { faCircle as farCircle } from '@fortawesome/free-regular-svg-icons'; import * as CRC32 from 'crc-32'; import Reversi, { Color } from '../../../games/reversi/core'; import { url } from '@client/config'; @@ -120,7 +117,6 @@ export default defineComponent({ logPos: 0, watchers: [], pollingClock: null, - faAngleDoubleLeft, faAngleLeft, faAngleRight, faAngleDoubleRight, fasCircle, farCircle, faPlay }; }, diff --git a/src/client/pages/reversi/game.setting.vue b/src/client/pages/reversi/game.setting.vue index c7c2937ba8..1a2abba795 100644 --- a/src/client/pages/reversi/game.setting.vue +++ b/src/client/pages/reversi/game.setting.vue @@ -17,11 +17,11 @@ </header> <div> - <div class="random" v-if="game.map == null"><fa icon="dice"/></div> + <div class="random" v-if="game.map == null"><i class="fas fa-dice"></i></div> <div class="board" v-else :style="{ 'grid-template-rows': `repeat(${ game.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.map[0].length }, 1fr)` }"> <div v-for="(x, i) in game.map.join('')" :class="{ none: x == ' ' }" @click="onPixelClick(i, x)"> - <fa v-if="x == 'b'" :icon="fasCircle"/> - <fa v-if="x == 'w'" :icon="farCircle"/> + <i v-if="x === 'b'" class="fas fa-circle"></i> + <i v-if="x === 'w'" class="far fa-circle"></i> </div> </div> </div> @@ -125,8 +125,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCircle as fasCircle } from '@fortawesome/free-solid-svg-icons'; -import { faCircle as farCircle } from '@fortawesome/free-regular-svg-icons'; import * as maps from '../../../games/reversi/maps'; import MkButton from '@client/components/ui/button.vue'; import MkSwitch from '@client/components/ui/switch.vue'; diff --git a/src/client/pages/reversi/game.vue b/src/client/pages/reversi/game.vue index 896dbc39cc..62c99d7755 100644 --- a/src/client/pages/reversi/game.vue +++ b/src/client/pages/reversi/game.vue @@ -9,7 +9,6 @@ import { defineComponent } from 'vue'; import GameSetting from './game.setting.vue'; import GameBoard from './game.board.vue'; import * as os from '@client/os'; -import { faGamepad } from '@fortawesome/free-solid-svg-icons'; import * as symbols from '@client/symbols'; export default defineComponent({ @@ -29,7 +28,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._reversi.reversi, - icon: faGamepad + icon: 'fas fa-gamepad' }, game: null, connection: null, diff --git a/src/client/pages/reversi/index.vue b/src/client/pages/reversi/index.vue index 59b228f5f6..37126fca10 100644 --- a/src/client/pages/reversi/index.vue +++ b/src/client/pages/reversi/index.vue @@ -64,7 +64,6 @@ import { defineComponent } from 'vue'; import * as os from '@client/os'; import MkButton from '@client/components/ui/button.vue'; import MkFolder from '@client/components/ui/folder.vue'; -import { faGamepad } from '@fortawesome/free-solid-svg-icons'; import * as symbols from '@client/symbols'; export default defineComponent({ @@ -78,7 +77,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._reversi.reversi, - icon: faGamepad + icon: 'fas fa-gamepad' }, games: [], gamesFetching: true, diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue index e1de52b8f1..ea34ef11b5 100644 --- a/src/client/pages/room/room.vue +++ b/src/client/pages/room/room.vue @@ -17,18 +17,18 @@ </template> </div> <div class="_content"> - <MkButton inline @click="translate()" :primary="isTranslateMode"><Fa :icon="faArrowsAlt"/> {{ $ts._rooms.translate }}</MkButton> - <MkButton inline @click="rotate()" :primary="isRotateMode"><Fa :icon="faUndo"/> {{ $ts._rooms.rotate }}</MkButton> - <MkButton inline v-if="isTranslateMode || isRotateMode" @click="exit()"><Fa :icon="faBan"/> {{ $ts._rooms.exit }}</MkButton> + <MkButton inline @click="translate()" :primary="isTranslateMode"><i class="fas fa-arrows-alt"></i> {{ $ts._rooms.translate }}</MkButton> + <MkButton inline @click="rotate()" :primary="isRotateMode"><i class="fas fa-undo"></i> {{ $ts._rooms.rotate }}</MkButton> + <MkButton inline v-if="isTranslateMode || isRotateMode" @click="exit()"><i class="fas fa-ban"></i> {{ $ts._rooms.exit }}</MkButton> </div> <div class="_content"> - <MkButton @click="remove()"><Fa :icon="faTrashAlt"/> {{ $ts._rooms.remove }}</MkButton> + <MkButton @click="remove()"><i class="fas fa-trash-alt"></i> {{ $ts._rooms.remove }}</MkButton> </div> </div> <div class="menu _section" v-if="isMyRoom"> <div class="_content"> - <MkButton @click="add()"><Fa :icon="faBoxOpen"/> {{ $ts._rooms.addFurniture }}</MkButton> + <MkButton @click="add()"><i class="fas fa-box-open"></i> {{ $ts._rooms.addFurniture }}</MkButton> </div> <div class="_content"> <MkSelect :value="roomType" @update:value="updateRoomType($event)"> @@ -42,8 +42,8 @@ </label> </div> <div class="_content"> - <MkButton inline :disabled="!changed" primary @click="save()"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton> - <MkButton inline @click="clear()"><Fa :icon="faBroom"/> {{ $ts._rooms.clear }}</MkButton> + <MkButton inline :disabled="!changed" primary @click="save()"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton inline @click="clear()"><i class="fas fa-broom"></i> {{ $ts._rooms.clear }}</MkButton> </div> </div> </div> @@ -55,8 +55,6 @@ import { Room } from '@client/scripts/room/room'; import parseAcct from '@/misc/acct/parse'; import XPreview from './preview.vue'; const storeItems = require('@client/scripts/room/furnitures.json5'); -import { faBoxOpen, faUndo, faArrowsAlt, faBan, faBroom } from '@fortawesome/free-solid-svg-icons'; -import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import { query as urlQuery } from '../../../prelude/url'; import MkButton from '@client/components/ui/button.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -98,7 +96,6 @@ export default defineComponent({ isRotateMode: false, isMyRoom: false, changed: false, - faBoxOpen, faSave, faTrashAlt, faUndo, faArrowsAlt, faBan, faBroom, }; }, diff --git a/src/client/pages/scratchpad.vue b/src/client/pages/scratchpad.vue index 1a863e6b2e..99164ec51f 100644 --- a/src/client/pages/scratchpad.vue +++ b/src/client/pages/scratchpad.vue @@ -2,11 +2,11 @@ <div class="iltifgqe"> <div class="editor _panel _gap"> <PrismEditor class="_code code" v-model="code" :highlight="highlighter" :line-numbers="false"/> - <MkButton style="position: absolute; top: 8px; right: 8px;" @click="run()" primary><Fa :icon="faPlay"/></MkButton> + <MkButton style="position: absolute; top: 8px; right: 8px;" @click="run()" primary><i class="fas fa-play"></i></MkButton> </div> <MkContainer :foldable="true" class="_gap"> - <template #header><Fa fixed-width/>{{ $ts.output }}</template> + <template #header>{{ $ts.output }}</template> <div class="bepmlvbi"> <div v-for="log in logs" class="log" :key="log.id" :class="{ print: log.print }">{{ log.text }}</div> </div> @@ -20,7 +20,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faTerminal, faPlay } from '@fortawesome/free-solid-svg-icons'; import 'prismjs'; import { highlight, languages } from 'prismjs/components/prism-core'; import 'prismjs/components/prism-clike'; @@ -46,11 +45,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.scratchpad, - icon: faTerminal, + icon: 'fas fa-terminal', }, code: '', logs: [], - faTerminal, faPlay } }, diff --git a/src/client/pages/search.vue b/src/client/pages/search.vue index b670714730..bf228576be 100644 --- a/src/client/pages/search.vue +++ b/src/client/pages/search.vue @@ -8,7 +8,6 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import { faSearch } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import XNotes from '@client/components/notes.vue'; import * as symbols from '@client/symbols'; @@ -22,7 +21,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: computed(() => this.$t('searchWith', { q: this.$route.query.q })), - icon: faSearch + icon: 'fas fa-search' }, pagination: { endpoint: 'notes/search', diff --git a/src/client/pages/settings/2fa.vue b/src/client/pages/settings/2fa.vue index 361611bcb2..aa14f91d71 100644 --- a/src/client/pages/settings/2fa.vue +++ b/src/client/pages/settings/2fa.vue @@ -1,6 +1,6 @@ <template> <section class="_card"> - <div class="_title"><Fa :icon="faLock"/> {{ $ts.twoStepAuthentication }}</div> + <div class="_title"><i class="fas fa-lock"></i> {{ $ts.twoStepAuthentication }}</div> <div class="_content"> <MkButton v-if="!data && !$i.twoFactorEnabled" @click="register">{{ $ts._2fa.registerDevice }}</MkButton> <template v-if="$i.twoFactorEnabled"> @@ -28,7 +28,7 @@ <ol v-if="registration && !registration.error"> <li v-if="registration.stage >= 0"> {{ $ts.tapSecurityKey }} - <Fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 0" /> + <i v-if="registration.saving && registration.stage == 0" class="fas fa-spinner fa-pulse fa-fw"></i> </li> <li v-if="registration.stage >= 1"> <MkForm :disabled="registration.stage != 1 || registration.saving"> @@ -36,7 +36,7 @@ <span>{{ $ts.securityKeyName }}</span> </MkInput> <MkButton @click="registerKey" :disabled="keyName.length == 0">{{ $ts.registerSecurityKey }}</MkButton> - <Fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 1" /> + <i v-if="registration.saving && registration.stage == 1" class="fas fa-spinner fa-pulse fa-fw"></i> </MkForm> </li> </ol> @@ -68,7 +68,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faLock } from '@fortawesome/free-solid-svg-icons'; import { hostname } from '@client/config'; import { byteify, hexify, stringify } from '@client/scripts/2fa'; import MkButton from '@client/components/ui/button.vue'; @@ -93,7 +92,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.twoStepAuthentication, - icon: faLock + icon: 'fas fa-lock' }, data: null, supportsCredentials: !!navigator.credentials, @@ -101,7 +100,6 @@ export default defineComponent({ registration: null, keyName: '', token: null, - faLock }; }, diff --git a/src/client/pages/settings/account-info.vue b/src/client/pages/settings/account-info.vue index 955a0f7845..4d851b7b12 100644 --- a/src/client/pages/settings/account-info.vue +++ b/src/client/pages/settings/account-info.vue @@ -132,7 +132,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormLink from '@client/components/form/link.vue'; @@ -162,7 +161,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.accountInfo, - icon: faInfoCircle + icon: 'fas fa-info-circle' }, stats: null } diff --git a/src/client/pages/settings/accounts.vue b/src/client/pages/settings/accounts.vue new file mode 100644 index 0000000000..a3fa0d4eb0 --- /dev/null +++ b/src/client/pages/settings/accounts.vue @@ -0,0 +1,148 @@ +<template> +<FormBase> + <FormSuspense :p="init"> + <FormButton @click="addAccount" primary><i class="fas fa-plus"></i> {{ $ts.addAccount }}</FormButton> + + <div class="_formItem _button" v-for="account in accounts" :key="account.id" @click="menu(account, $event)"> + <div class="_formPanel lcjjdxlm"> + <div class="avatar"> + <MkAvatar :user="account" class="avatar"/> + </div> + <div class="body"> + <div class="name"> + <MkUserName :user="account"/> + </div> + <div class="acct"> + <MkAcct :user="account"/> + </div> + </div> + </div> + </div> + </FormSuspense> +</FormBase> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import FormSuspense from '@client/components/form/suspense.vue'; +import FormLink from '@client/components/form/link.vue'; +import FormBase from '@client/components/form/base.vue'; +import FormGroup from '@client/components/form/group.vue'; +import FormButton from '@client/components/form/button.vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { getAccounts, addAccount, login } from '@client/account'; + +export default defineComponent({ + components: { + FormBase, + FormSuspense, + FormButton, + }, + + emits: ['info'], + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.accounts, + icon: 'fas fa-users', + }, + storedAccounts: getAccounts().filter(x => x.id !== this.$i.id), + accounts: null, + init: () => os.api('users/show', { + userIds: this.storedAccounts.map(x => x.id) + }).then(accounts => { + this.accounts = accounts; + }), + }; + }, + + mounted() { + this.$emit('info', this[symbols.PAGE_INFO]); + }, + + methods: { + menu(account, ev) { + os.modalMenu([{ + text: this.$ts.switch, + icon: 'fas fa-exchange-alt', + action: () => this.switchAccount(account), + }, { + text: this.$ts.remove, + icon: 'fas fa-trash-alt', + danger: true, + action: () => this.removeAccount(account), + }], ev.currentTarget || ev.target); + }, + + addAccount(ev) { + os.modalMenu([{ + text: this.$ts.existingAccount, + action: () => { this.addExistingAccount(); }, + }, { + text: this.$ts.createAccount, + action: () => { this.createAccount(); }, + }], ev.currentTarget || ev.target); + }, + + addExistingAccount() { + os.popup(import('@client/components/signin-dialog.vue'), {}, { + done: res => { + addAccount(res.id, res.i); + os.success(); + }, + }, 'closed'); + }, + + createAccount() { + os.popup(import('@client/components/signup-dialog.vue'), {}, { + done: res => { + addAccount(res.id, res.i); + this.switchAccountWithToken(res.i); + }, + }, 'closed'); + }, + + switchAccount(account: any) { + const storedAccounts = getAccounts(); + const token = storedAccounts.find(x => x.id === account.id).token; + this.switchAccountWithToken(token); + }, + + switchAccountWithToken(token: string) { + login(token); + }, + } +}); +</script> + +<style lang="scss" scoped> +.lcjjdxlm { + display: flex; + padding: 16px; + + > .avatar { + display: block; + flex-shrink: 0; + margin: 0 12px 0 0; + + > .avatar { + width: 50px; + height: 50px; + } + } + + > .body { + display: flex; + flex-direction: column; + justify-content: center; + width: calc(100% - 62px); + position: relative; + + > .name { + font-weight: bold; + } + } +} +</style> diff --git a/src/client/pages/settings/api.vue b/src/client/pages/settings/api.vue index 9b53399870..396d4405c3 100644 --- a/src/client/pages/settings/api.vue +++ b/src/client/pages/settings/api.vue @@ -8,7 +8,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faKey } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormLink from '@client/components/form/link.vue'; @@ -31,7 +30,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: 'API', - icon: faKey + icon: 'fas fa-key' }, isDesktop: window.innerWidth >= 1100, }; diff --git a/src/client/pages/settings/apps.vue b/src/client/pages/settings/apps.vue index 82bf9b7f8f..c864920ce1 100644 --- a/src/client/pages/settings/apps.vue +++ b/src/client/pages/settings/apps.vue @@ -22,7 +22,7 @@ <div><MkTime :time="token.lastUsedAt"/></div> </div> <div class="actions"> - <button class="_button" @click="revoke(token)"><Fa :icon="faTrashAlt"/></button> + <button class="_button" @click="revoke(token)"><i class="fas fa-trash-alt"></i></button> </div> <details> <summary>{{ $ts.details }}</summary> @@ -39,7 +39,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faTrashAlt, faPlug } from '@fortawesome/free-solid-svg-icons'; import FormPagination from '@client/components/form/pagination.vue'; import FormSelect from '@client/components/form/select.vue'; import FormLink from '@client/components/form/link.vue'; @@ -61,7 +60,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.installedApps, - icon: faPlug, + icon: 'fas fa-plug', }, pagination: { endpoint: 'i/apps', @@ -70,7 +69,6 @@ export default defineComponent({ sort: '+lastUsedAt' } }, - faTrashAlt, faPlug }; }, diff --git a/src/client/pages/settings/deck.vue b/src/client/pages/settings/deck.vue index 84992adc09..05f3061ca1 100644 --- a/src/client/pages/settings/deck.vue +++ b/src/client/pages/settings/deck.vue @@ -31,7 +31,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faImage, faCog, faColumns } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormLink from '@client/components/form/link.vue'; import FormRadios from '@client/components/form/radios.vue'; @@ -59,9 +58,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.deck, - icon: faColumns + icon: 'fas fa-columns' }, - faImage, faCog, } }, diff --git a/src/client/pages/settings/drive.vue b/src/client/pages/settings/drive.vue index 675b025ab8..3da2a21dc7 100644 --- a/src/client/pages/settings/drive.vue +++ b/src/client/pages/settings/drive.vue @@ -27,7 +27,7 @@ <FormButton :center="false" @click="chooseUploadFolder()" primary> {{ $ts.uploadFolder }} <template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template> - <template #suffixIcon><Fa :icon="faFolderOpen"/></template> + <template #suffixIcon><i class="fas fa-folder-open"></i></template> </FormButton> </FormBase> </template> @@ -36,8 +36,6 @@ import { defineComponent } from 'vue'; import * as tinycolor from 'tinycolor2'; import ApexCharts from 'apexcharts'; -import { faCloud, faFolderOpen } from '@fortawesome/free-solid-svg-icons'; -import { faClock, faEyeSlash, faTrashAlt } from '@fortawesome/free-regular-svg-icons'; import FormButton from '@client/components/form/button.vue'; import FormGroup from '@client/components/form/group.vue'; import FormKeyValueView from '@client/components/form/key-value-view.vue'; @@ -60,13 +58,12 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.drive, - icon: faCloud + icon: 'fas fa-cloud' }, fetching: true, usage: null, capacity: null, uploadFolder: null, - faCloud, faClock, faEyeSlash, faFolderOpen, faTrashAlt } }, diff --git a/src/client/pages/settings/email-address.vue b/src/client/pages/settings/email-address.vue index 97c5d396ce..28eeeb6b73 100644 --- a/src/client/pages/settings/email-address.vue +++ b/src/client/pages/settings/email-address.vue @@ -13,8 +13,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCog } from '@fortawesome/free-solid-svg-icons'; -import { faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons'; import FormButton from '@client/components/form/button.vue'; import FormInput from '@client/components/form/input.vue'; import FormBase from '@client/components/form/base.vue'; @@ -36,11 +34,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.emailAddress, - icon: faEnvelope + icon: 'fas fa-envelope' }, emailAddress: null, code: null, - faCog } }, diff --git a/src/client/pages/settings/email-notification.vue b/src/client/pages/settings/email-notification.vue index cc28bac4b0..ac3402568a 100644 --- a/src/client/pages/settings/email-notification.vue +++ b/src/client/pages/settings/email-notification.vue @@ -25,8 +25,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCog } from '@fortawesome/free-solid-svg-icons'; -import { faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons'; import FormButton from '@client/components/form/button.vue'; import FormSwitch from '@client/components/form/switch.vue'; import FormBase from '@client/components/form/base.vue'; @@ -49,7 +47,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.emailNotification, - icon: faEnvelope + icon: 'fas fa-envelope' }, mention: this.$i.emailNotificationTypes.includes('mention'), diff --git a/src/client/pages/settings/email.vue b/src/client/pages/settings/email.vue index 04f433f9ae..aa20d9d94e 100644 --- a/src/client/pages/settings/email.vue +++ b/src/client/pages/settings/email.vue @@ -3,14 +3,14 @@ <FormGroup> <template #label>{{ $ts.emailAddress }}</template> <FormLink to="/settings/email/address"> - <template v-if="$i.email && !$i.emailVerified" #icon><Fa :icon="faExclamationTriangle" style="color: var(--warn);"/></template> - <template v-else-if="$i.email && $i.emailVerified" #icon><Fa :icon="faCheck" style="color: var(--success);"/></template> + <template v-if="$i.email && !$i.emailVerified" #icon><i class="fas fa-exclamation-triangle" style="color: var(--warn);"></i></template> + <template v-else-if="$i.email && $i.emailVerified" #icon><i class="fas fa-check" style="color: var(--success);"></i></template> {{ $i.email || $ts.notSet }} </FormLink> </FormGroup> <FormLink to="/settings/email/notification"> - <template #icon><Fa :icon="faBell"/></template> + <template #icon><i class="fas fa-bell"></i></template> {{ $ts.emailNotification }} </FormLink> @@ -22,8 +22,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCog, faExclamationTriangle, faCheck } from '@fortawesome/free-solid-svg-icons'; -import { faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons'; import FormButton from '@client/components/form/button.vue'; import FormLink from '@client/components/form/link.vue'; import FormBase from '@client/components/form/base.vue'; @@ -47,9 +45,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.email, - icon: faEnvelope + icon: 'fas fa-envelope' }, - faCog, faExclamationTriangle, faCheck, faBell } }, diff --git a/src/client/pages/settings/experimental-features.vue b/src/client/pages/settings/experimental-features.vue index 25453b7e10..f8d5e419e9 100644 --- a/src/client/pages/settings/experimental-features.vue +++ b/src/client/pages/settings/experimental-features.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faFlask } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormLink from '@client/components/form/link.vue'; @@ -34,7 +33,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.experimentalFeatures, - icon: faFlask + icon: 'fas fa-flask' }, stats: null } diff --git a/src/client/pages/settings/general.vue b/src/client/pages/settings/general.vue index 2963ddf432..fdbae0b8a1 100644 --- a/src/client/pages/settings/general.vue +++ b/src/client/pages/settings/general.vue @@ -83,7 +83,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faImage, faCog, faColumns, faCogs } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormRadios from '@client/components/form/radios.vue'; @@ -117,13 +116,12 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.general, - icon: faCogs + icon: 'fas fa-cogs' }, langs, lang: localStorage.getItem('lang'), fontSize: localStorage.getItem('fontSize'), useSystemFont: localStorage.getItem('useSystemFont') != null, - faImage, faCog, faColumns } }, diff --git a/src/client/pages/settings/import-export.vue b/src/client/pages/settings/import-export.vue index 1591a9d548..e77efb4429 100644 --- a/src/client/pages/settings/import-export.vue +++ b/src/client/pages/settings/import-export.vue @@ -2,32 +2,31 @@ <FormBase> <FormGroup> <template #label>{{ $ts._exportOrImport.allNotes }}</template> - <FormButton @click="doExport('notes')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton> + <FormButton @click="doExport('notes')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> </FormGroup> <FormGroup> <template #label>{{ $ts._exportOrImport.followingList }}</template> - <FormButton @click="doExport('following')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton> - <FormButton @click="doImport('following', $event)"><Fa :icon="faUpload"/> {{ $ts.import }}</FormButton> + <FormButton @click="doExport('following')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> + <FormButton @click="doImport('following', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</FormButton> </FormGroup> <FormGroup> <template #label>{{ $ts._exportOrImport.userLists }}</template> - <FormButton @click="doExport('user-lists')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton> - <FormButton @click="doImport('user-lists', $event)"><Fa :icon="faUpload"/> {{ $ts.import }}</FormButton> + <FormButton @click="doExport('user-lists')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> + <FormButton @click="doImport('user-lists', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</FormButton> </FormGroup> <FormGroup> <template #label>{{ $ts._exportOrImport.muteList }}</template> - <FormButton @click="doExport('mute')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton> + <FormButton @click="doExport('mute')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> </FormGroup> <FormGroup> <template #label>{{ $ts._exportOrImport.blockingList }}</template> - <FormButton @click="doExport('blocking')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton> + <FormButton @click="doExport('blocking')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> </FormGroup> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faDownload, faUpload, faBoxes } from '@fortawesome/free-solid-svg-icons'; import FormSelect from '@client/components/form/select.vue'; import FormButton from '@client/components/form/button.vue'; import FormBase from '@client/components/form/base.vue'; @@ -49,9 +48,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.importAndExport, - icon: faBoxes + icon: 'fas fa-boxes' }, - faDownload, faUpload, faBoxes } }, diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index eb7469c861..049e912898 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -3,31 +3,39 @@ <div class="nav" v-if="!narrow || page == null"> <FormBase> <FormGroup> + <div class="_formItem"> + <div class="_formPanel lwjxoukj"> + <MkAvatar :user="$i" class="avatar"/> + </div> + </div> + <FormLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</FormLink> + </FormGroup> + <FormGroup> <template #label>{{ $ts.basicSettings }}</template> - <FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><Fa :icon="faUser"/></template>{{ $ts.profile }}</FormLink> - <FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><Fa :icon="faLockOpen"/></template>{{ $ts.privacy }}</FormLink> - <FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><Fa :icon="faLaugh"/></template>{{ $ts.reaction }}</FormLink> - <FormLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><Fa :icon="faCloud"/></template>{{ $ts.drive }}</FormLink> - <FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><Fa :icon="faBell"/></template>{{ $ts.notifications }}</FormLink> - <FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $ts.email }}</FormLink> - <FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><Fa :icon="faShareAlt"/></template>{{ $ts.integration }}</FormLink> - <FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><Fa :icon="faLock"/></template>{{ $ts.security }}</FormLink> + <FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</FormLink> + <FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><i class="fas fa-lock-open"></i></template>{{ $ts.privacy }}</FormLink> + <FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.reaction }}</FormLink> + <FormLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.drive }}</FormLink> + <FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><i class="fas fa-bell"></i></template>{{ $ts.notifications }}</FormLink> + <FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.email }}</FormLink> + <FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</FormLink> + <FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</FormLink> </FormGroup> <FormGroup> <template #label>{{ $ts.clientSettings }}</template> - <FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.general }}</FormLink> - <FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><Fa :icon="faPalette"/></template>{{ $ts.theme }}</FormLink> - <FormLink :active="page === 'sidebar'" replace to="/settings/sidebar"><template #icon><Fa :icon="faListUl"/></template>{{ $ts.sidebar }}</FormLink> - <FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><Fa :icon="faMusic"/></template>{{ $ts.sounds }}</FormLink> - <FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><Fa :icon="faPlug"/></template>{{ $ts.plugins }}</FormLink> + <FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</FormLink> + <FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</FormLink> + <FormLink :active="page === 'sidebar'" replace to="/settings/sidebar"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.sidebar }}</FormLink> + <FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</FormLink> + <FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</FormLink> </FormGroup> <FormGroup> <template #label>{{ $ts.otherSettings }}</template> - <FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><Fa :icon="faBoxes"/></template>{{ $ts.importAndExport }}</FormLink> - <FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><Fa :icon="faBan"/></template>{{ $ts.muteAndBlock }}</FormLink> - <FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><Fa :icon="faCommentSlash"/></template>{{ $ts.wordMute }}</FormLink> - <FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><Fa :icon="faKey"/></template>API</FormLink> - <FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $ts.other }}</FormLink> + <FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><i class="fas fa-boxes"></i></template>{{ $ts.importAndExport }}</FormLink> + <FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.muteAndBlock }}</FormLink> + <FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><i class="fas fa-comment-slash"></i></template>{{ $ts.wordMute }}</FormLink> + <FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><i class="fas fa-key"></i></template>API</FormLink> + <FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><i class="fas fa-ellipsis-h"></i></template>{{ $ts.other }}</FormLink> </FormGroup> <FormGroup> <FormButton @click="clear">{{ $ts.clearCache }}</FormButton> @@ -45,8 +53,6 @@ <script lang="ts"> import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; -import { faCog, faPalette, faPlug, faUser, faListUl, faLock, faCommentSlash, faMusic, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faCloud } from '@fortawesome/free-solid-svg-icons'; -import { faLaugh, faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons'; import { i18n } from '@client/i18n'; import FormLink from '@client/components/form/link.vue'; import FormGroup from '@client/components/form/group.vue'; @@ -75,7 +81,7 @@ export default defineComponent({ setup(props, context) { const indexInfo = { title: i18n.locale.settings, - icon: faCog + icon: 'fas fa-cog' }; const INFO = ref(indexInfo); const page = ref(props.initialPage); @@ -89,6 +95,7 @@ export default defineComponent({ const component = computed(() => { if (page.value == null) return null; switch (page.value) { + case 'accounts': return defineAsyncComponent(() => import('./accounts.vue')); case 'profile': return defineAsyncComponent(() => import('./profile.vue')); case 'privacy': return defineAsyncComponent(() => import('./privacy.vue')); case 'reaction': return defineAsyncComponent(() => import('./reaction.vue')); @@ -183,7 +190,6 @@ export default defineComponent({ localStorage.removeItem('theme'); unisonReload(); }, - faPalette, faPlug, faUser, faListUl, faLock, faLaugh, faCommentSlash, faMusic, faBell, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faEnvelope, faCloud, }; }, }); @@ -212,4 +218,15 @@ export default defineComponent({ } } } + +.lwjxoukj { + padding: 16px; + + > .avatar { + display: block; + margin: auto; + width: 42px; + height: 42px; + } +} </style> diff --git a/src/client/pages/settings/integration.vue b/src/client/pages/settings/integration.vue index 49f955bc35..2d2be04051 100644 --- a/src/client/pages/settings/integration.vue +++ b/src/client/pages/settings/integration.vue @@ -1,7 +1,7 @@ <template> <FormBase> <div class="_formItem" v-if="enableTwitterIntegration"> - <div class="_formLabel"><Fa :icon="faTwitter"/> Twitter</div> + <div class="_formLabel"><i class="fab fa-twitter"></i> Twitter</div> <div class="_formPanel" style="padding: 16px;"> <p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p> <MkButton v-if="integrations.twitter" @click="disconnectTwitter" danger>{{ $ts.disconnectSerice }}</MkButton> @@ -10,7 +10,7 @@ </div> <div class="_formItem" v-if="enableDiscordIntegration"> - <div class="_formLabel"><Fa :icon="faDiscord"/> Discord</div> + <div class="_formLabel"><i class="fab fa-discord"></i> Discord</div> <div class="_formPanel" style="padding: 16px;"> <p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p> <MkButton v-if="integrations.discord" @click="disconnectDiscord" danger>{{ $ts.disconnectSerice }}</MkButton> @@ -19,7 +19,7 @@ </div> <div class="_formItem" v-if="enableGithubIntegration"> - <div class="_formLabel"><Fa :icon="faGithub"/> GitHub</div> + <div class="_formLabel"><i class="fab fa-github"></i> GitHub</div> <div class="_formPanel" style="padding: 16px;"> <p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p> <MkButton v-if="integrations.github" @click="disconnectGithub" danger>{{ $ts.disconnectSerice }}</MkButton> @@ -31,8 +31,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faShareAlt } from '@fortawesome/free-solid-svg-icons'; -import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons'; import { apiUrl } from '@client/config'; import FormBase from '@client/components/form/base.vue'; import MkButton from '@client/components/ui/button.vue'; @@ -51,7 +49,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.integration, - icon: faShareAlt + icon: 'fas fa-share-alt' }, apiUrl, twitterForm: null, @@ -60,7 +58,6 @@ export default defineComponent({ enableTwitterIntegration: false, enableDiscordIntegration: false, enableGithubIntegration: false, - faShareAlt, faTwitter, faDiscord, faGithub }; }, diff --git a/src/client/pages/settings/mute-block.vue b/src/client/pages/settings/mute-block.vue index 11450e049b..dde0199e18 100644 --- a/src/client/pages/settings/mute-block.vue +++ b/src/client/pages/settings/mute-block.vue @@ -33,7 +33,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faBan } from '@fortawesome/free-solid-svg-icons'; import MkPagination from '@client/components/ui/pagination.vue'; import MkTab from '@client/components/tab.vue'; import FormInfo from '@client/components/form/info.vue'; @@ -60,7 +59,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.muteAndBlock, - icon: faBan + icon: 'fas fa-ban' }, tab: 'mute', mutingPagination: { diff --git a/src/client/pages/settings/notifications.vue b/src/client/pages/settings/notifications.vue index ea72bcfee8..ec95452ba2 100644 --- a/src/client/pages/settings/notifications.vue +++ b/src/client/pages/settings/notifications.vue @@ -11,8 +11,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCog } from '@fortawesome/free-solid-svg-icons'; -import { faBell } from '@fortawesome/free-regular-svg-icons'; import FormButton from '@client/components/form/button.vue'; import FormLink from '@client/components/form/link.vue'; import FormBase from '@client/components/form/base.vue'; @@ -35,9 +33,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.notifications, - icon: faBell + icon: 'fas fa-bell' }, - faCog } }, diff --git a/src/client/pages/settings/other.vue b/src/client/pages/settings/other.vue index 2bd9c2476c..f73ff9cb21 100644 --- a/src/client/pages/settings/other.vue +++ b/src/client/pages/settings/other.vue @@ -21,10 +21,10 @@ </template> </FormGroup> - <FormLink to="/settings/registry"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.registry }}</FormLink> + <FormLink to="/settings/registry"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.registry }}</FormLink> - <FormLink to="/bios" behavior="browser"><template #icon><Fa :icon="faDoorOpen"/></template>BIOS</FormLink> - <FormLink to="/cli" behavior="browser"><template #icon><Fa :icon="faDoorOpen"/></template>CLI</FormLink> + <FormLink to="/bios" behavior="browser"><template #icon><i class="fas fa-door-open"></i></template>BIOS</FormLink> + <FormLink to="/cli" behavior="browser"><template #icon><i class="fas fa-door-open"></i></template>CLI</FormLink> <FormButton @click="closeAccount" danger>{{ $ts.closeAccount }}</FormButton> </FormBase> @@ -32,7 +32,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faEllipsisH, faCogs, faDoorOpen } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormLink from '@client/components/form/link.vue'; @@ -62,10 +61,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.other, - icon: faEllipsisH + icon: 'fas fa-ellipsis-h' }, debug, - faCogs, faDoorOpen, } }, diff --git a/src/client/pages/settings/plugin.install.vue b/src/client/pages/settings/plugin.install.vue index bc80188fc6..30cbf58ad7 100644 --- a/src/client/pages/settings/plugin.install.vue +++ b/src/client/pages/settings/plugin.install.vue @@ -8,13 +8,12 @@ </FormTextarea> </FormGroup> - <FormButton @click="install" :disabled="code == null" primary inline><Fa :icon="faCheck"/> {{ $ts.install }}</FormButton> + <FormButton @click="install" :disabled="code == null" primary inline><i class="fas fa-check"></i> {{ $ts.install }}</FormButton> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye } from '@fortawesome/free-solid-svg-icons'; import { AiScript, parse } from '@syuilo/aiscript'; import { serialize } from '@syuilo/aiscript/built/serializer'; import { v4 as uuid } from 'uuid'; @@ -49,10 +48,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._plugin.install, - icon: faDownload + icon: 'fas fa-download' }, code: null, - faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye } }, diff --git a/src/client/pages/settings/plugin.manage.vue b/src/client/pages/settings/plugin.manage.vue index d7aabe560e..3df87ca084 100644 --- a/src/client/pages/settings/plugin.manage.vue +++ b/src/client/pages/settings/plugin.manage.vue @@ -22,8 +22,8 @@ </div> <div class="_formItem"> <div class="_formPanel" style="padding: 16px;"> - <MkButton @click="config(plugin)" inline v-if="plugin.config"><Fa :icon="faCog"/> {{ $ts.settings }}</MkButton> - <MkButton @click="uninstall(plugin)" inline danger><Fa :icon="faTrashAlt"/> {{ $ts.uninstall }}</MkButton> + <MkButton @click="config(plugin)" inline v-if="plugin.config"><i class="fas fa-cog"></i> {{ $ts.settings }}</MkButton> + <MkButton @click="uninstall(plugin)" inline danger><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</MkButton> </div> </div> </FormGroup> @@ -32,7 +32,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faPlug, faSave, faTrashAlt, faFolderOpen, faDownload, faCog } from '@fortawesome/free-solid-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkTextarea from '@client/components/ui/textarea.vue'; import MkSelect from '@client/components/ui/select.vue'; @@ -59,10 +58,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._plugin.manage, - icon: faPlug + icon: 'fas fa-plug' }, plugins: ColdDeviceStorage.get('plugins'), - faPlug, faSave, faTrashAlt, faFolderOpen, faDownload, faCog } }, diff --git a/src/client/pages/settings/plugin.vue b/src/client/pages/settings/plugin.vue index bee4e57ec3..13eaca07fd 100644 --- a/src/client/pages/settings/plugin.vue +++ b/src/client/pages/settings/plugin.vue @@ -1,13 +1,12 @@ <template> <FormBase> - <FormLink to="/settings/plugin/install"><template #icon><Fa :icon="faDownload"/></template>{{ $ts._plugin.install }}</FormLink> - <FormLink to="/settings/plugin/manage"><template #icon><Fa :icon="faFolderOpen"/></template>{{ $ts._plugin.manage }}<template #suffix>{{ plugins }}</template></FormLink> + <FormLink to="/settings/plugin/install"><template #icon><i class="fas fa-download"></i></template>{{ $ts._plugin.install }}</FormLink> + <FormLink to="/settings/plugin/manage"><template #icon><i class="fas fa-folder-open"></i></template>{{ $ts._plugin.manage }}<template #suffix>{{ plugins }}</template></FormLink> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faPlug, faSave, faTrashAlt, faFolderOpen, faDownload, faCog } from '@fortawesome/free-solid-svg-icons'; import FormBase from '@client/components/form/base.vue'; import FormGroup from '@client/components/form/group.vue'; import FormLink from '@client/components/form/link.vue'; @@ -27,10 +26,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.plugins, - icon: faPlug + icon: 'fas fa-plug' }, plugins: ColdDeviceStorage.get('plugins').length, - faPlug, faSave, faTrashAlt, faFolderOpen, faDownload, faCog } }, diff --git a/src/client/pages/settings/privacy.vue b/src/client/pages/settings/privacy.vue index c8df378410..4095e744c2 100644 --- a/src/client/pages/settings/privacy.vue +++ b/src/client/pages/settings/privacy.vue @@ -33,7 +33,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faLockOpen } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormBase from '@client/components/form/base.vue'; @@ -56,7 +55,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.privacy, - icon: faLockOpen + icon: 'fas fa-lock-open' }, isLocked: false, autoAcceptFollowed: false, diff --git a/src/client/pages/settings/profile.vue b/src/client/pages/settings/profile.vue index 5ec580a206..de7e86bd12 100644 --- a/src/client/pages/settings/profile.vue +++ b/src/client/pages/settings/profile.vue @@ -19,12 +19,12 @@ <FormInput v-model:value="location" manual-save> <span>{{ $ts.location }}</span> - <template #prefix><Fa :icon="faMapMarkerAlt"/></template> + <template #prefix><i class="fas fa-map-marker-alt"></i></template> </FormInput> <FormInput v-model:value="birthday" type="date" manual-save> <span>{{ $ts.birthday }}</span> - <template #prefix><Fa :icon="faBirthdayCake"/></template> + <template #prefix><i class="fas fa-birthday-cake"></i></template> </FormInput> <FormSelect v-model:value="lang"> @@ -47,8 +47,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faUnlockAlt, faCogs, faUser, faMapMarkerAlt, faBirthdayCake } from '@fortawesome/free-solid-svg-icons'; -import { faSave } from '@fortawesome/free-regular-svg-icons'; import FormButton from '@client/components/form/button.vue'; import FormInput from '@client/components/form/input.vue'; import FormTextarea from '@client/components/form/textarea.vue'; @@ -78,7 +76,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.profile, - icon: faUser + icon: 'fas fa-user' }, host, langs, @@ -101,7 +99,6 @@ export default defineComponent({ isCat: false, alwaysMarkNsfw: false, saving: false, - faSave, faUnlockAlt, faCogs, faUser, faMapMarkerAlt, faBirthdayCake } }, diff --git a/src/client/pages/settings/reaction.vue b/src/client/pages/settings/reaction.vue index 0293f53fa8..9bffd5f903 100644 --- a/src/client/pages/settings/reaction.vue +++ b/src/client/pages/settings/reaction.vue @@ -10,7 +10,7 @@ </button> </template> <template #footer> - <button class="_button add" @click="chooseEmoji"><Fa :icon="faPlus"/></button> + <button class="_button add" @click="chooseEmoji"><i class="fas fa-plus"></i></button> </template> </XDraggable> </div> @@ -29,15 +29,13 @@ <option :value="2">{{ $ts.medium }}</option> <option :value="3">{{ $ts.large }}</option> </FormRadios> - <FormButton @click="preview"><Fa :icon="faEye"/> {{ $ts.preview }}</FormButton> - <FormButton danger @click="setDefault"><Fa :icon="faUndo"/> {{ $ts.default }}</FormButton> + <FormButton @click="preview"><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton> + <FormButton danger @click="setDefault"><i class="fas fa-undo"></i> {{ $ts.default }}</FormButton> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faLaugh, faSave, faEye } from '@fortawesome/free-regular-svg-icons'; -import { faUndo, faPlus } from '@fortawesome/free-solid-svg-icons'; import XDraggable from 'vuedraggable'; import FormInput from '@client/components/form/input.vue'; import FormRadios from '@client/components/form/radios.vue'; @@ -62,14 +60,13 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.reaction, - icon: faLaugh, + icon: 'fas fa-laugh', action: { - icon: faEye, + icon: 'fas fa-eye', handler: this.preview } }, reactions: JSON.parse(JSON.stringify(this.$store.state.reactions)), - faLaugh, faSave, faEye, faUndo, faPlus } }, diff --git a/src/client/pages/settings/registry.keys.vue b/src/client/pages/settings/registry.keys.vue index 5cdfdc1332..f71589ba4f 100644 --- a/src/client/pages/settings/registry.keys.vue +++ b/src/client/pages/settings/registry.keys.vue @@ -22,7 +22,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faCogs } from '@fortawesome/free-solid-svg-icons'; import * as JSON5 from 'json5'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; @@ -57,7 +56,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.registry, - icon: faCogs + icon: 'fas fa-cogs' }, keys: null, } diff --git a/src/client/pages/settings/registry.value.vue b/src/client/pages/settings/registry.value.vue index 7d5756af99..48245ae99f 100644 --- a/src/client/pages/settings/registry.value.vue +++ b/src/client/pages/settings/registry.value.vue @@ -22,7 +22,7 @@ <FormTextarea tall v-model:value="valueForEditor" class="_monospace" style="tab-size: 2;"> <span>{{ $ts.value }} (JSON)</span> </FormTextarea> - <FormButton @click="save" primary><Fa :icon="faSave"/> {{ $ts.save }}</FormButton> + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> </FormGroup> <FormKeyValueView> @@ -30,14 +30,13 @@ <template #value><MkTime :time="value.updatedAt" mode="detail"/></template> </FormKeyValueView> - <FormButton danger @click="del"><Fa :icon="faTrash"/> {{ $ts.delete }}</FormButton> + <FormButton danger @click="del"><i class="fas fa-trash"></i> {{ $ts.delete }}</FormButton> </template> </FormBase> </template> <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faCogs, faSave, faTrash } from '@fortawesome/free-solid-svg-icons'; import * as JSON5 from 'json5'; import FormInfo from '@client/components/form/info.vue'; import FormSwitch from '@client/components/form/switch.vue'; @@ -77,11 +76,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.registry, - icon: faCogs + icon: 'fas fa-cogs' }, value: null, valueForEditor: null, - faSave, faTrash, } }, diff --git a/src/client/pages/settings/registry.vue b/src/client/pages/settings/registry.vue index 085389fc95..5ba1bc751b 100644 --- a/src/client/pages/settings/registry.vue +++ b/src/client/pages/settings/registry.vue @@ -10,7 +10,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faCogs } from '@fortawesome/free-solid-svg-icons'; import * as JSON5 from 'json5'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; @@ -39,7 +38,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.registry, - icon: faCogs + icon: 'fas fa-cogs' }, scopes: null, } diff --git a/src/client/pages/settings/security.vue b/src/client/pages/settings/security.vue index 64733c55a2..b70fa5a9f3 100644 --- a/src/client/pages/settings/security.vue +++ b/src/client/pages/settings/security.vue @@ -1,15 +1,15 @@ <template> <FormBase> <X2fa/> - <FormLink to="/settings/2fa"><template #icon><Fa :icon="faMobileAlt"/></template>{{ $ts.twoStepAuthentication }}</FormLink> + <FormLink to="/settings/2fa"><template #icon><i class="fas fa-mobile-alt"></i></template>{{ $ts.twoStepAuthentication }}</FormLink> <FormButton primary @click="change()">{{ $ts.changePassword }}</FormButton> <FormPagination :pagination="pagination"> <template #label>{{ $ts.signinHistory }}</template> <template #default="{items}"> <div class="_formPanel timnmucd" v-for="item in items" :key="item.id"> <header> - <Fa class="icon succ" :icon="faCheck" v-if="item.success"/> - <Fa class="icon fail" :icon="faTimesCircle" v-else/> + <i v-if="item.success" class="fas fa-check icon succ"></i> + <i v-else class="fas fa-times-circle icon fail"></i> <code class="ip _monospace">{{ item.ip }}</code> <MkTime :time="item.createdAt" class="time"/> </header> @@ -17,7 +17,7 @@ </template> </FormPagination> <FormGroup> - <FormButton danger @click="regenerateToken"><Fa :icon="faSyncAlt"/> {{ $ts.regenerateLoginToken }}</FormButton> + <FormButton danger @click="regenerateToken"><i class="fas fa-sync-alt"></i> {{ $ts.regenerateLoginToken }}</FormButton> <template #caption>{{ $ts.regenerateLoginTokenDescription }}</template> </FormGroup> </FormBase> @@ -25,7 +25,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faCheck, faTimesCircle, faLock, faSyncAlt, faMobileAlt } from '@fortawesome/free-solid-svg-icons'; import FormBase from '@client/components/form/base.vue'; import FormLink from '@client/components/form/link.vue'; import FormGroup from '@client/components/form/group.vue'; @@ -49,13 +48,12 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.security, - icon: faLock + icon: 'fas fa-lock' }, pagination: { endpoint: 'i/signin-history', limit: 5, }, - faLock, faSyncAlt, faCheck, faTimesCircle, faMobileAlt, } }, diff --git a/src/client/pages/settings/sidebar.vue b/src/client/pages/settings/sidebar.vue index adeec2f636..f0172e945f 100644 --- a/src/client/pages/settings/sidebar.vue +++ b/src/client/pages/settings/sidebar.vue @@ -12,14 +12,13 @@ <!-- <MkRadio v-model="sidebarDisplay" value="hide" disabled>{{ $ts._sidebar.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 --> </FormRadios> - <FormButton @click="save()" primary><Fa :icon="faSave"/> {{ $ts.save }}</FormButton> - <FormButton @click="reset()" danger><Fa :icon="faRedo"/> {{ $ts.default }}</FormButton> + <FormButton @click="save()" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> + <FormButton @click="reset()" danger><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faListUl, faSave, faRedo } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormTextarea from '@client/components/form/textarea.vue'; import FormRadios from '@client/components/form/radios.vue'; @@ -45,11 +44,10 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.sidebar, - icon: faListUl + icon: 'fas fa-list-ul' }, menuDef: sidebarDef, items: '', - faSave, faRedo } }, diff --git a/src/client/pages/settings/sounds.vue b/src/client/pages/settings/sounds.vue index 54be003115..f56ec4cd89 100644 --- a/src/client/pages/settings/sounds.vue +++ b/src/client/pages/settings/sounds.vue @@ -1,7 +1,7 @@ <template> <FormBase> <FormRange v-model:value="masterVolume" :min="0" :max="1" :step="0.05"> - <template #label><Fa :icon="volumeIcon" :key="volumeIcon"/> {{ $ts.masterVolume }}</template> + <template #label><i class="fas fa-volume-icon"></i> {{ $ts.masterVolume }}</template> </FormRange> <FormGroup> @@ -9,17 +9,16 @@ <FormButton v-for="type in Object.keys(sounds)" :key="type" :center="false" @click="edit(type)"> {{ $t('_sfx.' + type) }} <template #suffix>{{ sounds[type].type || $ts.none }}</template> - <template #suffixIcon><Fa :icon="faChevronDown"/></template> + <template #suffixIcon><i class="fas fa-chevron-down"></i></template> </FormButton> </FormGroup> - <FormButton @click="reset()" danger><Fa :icon="faRedo"/> {{ $ts.default }}</FormButton> + <FormButton @click="reset()" danger><i class="fas fa-redo"></i> {{ $ts.default }}</FormButton> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faMusic, faPlay, faVolumeUp, faVolumeMute, faChevronDown, faRedo } from '@fortawesome/free-solid-svg-icons'; import FormRange from '@client/components/form/range.vue'; import FormSelect from '@client/components/form/select.vue'; import FormBase from '@client/components/form/base.vue'; @@ -71,10 +70,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.sounds, - icon: faMusic + icon: 'fas fa-music' }, sounds: {}, - faMusic, faPlay, faVolumeUp, faVolumeMute, faChevronDown, faRedo, } }, @@ -84,7 +82,7 @@ export default defineComponent({ set(value) { ColdDeviceStorage.set('sound_masterVolume', value); } }, volumeIcon() { - return this.masterVolume === 0 ? faVolumeMute : faVolumeUp; + return this.masterVolume === 0 ? 'fas fa-volume-mute' : 'fas fa-volume-up'; } }, diff --git a/src/client/pages/settings/theme.install.vue b/src/client/pages/settings/theme.install.vue index 744d1aba44..d719cc801f 100644 --- a/src/client/pages/settings/theme.install.vue +++ b/src/client/pages/settings/theme.install.vue @@ -4,16 +4,15 @@ <FormTextarea v-model:value="installThemeCode"> <span>{{ $ts._theme.code }}</span> </FormTextarea> - <FormButton @click="() => preview(installThemeCode)" :disabled="installThemeCode == null" inline><Fa :icon="faEye"/> {{ $ts.preview }}</FormButton> + <FormButton @click="() => preview(installThemeCode)" :disabled="installThemeCode == null" inline><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton> </FormGroup> - <FormButton @click="() => install(installThemeCode)" :disabled="installThemeCode == null" primary inline><Fa :icon="faCheck"/> {{ $ts.install }}</FormButton> + <FormButton @click="() => install(installThemeCode)" :disabled="installThemeCode == null" primary inline><i class="fas fa-check"></i> {{ $ts.install }}</FormButton> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye } from '@fortawesome/free-solid-svg-icons'; import * as JSON5 from 'json5'; import FormTextarea from '@client/components/form/textarea.vue'; import FormSelect from '@client/components/form/select.vue'; @@ -45,10 +44,9 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._theme.install, - icon: faDownload + icon: 'fas fa-download' }, installThemeCode: null, - faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye } }, diff --git a/src/client/pages/settings/theme.manage.vue b/src/client/pages/settings/theme.manage.vue index ea9d5949ff..7cc7a0169a 100644 --- a/src/client/pages/settings/theme.manage.vue +++ b/src/client/pages/settings/theme.manage.vue @@ -20,14 +20,13 @@ <span>{{ $ts._theme.code }}</span> <template #desc><button @click="copyThemeCode()" class="_textButton">{{ $ts.copy }}</button></template> </FormTextarea> - <FormButton @click="uninstall()" danger v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><Fa :icon="faTrashAlt"/> {{ $ts.uninstall }}</FormButton> + <FormButton @click="uninstall()" danger v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</FormButton> </template> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye } from '@fortawesome/free-solid-svg-icons'; import * as JSON5 from 'json5'; import FormTextarea from '@client/components/form/textarea.vue'; import FormSelect from '@client/components/form/select.vue'; @@ -60,12 +59,11 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._theme.manage, - icon: faFolderOpen + icon: 'fas fa-folder-open' }, installedThemes: getThemes(), builtinThemes, selectedThemeId: null, - faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye } }, diff --git a/src/client/pages/settings/theme.vue b/src/client/pages/settings/theme.vue index 606e10ab7a..1eb0d68be5 100644 --- a/src/client/pages/settings/theme.vue +++ b/src/client/pages/settings/theme.vue @@ -71,22 +71,21 @@ <FormButton primary v-else @click="wallpaper = null">{{ $ts.removeWallpaper }}</FormButton> <FormGroup> - <FormLink to="https://assets.msky.cafe/theme/list" external><template #icon><Fa :icon="faGlobe"/></template>{{ $ts._theme.explore }}</FormLink> - <FormLink to="/settings/theme/install"><template #icon><Fa :icon="faDownload"/></template>{{ $ts._theme.install }}</FormLink> + <FormLink to="https://assets.msky.cafe/theme/list" external><template #icon><i class="fas fa-globe"></i></template>{{ $ts._theme.explore }}</FormLink> + <FormLink to="/settings/theme/install"><template #icon><i class="fas fa-download"></i></template>{{ $ts._theme.install }}</FormLink> </FormGroup> <FormGroup> - <FormLink to="/theme-editor"><template #icon><Fa :icon="faPaintRoller"/></template>{{ $ts._theme.make }}</FormLink> - <!--<FormLink to="/advanced-theme-editor"><template #icon><Fa :icon="faPaintRoller"/></template>{{ $ts._theme.make }} ({{ $ts.advanced }})</FormLink>--> + <FormLink to="/theme-editor"><template #icon><i class="fas fa-paint-roller"></i></template>{{ $ts._theme.make }}</FormLink> + <!--<FormLink to="/advanced-theme-editor"><template #icon><i class="fas fa-paint-roller"></i></template>{{ $ts._theme.make }} ({{ $ts.advanced }})</FormLink>--> </FormGroup> - <FormLink to="/settings/theme/manage"><template #icon><Fa :icon="faFolderOpen"/></template>{{ $ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink> + <FormLink to="/settings/theme/manage"><template #icon><i class="fas fa-folder-open"></i></template>{{ $ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink> </FormBase> </template> <script lang="ts"> import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue'; -import { faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye, faGlobe, faPaintRoller } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormBase from '@client/components/form/base.vue'; @@ -117,7 +116,7 @@ export default defineComponent({ setup(props, { emit }) { const INFO = { title: i18n.locale.theme, - icon: faPalette + icon: 'fas fa-palette' }; const installedThemes = ref(getThemes()); @@ -191,7 +190,6 @@ export default defineComponent({ wallpaper.value = file.url; }); }, - faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye, faGlobe, faPaintRoller, }; } }); diff --git a/src/client/pages/settings/update.vue b/src/client/pages/settings/update.vue index d7b2adae56..8000327d0c 100644 --- a/src/client/pages/settings/update.vue +++ b/src/client/pages/settings/update.vue @@ -30,7 +30,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faInfoCircle, faSyncAlt } from '@fortawesome/free-solid-svg-icons'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormLink from '@client/components/form/link.vue'; @@ -61,7 +60,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: 'Misskey Update', - icon: faSyncAlt + icon: 'fas fa-sync-alt' }, version, instanceName, diff --git a/src/client/pages/settings/word-mute.vue b/src/client/pages/settings/word-mute.vue index 79de2ebbdf..fe3fece844 100644 --- a/src/client/pages/settings/word-mute.vue +++ b/src/client/pages/settings/word-mute.vue @@ -25,14 +25,13 @@ </FormKeyValueView> </div> </div> - <FormButton @click="save()" primary inline :disabled="!changed"><Fa :icon="faSave"/> {{ $ts.save }}</FormButton> + <FormButton @click="save()" primary inline :disabled="!changed"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> </FormBase> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faCommentSlash, faSave } from '@fortawesome/free-solid-svg-icons'; import FormTextarea from '@client/components/form/textarea.vue'; import FormBase from '@client/components/form/base.vue'; import FormKeyValueView from '@client/components/form/key-value-view.vue'; @@ -59,14 +58,13 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.wordMute, - icon: faCommentSlash + icon: 'fas fa-comment-slash' }, tab: 'soft', softMutedWords: '', hardMutedWords: '', hardWordMutedNotesCount: null, changed: false, - faSave, } }, diff --git a/src/client/pages/share.vue b/src/client/pages/share.vue index 313b73b9cb..67e598fa8f 100644 --- a/src/client/pages/share.vue +++ b/src/client/pages/share.vue @@ -13,7 +13,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faShareAlt } from '@fortawesome/free-solid-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import XPostForm from '@client/components/post-form.vue'; import * as os from '@client/os'; @@ -29,7 +28,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.share, - icon: faShareAlt + icon: 'fas fa-share-alt' }, title: null, text: null, @@ -37,7 +36,6 @@ export default defineComponent({ initialText: null, posted: false, - faShareAlt } }, diff --git a/src/client/pages/tag.vue b/src/client/pages/tag.vue index 813181dd1f..3ca9fe5c0c 100644 --- a/src/client/pages/tag.vue +++ b/src/client/pages/tag.vue @@ -6,7 +6,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faHashtag } from '@fortawesome/free-solid-svg-icons'; import Progress from '@client/scripts/loading'; import XNotes from '@client/components/notes.vue'; import * as symbols from '@client/symbols'; @@ -27,7 +26,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.tag, - icon: faHashtag + icon: 'fas fa-hashtag' }, pagination: { endpoint: 'notes/search-by-tag', @@ -36,7 +35,6 @@ export default defineComponent({ tag: this.tag, }) }, - faHashtag }; }, diff --git a/src/client/pages/test.vue b/src/client/pages/test.vue index 252fa1c828..9a06d31090 100644 --- a/src/client/pages/test.vue +++ b/src/client/pages/test.vue @@ -132,7 +132,6 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; -import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import MkSwitch from '@client/components/ui/switch.vue'; @@ -154,7 +153,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: 'TEST', - icon: faExclamationTriangle + icon: 'fas fa-exclamation-triangle' }, dialogTitle: 'Hello', dialogBody: 'World!', diff --git a/src/client/pages/theme-editor.vue b/src/client/pages/theme-editor.vue index db273746a8..ce8bae4ff5 100644 --- a/src/client/pages/theme-editor.vue +++ b/src/client/pages/theme-editor.vue @@ -42,7 +42,7 @@ </FormTextarea> <FormButton @click="applyThemeCode" primary>{{ $ts.apply }}</FormButton> </FormGroup> - <FormButton v-else @click="codeEnabled = true"><Fa :icon="faCode"/> {{ $ts.editCode }}</FormButton> + <FormButton v-else @click="codeEnabled = true"><i class="fas fa-code"></i> {{ $ts.editCode }}</FormButton> <FormGroup v-if="descriptionEnabled"> <FormTextarea v-model:value="description"> @@ -52,15 +52,14 @@ <FormButton v-else @click="descriptionEnabled = true">{{ $ts.addDescription }}</FormButton> <FormGroup> - <FormButton @click="showPreview"><Fa :icon="faEye"/> {{ $ts.preview }}</FormButton> - <FormButton @click="saveAs" primary><Fa :icon="faSave"/> {{ $ts.saveAs }}</FormButton> + <FormButton @click="showPreview"><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton> + <FormButton @click="saveAs" primary><i class="fas fa-save"></i> {{ $ts.saveAs }}</FormButton> </FormGroup> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faPalette, faSave, faEye, faCode } from '@fortawesome/free-solid-svg-icons'; import { toUnicode } from 'punycode/'; import * as tinycolor from 'tinycolor2'; import { v4 as uuid} from 'uuid'; @@ -90,7 +89,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.themeEditor, - icon: faPalette, + icon: 'fas fa-palette', }, theme: { base: 'light', @@ -129,7 +128,6 @@ export default defineComponent({ { color: 'pink', forLight: '#84667d', forDark: '#e4d1e0', forPreview: '#b12390' }, ], changed: false, - faPalette, faSave, faEye, faCode, } }, diff --git a/src/client/pages/timeline.tutorial.vue b/src/client/pages/timeline.tutorial.vue index bcbf16acc7..620994c0da 100644 --- a/src/client/pages/timeline.tutorial.vue +++ b/src/client/pages/timeline.tutorial.vue @@ -1,6 +1,6 @@ <template> <div class="_card tbkwesmv"> - <div class="_title"><Fa :icon="faInfoCircle"/> {{ $ts._tutorial.title }}</div> + <div class="_title"><i class="fas fa-info-circle"></i> {{ $ts._tutorial.title }}</div> <div class="_content" v-if="tutorial === 0"> <div>{{ $ts._tutorial.step1_1 }}</div> <div>{{ $ts._tutorial.step1_2 }}</div> @@ -52,22 +52,21 @@ <div class="_footer navigation"> <div class="step"> <button class="arrow _button" @click="tutorial--" :disabled="tutorial === 0"> - <Fa :icon="faChevronLeft"/> + <i class="fas fa-chevron-left"></i> </button> <span>{{ tutorial + 1 }} / 7</span> <button class="arrow _button" @click="tutorial++" :disabled="tutorial === 6"> - <Fa :icon="faChevronRight"/> + <i class="fas fa-chevron-right"></i> </button> </div> - <MkButton class="ok" @click="tutorial = -1" primary v-if="tutorial === 6"><Fa :icon="faCheck"/> {{ $ts.gotIt }}</MkButton> - <MkButton class="ok" @click="tutorial++" primary v-else><Fa :icon="faCheck"/> {{ $ts.next }}</MkButton> + <MkButton class="ok" @click="tutorial = -1" primary v-if="tutorial === 6"><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton> + <MkButton class="ok" @click="tutorial++" primary v-else><i class="fas fa-check"></i> {{ $ts.next }}</MkButton> </div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import { faInfoCircle, faChevronLeft, faChevronRight, faCheck } from '@fortawesome/free-solid-svg-icons' import MkButton from '@client/components/ui/button.vue'; export default defineComponent({ @@ -77,7 +76,6 @@ export default defineComponent({ data() { return { - faInfoCircle, faChevronLeft, faChevronRight, faCheck } }, diff --git a/src/client/pages/timeline.vue b/src/client/pages/timeline.vue index efad31c252..5660d0099e 100644 --- a/src/client/pages/timeline.vue +++ b/src/client/pages/timeline.vue @@ -4,18 +4,18 @@ <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/> <div class="tabs _block"> <div class="left"> - <button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><Fa :icon="faHome"/></button> - <button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><Fa :icon="faComments"/></button> - <button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social" v-if="isLocalTimelineAvailable"><Fa :icon="faShareAlt"/></button> - <button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global" v-if="isGlobalTimelineAvailable"><Fa :icon="faGlobe"/></button> + <button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><i class="fas fa-home"></i></button> + <button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><i class="fas fa-comments"></i></button> + <button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social" v-if="isLocalTimelineAvailable"><i class="fas fa-share-alt"></i></button> + <button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global" v-if="isGlobalTimelineAvailable"><i class="fas fa-globe"></i></button> <span class="divider"></span> - <button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><Fa :icon="faAt"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadMentions"/></button> - <button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><Fa :icon="faEnvelope"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadSpecifiedNotes"/></button> + <button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><i class="fas fa-at"></i><i v-if="$i.hasUnreadMentions" class="fas fa-circle i"></i></button> + <button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><i class="fas fa-envelope"></i><i v-if="$i.hasUnreadSpecifiedNotes" class="fas fa-circle i"></i></button> </div> <div class="right"> - <button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><Fa :icon="faSatelliteDish"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadChannel"/></button> - <button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><Fa :icon="faSatellite"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadAntenna"/></button> - <button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><Fa :icon="faListUl"/></button> + <button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><i class="fas fa-satellite-dish"></i><i v-if="$i.hasUnreadChannel" class="fas fa-circle i"></i></button> + <button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><i class="fas fa-satellite"></i><i v-if="$i.hasUnreadAntenna" class="fas fa-circle i"></i></button> + <button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><i class="fas fa-list-ul"></i></button> </div> </div> <XTimeline ref="tl" @@ -36,8 +36,6 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent, computed } from 'vue'; -import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faPencilAlt, faAt } from '@fortawesome/free-solid-svg-icons'; -import { faComments, faEnvelope, faCalendarAlt } from '@fortawesome/free-regular-svg-icons'; import Progress from '@client/scripts/loading'; import XTimeline from '@client/components/timeline.vue'; import XPostForm from '@client/components/post-form.vue'; @@ -64,14 +62,13 @@ export default defineComponent({ queue: 0, [symbols.PAGE_INFO]: computed(() => ({ title: this.$ts.timeline, - icon: this.src === 'local' ? faComments : this.src === 'social' ? faShareAlt : this.src === 'global' ? faGlobe : faHome, + icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home', actions: [{ - icon: faCalendarAlt, + icon: 'fas fa-calendar-alt', text: this.$ts.jumpToSpecifiedDate, handler: this.timetravel }] })), - faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faAt, faEnvelope, }; }, diff --git a/src/client/pages/user-ap-info.vue b/src/client/pages/user-ap-info.vue index 648ecdb10a..c08a352571 100644 --- a/src/client/pages/user-ap-info.vue +++ b/src/client/pages/user-ap-info.vue @@ -58,7 +58,6 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import FormObjectView from '@client/components/form/object-view.vue'; import FormTextarea from '@client/components/form/textarea.vue'; import FormLink from '@client/components/form/link.vue'; @@ -96,7 +95,7 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.userInfo, - icon: faInfoCircle + icon: 'fas fa-info-circle' }, user: null, apPromiseFactory: null, diff --git a/src/client/pages/user-info.vue b/src/client/pages/user-info.vue index 06f2e4270d..51bd5016bb 100644 --- a/src/client/pages/user-info.vue +++ b/src/client/pages/user-info.vue @@ -1,12 +1,34 @@ <template> <FormBase> - <FormGroup v-if="user"> - <template #label><MkAcct :user="user"/></template> + <FormSuspense :p="init"> + <div class="_formItem aeakzknw"> + <MkAvatar class="avatar" :user="user" :show-indicator="true"/> + </div> - <FormKeyValueView> - <template #key>ID</template> - <template #value><span class="_monospace">{{ user.id }}</span></template> - </FormKeyValueView> + <FormLink :to="userPage(user)">Profile</FormLink> + + <FormGroup> + <FormKeyValueView> + <template #key>Acct</template> + <template #value><span class="_monospace">{{ acct(user) }}</span></template> + </FormKeyValueView> + + <FormKeyValueView> + <template #key>ID</template> + <template #value><span class="_monospace">{{ user.id }}</span></template> + </FormKeyValueView> + </FormGroup> + + <FormGroup v-if="iAmModerator"> + <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $ts.moderator }}</FormSwitch> + <FormSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $ts.silence }}</FormSwitch> + <FormSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $ts.suspend }}</FormSwitch> + </FormGroup> + + <FormGroup> + <FormButton v-if="user.host != null" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ $ts.updateRemoteUser }}</FormButton> + <FormButton v-if="user.host == null && iAmModerator" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton> + </FormGroup> <FormGroup> <FormLink :to="`/user-ap-info/${user.id}`">ActivityPub</FormLink> @@ -28,15 +50,15 @@ <FormObjectView tall :value="user"> <span>Raw</span> </FormObjectView> - </FormGroup> + </FormSuspense> </FormBase> </template> <script lang="ts"> import { computed, defineAsyncComponent, defineComponent } from 'vue'; -import { faExternalLinkAlt, faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import FormObjectView from '@client/components/form/object-view.vue'; import FormTextarea from '@client/components/form/textarea.vue'; +import FormSwitch from '@client/components/form/switch.vue'; import FormLink from '@client/components/form/link.vue'; import FormBase from '@client/components/form/base.vue'; import FormGroup from '@client/components/form/group.vue'; @@ -48,11 +70,13 @@ import number from '@client/filters/number'; import bytes from '@client/filters/bytes'; import * as symbols from '@client/symbols'; import { url } from '@client/config'; +import { userPage, acct } from '@client/filters/user'; export default defineComponent({ components: { FormBase, FormTextarea, + FormSwitch, FormObjectView, FormButton, FormLink, @@ -71,33 +95,151 @@ export default defineComponent({ data() { return { [symbols.PAGE_INFO]: computed(() => ({ - title: this.$ts.userInfo, - icon: faInfoCircle, + title: this.user ? acct(this.user) : this.$ts.userInfo, + icon: 'fas fa-info-circle', actions: this.user ? [this.user.url ? { text: this.user.url, - icon: faExternalLinkAlt, + icon: 'fas fa-external-link-alt', handler: () => { window.open(this.user.url, '_blank'); } } : undefined].filter(x => x !== undefined) : [], })), + init: null, user: null, + info: null, + moderator: false, + silenced: false, + suspended: false, + } + }, + + computed: { + iAmModerator(): boolean { + return this.$i && (this.$i.isAdmin || this.$i.isModerator); } }, - mounted() { - this.fetch(); + watch: { + userId: { + handler() { + this.init = this.createFetcher(); + }, + immediate: true + } }, methods: { number, bytes, + userPage, + acct, + + createFetcher() { + if (this.iAmModerator) { + return () => Promise.all([os.api('users/show', { + userId: this.userId + }), os.api('admin/show-user', { + userId: this.userId + })]).then(([user, info]) => { + this.user = user; + this.info = info; + this.moderator = this.info.isModerator; + this.silenced = this.info.isSilenced; + this.suspended = this.info.isSuspended; + }); + } else { + return () => os.api('users/show', { + userId: this.userId + }).then((user) => { + this.user = user; + }); + } + }, + + refreshUser() { + this.init = this.createFetcher(); + }, - async fetch() { - this.user = await os.api('users/show', { - userId: this.userId + async updateRemoteUser() { + await os.apiWithDialog('federation/update-remote-user', { userId: this.user.id }); + this.refreshUser(); + }, + + async resetPassword() { + os.apiWithDialog('admin/reset-password', { + userId: this.user.id, + }, undefined, ({ password }) => { + os.dialog({ + type: 'success', + text: this.$t('newPasswordIs', { password }) + }); }); - } + }, + + async toggleSilence(v) { + const confirm = await os.dialog({ + type: 'warning', + showCancelButton: true, + text: v ? this.$ts.silenceConfirm : this.$ts.unsilenceConfirm, + }); + if (confirm.canceled) { + this.silenced = !v; + } else { + await os.api(v ? 'admin/silence-user' : 'admin/unsilence-user', { userId: this.user.id }); + await this.refreshUser(); + } + }, + + async toggleSuspend(v) { + const confirm = await os.dialog({ + type: 'warning', + showCancelButton: true, + text: v ? this.$ts.suspendConfirm : this.$ts.unsuspendConfirm, + }); + if (confirm.canceled) { + this.suspended = !v; + } else { + await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: this.user.id }); + await this.refreshUser(); + } + }, + + async toggleModerator(v) { + await os.api(v ? 'admin/moderators/add' : 'admin/moderators/remove', { userId: this.user.id }); + await this.refreshUser(); + }, + + async deleteAllFiles() { + const confirm = await os.dialog({ + type: 'warning', + showCancelButton: true, + text: this.$ts.deleteAllFilesConfirm, + }); + if (confirm.canceled) return; + const process = async () => { + await os.api('admin/delete-all-files-of-a-user', { userId: this.user.id }); + os.success(); + }; + await process().catch(e => { + os.dialog({ + type: 'error', + text: e.toString() + }); + }); + await this.refreshUser(); + }, } }); </script> + +<style lang="scss" scoped> +.aeakzknw { + > .avatar { + display: block; + margin: 0 auto; + width: 64px; + height: 64px; + } +} +</style> diff --git a/src/client/pages/user/index.activity.vue b/src/client/pages/user/index.activity.vue index 3eca1ab210..9101c8ae5f 100644 --- a/src/client/pages/user/index.activity.vue +++ b/src/client/pages/user/index.activity.vue @@ -1,6 +1,6 @@ <template> <MkContainer> - <template #header><Fa :icon="faChartBar" style="margin-right: 0.5em;"/>{{ $ts.activity }}</template> + <template #header><i class="fas fa-chart-bar" style="margin-right: 0.5em;"></i>{{ $ts.activity }}</template> <div style="padding: 8px;"> <div ref="chart"></div> @@ -11,7 +11,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; import ApexCharts from 'apexcharts'; -import { faChartBar } from '@fortawesome/free-solid-svg-icons'; import * as os from '@client/os'; import MkContainer from '@client/components/ui/container.vue'; @@ -35,7 +34,6 @@ export default defineComponent({ fetching: true, data: [], peak: null, - faChartBar, }; }, mounted() { diff --git a/src/client/pages/user/index.photos.vue b/src/client/pages/user/index.photos.vue index 21d84cef4f..a899b116e5 100644 --- a/src/client/pages/user/index.photos.vue +++ b/src/client/pages/user/index.photos.vue @@ -1,6 +1,6 @@ <template> <MkContainer :max-height="300" :foldable="true"> - <template #header><Fa :icon="faImage" style="margin-right: 0.5em;"/>{{ $ts.images }}</template> + <template #header><i class="fas fa-image" style="margin-right: 0.5em;"></i>{{ $ts.images }}</template> <div class="ujigsodd"> <MkLoading v-if="fetching"/> <div class="stream" v-if="!fetching && images.length > 0"> @@ -19,7 +19,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faImage } from '@fortawesome/free-solid-svg-icons'; import { getStaticImageUrl } from '@client/scripts/get-static-image-url'; import notePage from '../../filters/note'; import * as os from '@client/os'; @@ -41,7 +40,6 @@ export default defineComponent({ return { fetching: true, images: [], - faImage }; }, mounted() { diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue index 92656fff2c..207b44f631 100644 --- a/src/client/pages/user/index.vue +++ b/src/client/pages/user/index.vue @@ -34,15 +34,15 @@ </div> <div class="fields system"> <dl class="field" v-if="user.location"> - <dt class="name"><Fa :icon="faMapMarker" fixed-width/> {{ $ts.location }}</dt> + <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> <dd class="value">{{ user.location }}</dd> </dl> <dl class="field" v-if="user.birthday"> - <dt class="name"><Fa :icon="faBirthdayCake" fixed-width/> {{ $ts.birthday }}</dt> + <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt> <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd> </dl> <dl class="field"> - <dt class="name"><Fa :icon="faCalendarAlt" fixed-width/> {{ $ts.registeredDate }}</dt> + <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt> <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd> </dl> </div> @@ -62,19 +62,19 @@ <div class="main"> <div class="nav _gap"> <MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link"> - <Fa :icon="faCommentAlt" class="icon"/> + <i class="fas fa-comment-alt icon"></i> <span>{{ $ts.notes }}</span> </MkA> <MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link"> - <Fa :icon="faPaperclip" class="icon"/> + <i class="fas fa-paperclip icon"></i> <span>{{ $ts.clips }}</span> </MkA> <MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link"> - <Fa :icon="faFileAlt" class="icon"/> + <i class="fas fa-file-alt icon"></i> <span>{{ $ts.pages }}</span> </MkA> <div class="actions"> - <button @click="menu" class="menu _button"><Fa :icon="faEllipsisH"/></button> + <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> <MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> </div> </div> @@ -95,8 +95,8 @@ </div> <div class="ftskorzw narrow _root" v-else-if="user && narrow === true" v-size="{ max: [500] }"> <!-- TODO --> - <!-- <div class="punished" v-if="user.isSuspended"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/> {{ $ts.userSuspended }}</div> --> - <!-- <div class="punished" v-if="user.isSilenced"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/> {{ $ts.userSilenced }}</div> --> + <!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> --> + <!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> --> <div class="profile"> <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/> @@ -109,15 +109,15 @@ <MkUserName class="name" :user="user" :nowrap="true"/> <div class="bottom"> <span class="username"><MkAcct :user="user" :detail="true" /></span> - <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><Fa :icon="faBookmark"/></span> - <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><Fa :icon="farBookmark"/></span> - <span v-if="user.isLocked" :title="$ts.isLocked"><Fa :icon="faLock"/></span> - <span v-if="user.isBot" :title="$ts.isBot"><Fa :icon="faRobot"/></span> + <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span> + <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span> + <span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span> + <span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span> </div> </div> <span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span> <div class="actions" v-if="$i"> - <button @click="menu" class="menu _button"><Fa :icon="faEllipsisH"/></button> + <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/> </div> </div> @@ -126,10 +126,10 @@ <MkUserName :user="user" :nowrap="false" class="name"/> <div class="bottom"> <span class="username"><MkAcct :user="user" :detail="true" /></span> - <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><Fa :icon="faBookmark"/></span> - <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><Fa :icon="farBookmark"/></span> - <span v-if="user.isLocked" :title="$ts.isLocked"><Fa :icon="faLock"/></span> - <span v-if="user.isBot" :title="$ts.isBot"><Fa :icon="faRobot"/></span> + <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span> + <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span> + <span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span> + <span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span> </div> </div> <div class="description"> @@ -138,15 +138,15 @@ </div> <div class="fields system"> <dl class="field" v-if="user.location"> - <dt class="name"><Fa :icon="faMapMarker" fixed-width/> {{ $ts.location }}</dt> + <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> <dd class="value">{{ user.location }}</dd> </dl> <dl class="field" v-if="user.birthday"> - <dt class="name"><Fa :icon="faBirthdayCake" fixed-width/> {{ $ts.birthday }}</dt> + <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt> <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd> </dl> <dl class="field"> - <dt class="name"><Fa :icon="faCalendarAlt" fixed-width/> {{ $ts.registeredDate }}</dt> + <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt> <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd> </dl> </div> @@ -180,22 +180,22 @@ <div class="contents"> <div class="nav _gap"> <MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link"> - <Fa :icon="faCommentAlt" class="icon"/> + <i class="fas fa-comment-alt icon"></i> <span>{{ $ts.notes }}</span> </MkA> <MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link"> - <Fa :icon="faPaperclip" class="icon"/> + <i class="fas fa-paperclip icon"></i> <span>{{ $ts.clips }}</span> </MkA> <MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link"> - <Fa :icon="faFileAlt" class="icon"/> + <i class="fas fa-file-alt icon"></i> <span>{{ $ts.pages }}</span> </MkA> </div> <template v-if="page === 'index'"> <div> - <div v-if="user.pinnedNotes.length > 0"> + <div v-if="user.pinnedNotes.length > 0" class="_gap"> <XNote v-for="note in user.pinnedNotes" class="note _block" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/> </div> <MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo> @@ -219,8 +219,6 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent, computed } from 'vue'; -import { faExclamationTriangle, faEllipsisH, faRobot, faLock, faBookmark, faChartBar, faImage, faBirthdayCake, faMapMarker, faPaperclip, faFileAlt } from '@fortawesome/free-solid-svg-icons'; -import { faCalendarAlt, faBookmark as farBookmark, faCommentAlt } from '@fortawesome/free-regular-svg-icons'; import * as age from 's-age'; import XUserTimeline from './index.timeline.vue'; import XNote from '@client/components/note.vue'; @@ -284,7 +282,6 @@ export default defineComponent({ error: null, parallaxAnimationId: null, narrow: null, - faExclamationTriangle, faEllipsisH, faRobot, faLock, faBookmark, farBookmark, faChartBar, faImage, faBirthdayCake, faMapMarker, faCalendarAlt, faCommentAlt, faPaperclip, faFileAlt, }; }, diff --git a/src/client/pages/v.vue b/src/client/pages/v.vue index 37a850b625..4440e8070e 100644 --- a/src/client/pages/v.vue +++ b/src/client/pages/v.vue @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import { version } from '@client/config'; import * as symbols from '@client/symbols'; @@ -24,7 +23,6 @@ export default defineComponent({ icon: null }, version, - faInfoCircle } }, }); diff --git a/src/client/pages/welcome.entrance.a.vue b/src/client/pages/welcome.entrance.a.vue index 7b02c44923..da3c694265 100644 --- a/src/client/pages/welcome.entrance.a.vue +++ b/src/client/pages/welcome.entrance.a.vue @@ -43,7 +43,7 @@ <template #n><b>{{ onlineUsersCount }}</b></template> </I18n> </div> - <button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button> + <button class="_button _acrylic menu" @click="showMenu"><i class="fas fa-ellipsis-h"></i></button> </div> </div> </div> @@ -52,7 +52,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { toUnicode } from 'punycode/'; import XSigninDialog from '@client/components/signin-dialog.vue'; import XSignupDialog from '@client/components/signup-dialog.vue'; @@ -80,7 +79,6 @@ export default defineComponent({ stats: null, tags: [], onlineUsersCount: null, - faEllipsisH }; }, @@ -121,19 +119,19 @@ export default defineComponent({ showMenu(ev) { os.modalMenu([{ text: this.$t('aboutX', { x: instanceName }), - icon: faInfoCircle, + icon: 'fas fa-info-circle', action: () => { os.pageWindow('/about'); } }, { text: this.$ts.aboutMisskey, - icon: faInfoCircle, + icon: 'fas fa-info-circle', action: () => { os.pageWindow('/about-misskey'); } }, null, { text: this.$ts.help, - icon: faQuestionCircle, + icon: 'fas fa-question-circle', action: () => { os.pageWindow('/docs'); } diff --git a/src/client/pages/welcome.entrance.b.vue b/src/client/pages/welcome.entrance.b.vue index d8622e4d8e..d108eb7d94 100644 --- a/src/client/pages/welcome.entrance.b.vue +++ b/src/client/pages/welcome.entrance.b.vue @@ -36,7 +36,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { toUnicode } from 'punycode/'; import XSigninDialog from '@client/components/signin-dialog.vue'; import XSignupDialog from '@client/components/signup-dialog.vue'; @@ -64,7 +63,6 @@ export default defineComponent({ stats: null, tags: [], onlineUsersCount: null, - faEllipsisH }; }, @@ -105,19 +103,19 @@ export default defineComponent({ showMenu(ev) { os.modalMenu([{ text: this.$t('aboutX', { x: instanceName }), - icon: faInfoCircle, + icon: 'fas fa-info-circle', action: () => { os.pageWindow('/about'); } }, { text: this.$ts.aboutMisskey, - icon: faInfoCircle, + icon: 'fas fa-info-circle', action: () => { os.pageWindow('/about-misskey'); } }, null, { text: this.$ts.help, - icon: faQuestionCircle, + icon: 'fas fa-question-circle', action: () => { os.pageWindow('/docs'); } diff --git a/src/client/pages/welcome.entrance.c.vue b/src/client/pages/welcome.entrance.c.vue index 47ddf9e5ed..93811e98fb 100644 --- a/src/client/pages/welcome.entrance.c.vue +++ b/src/client/pages/welcome.entrance.c.vue @@ -40,7 +40,7 @@ <template #n><b>{{ onlineUsersCount }}</b></template> </I18n> </div> - <button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button> + <button class="_button _acrylic menu" @click="showMenu"><i class="fas fa-ellipsis-h"></i></button> </div> </div> <nav class="nav"> @@ -56,7 +56,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { toUnicode } from 'punycode/'; import XSigninDialog from '@client/components/signin-dialog.vue'; import XSignupDialog from '@client/components/signup-dialog.vue'; @@ -84,7 +83,6 @@ export default defineComponent({ stats: null, tags: [], onlineUsersCount: null, - faEllipsisH }; }, @@ -125,19 +123,19 @@ export default defineComponent({ showMenu(ev) { os.modalMenu([{ text: this.$t('aboutX', { x: instanceName }), - icon: faInfoCircle, + icon: 'fas fa-info-circle', action: () => { os.pageWindow('/about'); } }, { text: this.$ts.aboutMisskey, - icon: faInfoCircle, + icon: 'fas fa-info-circle', action: () => { os.pageWindow('/about-misskey'); } }, null, { text: this.$ts.help, - icon: faQuestionCircle, + icon: 'fas fa-question-circle', action: () => { os.pageWindow('/docs'); } diff --git a/src/client/pages/welcome.setup.vue b/src/client/pages/welcome.setup.vue index db64f6b194..2a71e2311c 100644 --- a/src/client/pages/welcome.setup.vue +++ b/src/client/pages/welcome.setup.vue @@ -10,7 +10,7 @@ </MkInput> <MkInput v-model:value="password" type="password"> <span>{{ $ts.password }}</span> - <template #prefix><Fa :icon="faLock"/></template> + <template #prefix><i class="fas fa-lock"></i></template> </MkInput> <footer> <MkButton primary type="submit" :disabled="submitting">{{ submitting ? $ts.processing : $ts.done }}<MkEllipsis v-if="submitting"/></MkButton> @@ -21,7 +21,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import { faLock } from '@fortawesome/free-solid-svg-icons'; import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/ui/input.vue'; import { host } from '@client/config'; @@ -40,7 +39,6 @@ export default defineComponent({ password: '', submitting: false, host, - faLock } }, |