Merge branch 'develop'

This commit is contained in:
syuilo 2020-03-29 17:46:31 +09:00
commit e399b8c75a
26 changed files with 258 additions and 85 deletions

View file

@ -1,6 +1,34 @@
ChangeLog ChangeLog
========= =========
12.27.1 (2020/03/28)
-------------------
### ✨Improvements
* MiAuthのバグを修正
12.27.0 (2020/03/28)
-------------------
### ✨Improvements
* サードパーティーアプリケーションの認証方法にMiAuthを追加 ([Misskey API ドキュメント](https://github.com/syuilo/misskey/blob/b8088dc01a0c53b264c0697082ff5b16b06c4cda/src/docs/api.ja-JP.md#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%81%97%E3%81%A6%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B))
従来の、API `app/create` => `auth/session/generate` => `auth/session/userkey` を使用する方法は依然として使用可能です。
UIからアプリを作成する画面 (`/dev/apps`) は廃止されました、同等の操作を行いたい場合は API `app/create` で可能です。
* テーマをインポートする前にプレビューできるように
* アプリから通知を作成できるように
* インストールしたアプリを見たり削除したりできるように
12.26.0 (2020/03/25)
-------------------
### ✨Improvements
* ロゴが新しく
* インスタンス設定の「ユーザー」が登録の逆順で表示されるように
### 🐛Fixes
* 新規登録フォームの「利用規約」のリンク色が通常の文字と同じだった問題を修正
* ダークモードの同期の問題を修正
12.25.0 (2020/03/24) 12.25.0 (2020/03/24)
------------------- -------------------
@ -25,7 +53,6 @@ ChangeLog
### 🐛Fixes ### 🐛Fixes
* iOSで起動できない問題を修正 * iOSで起動できない問題を修正
* 画面が小さいとメニューがすべて見えない問題を修正
* Pages画面にタイトルがない問題を修正 * Pages画面にタイトルがない問題を修正
12.24.0 (2020/03/22) 12.24.0 (2020/03/22)

View file

@ -442,15 +442,10 @@ _charts:
_instanceCharts: _instanceCharts:
requests: "Anfragen" requests: "Anfragen"
users: "Unterschied in der Anzahl von Benutzern" users: "Unterschied in der Anzahl von Benutzern"
usersTotal: "Anzahl aller Benutzer"
notes: "Unterschied in der Anzahl von Notizen" notes: "Unterschied in der Anzahl von Notizen"
notesTotal: "Anzahl aller Notizen"
ff: "Unterschied in der Anzahl von Followern" ff: "Unterschied in der Anzahl von Followern"
ffTotal: "Gesamtanzahl der Follower"
cacheSize: "Unterschied in der Größe des Caches" cacheSize: "Unterschied in der Größe des Caches"
cacheSizeTotal: "Gesamtgröße des Caches"
files: "Unterschied in der Anzahl der Dateien" files: "Unterschied in der Anzahl der Dateien"
filesTotal: "Gesamtanzahl der Dateien"
_timelines: _timelines:
home: "Startseite" home: "Startseite"
local: "Lokal" local: "Lokal"

View file

@ -468,6 +468,14 @@ unableToProcess: "The operation could not be completed."
recentUsed: "Recently used" recentUsed: "Recently used"
install: "Install" install: "Install"
uninstall: "Uninstall" uninstall: "Uninstall"
installedApps: "Authorized Applications"
nothing: "There's nothing to see here"
installedDate: "Authorized"
lastUsedDate: "Last used"
state: "State"
sort: "Sort"
ascendingOrder: "Ascending"
descendingOrder: "Descending"
_theme: _theme:
explore: "Explore Themes" explore: "Explore Themes"
install: "Install theme" install: "Install theme"
@ -560,7 +568,11 @@ _permissions:
"write:user-groups": "Edit or delete user groups" "write:user-groups": "Edit or delete user groups"
_auth: _auth:
shareAccess: "Would you like to authorize \"{name}\" to access this account?" shareAccess: "Would you like to authorize \"{name}\" to access this account?"
shareAccessAsk: "Are you sure you want to authorize this application to access your account?"
permissionAsk: "This application requires following permissions:" permissionAsk: "This application requires following permissions:"
pleaseGoBack: "Please go back to the application"
callback: "Returning back to the application"
denied: "Access Denied"
_antennaSources: _antennaSources:
all: "All notes" all: "All notes"
homeTimeline: "Notes from following users" homeTimeline: "Notes from following users"
@ -670,7 +682,7 @@ _instanceCharts:
ff: "Difference in # of followers" ff: "Difference in # of followers"
ffTotal: "Total # of followers" ffTotal: "Total # of followers"
cacheSize: "Difference in cache size" cacheSize: "Difference in cache size"
cacheSizeTotal: "Total accumulated cache" cacheSizeTotal: "Total cache size"
files: "Difference in # of files" files: "Difference in # of files"
filesTotal: "Total # of files" filesTotal: "Total # of files"
_timelines: _timelines:

View file

@ -468,6 +468,10 @@ unableToProcess: "La operación no se puede llevar a cabo"
recentUsed: "Usado recientemente" recentUsed: "Usado recientemente"
install: "Instalación" install: "Instalación"
uninstall: "Desinstalar" uninstall: "Desinstalar"
installedApps: "Aplicaciones Autorizadas"
nothing: "No hay nada que ver aqui"
installedDate: "Autorizado"
lastUsedDate: "Utilizado el"
_theme: _theme:
explore: "Explorar temas" explore: "Explorar temas"
install: "Instalar tema" install: "Instalar tema"
@ -560,7 +564,11 @@ _permissions:
"write:user-groups": "Administrar grupos de usuarios" "write:user-groups": "Administrar grupos de usuarios"
_auth: _auth:
shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?" shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?"
shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder a su cuenta?"
permissionAsk: "Esta aplicación requiere los siguientes permisos" permissionAsk: "Esta aplicación requiere los siguientes permisos"
pleaseGoBack: "Por favor, vuelve a la aplicación"
callback: "Volviendo a la aplicación"
denied: "Acceso denegado"
_antennaSources: _antennaSources:
all: "Todas las notas" all: "Todas las notas"
homeTimeline: "Notas de los usuarios que sigues" homeTimeline: "Notas de los usuarios que sigues"
@ -664,15 +672,10 @@ _charts:
_instanceCharts: _instanceCharts:
requests: "Pedidos" requests: "Pedidos"
users: "Variación de usuarios" users: "Variación de usuarios"
usersTotal: "Total de usuarios"
notes: "Variación de la cantidad de notas" notes: "Variación de la cantidad de notas"
notesTotal: "Estimación de notas"
ff: "Variación de cantidad de seguidos/seguidores" ff: "Variación de cantidad de seguidos/seguidores"
ffTotal: "Total de seguidos/seguidores"
cacheSize: "Variación del tamaño de la caché" cacheSize: "Variación del tamaño de la caché"
cacheSizeTotal: "Total del tamaño de la caché"
files: "Variación de cantidad de archivos" files: "Variación de cantidad de archivos"
filesTotal: "Total de archivos"
_timelines: _timelines:
home: "Inicio" home: "Inicio"
local: "Local" local: "Local"

View file

@ -42,8 +42,8 @@ sendMessage: "Envoyer un message"
copyUsername: "Copier le nom d'utilisateur" copyUsername: "Copier le nom d'utilisateur"
reply: "Répondre" reply: "Répondre"
loadMore: "Voir plus" loadMore: "Voir plus"
youGotNewFollower: "Vous a abonnés" youGotNewFollower: "Vous suit"
receiveFollowRequest: "Demande de abonnés reçue" receiveFollowRequest: "Demande de suivi reçue"
followRequestAccepted: "L'abonne la demande acceptée" followRequestAccepted: "L'abonne la demande acceptée"
mentions: "Mentions" mentions: "Mentions"
directNotes: "Messages directs" directNotes: "Messages directs"
@ -53,7 +53,7 @@ export: "Exporter"
files: "Fichier·s" files: "Fichier·s"
download: "Télécharger" download: "Télécharger"
driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes avec ce fichier joint seront aussi supprimées." driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes avec ce fichier joint seront aussi supprimées."
unfollowConfirm: "Êtes-vous sûr·de ne plus vouloir abonne {name} ?" unfollowConfirm: "Se désabonner de {name} ?"
exportRequested: "Vous avez demandé une exportation. Cela pourrait prendre un peu de temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive." exportRequested: "Vous avez demandé une exportation. Cela pourrait prendre un peu de temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive."
importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps." importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
lists: "Listes" lists: "Listes"
@ -62,17 +62,17 @@ note: "Note"
notes: "Notes" notes: "Notes"
following: "Abonnements" following: "Abonnements"
followers: "Abonné·e·s" followers: "Abonné·e·s"
followsYou: "Votre abonné" followsYou: "Vous suit"
createList: "Créer une liste" createList: "Créer une liste"
manageLists: "Gérer les listes" manageLists: "Gérer les listes"
error: "Une erreur est survenue" error: "Une erreur est survenue"
retry: "Réessayer" retry: "Réessayer"
enterListName: "Nom de la liste" enterListName: "Nom de la liste"
privacy: "Vie privée" privacy: "Vie privée"
makeFollowManuallyApprove: "Demandes dabonnements requiert lapprobation" makeFollowManuallyApprove: "Demandes dsuivi requiert l'approbation"
defaultNoteVisibility: "Visibilité par défaut" defaultNoteVisibility: "Visibilité par défaut"
follow: "Abonnement" follow: "Suivre"
followRequest: "Demande dabonnement" followRequest: "Demande dsuivre"
followRequests: "Demandes dabonnement" followRequests: "Demandes dabonnement"
unfollow: "Se désabonner" unfollow: "Se désabonner"
followRequestPending: "En attente dapprobation" followRequestPending: "En attente dapprobation"
@ -121,7 +121,7 @@ setWallpaper: "Définir le fond d'écran"
removeWallpaper: "Supprimer l'arrière plan" removeWallpaper: "Supprimer l'arrière plan"
searchWith: "Recherche : {q}" searchWith: "Recherche : {q}"
youHaveNoLists: "Vous n'avez aucune liste" youHaveNoLists: "Vous n'avez aucune liste"
followConfirm: "Désirez-vous abonne {name} ?" followConfirm: "Désirez-vous suivre {name} ?"
proxyAccount: "Compte proxy" proxyAccount: "Compte proxy"
proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant pour les utilisateurs d'autres instances.\nExemple : quand un·e utilisateur·rice distant·e est ajouté·e à une liste, ses notes ne serait pas visibles sur l'instance si personne ne le·la abonné. Le compte proxy va donc le·la abonne pour que ses notes soient acheminées." proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant pour les utilisateurs d'autres instances.\nExemple : quand un·e utilisateur·rice distant·e est ajouté·e à une liste, ses notes ne serait pas visibles sur l'instance si personne ne le·la abonné. Le compte proxy va donc le·la abonne pour que ses notes soient acheminées."
host: "Hôte" host: "Hôte"
@ -468,6 +468,10 @@ unableToProcess: "L'opération n'a pas pu être complétée"
recentUsed: "Récemment utilisé" recentUsed: "Récemment utilisé"
install: "Installation" install: "Installation"
uninstall: "Désinstaller" uninstall: "Désinstaller"
installedApps: "Applications Autorisées"
nothing: "Il n'y a rien à voir ici"
installedDate: "Autorisé"
lastUsedDate: "Dernière utilisation"
_theme: _theme:
explore: "Explorer les thèmes" explore: "Explorer les thèmes"
install: "Installer un thème" install: "Installer un thème"
@ -560,7 +564,11 @@ _permissions:
"write:user-groups": "Éditer les groupes des utilisateur·rice·s" "write:user-groups": "Éditer les groupes des utilisateur·rice·s"
_auth: _auth:
shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?" shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?"
shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre compte?"
permissionAsk: "Cette application nécessite les autorisations suivantes " permissionAsk: "Cette application nécessite les autorisations suivantes "
pleaseGoBack: "Veillez retourner à l'application"
callback: "Retour vers lapplication"
denied: "Accès refusé"
_antennaSources: _antennaSources:
all: "Toutes les notes" all: "Toutes les notes"
homeTimeline: "Notes de l'utilisateur auquel je m'abonne" homeTimeline: "Notes de l'utilisateur auquel je m'abonne"
@ -664,15 +672,10 @@ _charts:
_instanceCharts: _instanceCharts:
requests: "Requêtes" requests: "Requêtes"
users: "Variation du nombre d'utilisateur·rice·s" users: "Variation du nombre d'utilisateur·rice·s"
usersTotal: "Somme du nombre d'utilisateur·rice·s accumulés"
notes: "Variation du nombre d'notes" notes: "Variation du nombre d'notes"
notesTotal: "Somme du nombre dnotes accumulés"
ff: "Variation des abonné·e·s" ff: "Variation des abonné·e·s"
ffTotal: "Somme du nombre d'abonnements accumulés"
cacheSize: "Variation de la taille du cache" cacheSize: "Variation de la taille du cache"
cacheSizeTotal: "Somme de la taille du cache accumulé"
files: "Variation du nombre de fichiers" files: "Variation du nombre de fichiers"
filesTotal: "Somme du nombre de fichiers accumulés"
_timelines: _timelines:
home: "Principal" home: "Principal"
local: "Local" local: "Local"

View file

@ -472,6 +472,10 @@ installedApps: "インストールされたアプリ"
nothing: "ありません" nothing: "ありません"
installedDate: "インストール日時" installedDate: "インストール日時"
lastUsedDate: "最終使用日時" lastUsedDate: "最終使用日時"
state: "状態"
sort: "ソート"
ascendingOrder: "昇順"
descendingOrder: "降順"
_theme: _theme:
explore: "テーマを探す" explore: "テーマを探す"
@ -691,15 +695,15 @@ _charts:
_instanceCharts: _instanceCharts:
requests: "リクエスト" requests: "リクエスト"
users: "ユーザーの増減" users: "ユーザーの増減"
usersTotal: "ユーザーの" usersTotal: "ユーザーの積"
notes: "ノートの増減" notes: "ノートの増減"
notesTotal: "ノートの" notesTotal: "ノートの積"
ff: "フォロー/フォロワーの増減" ff: "フォロー/フォロワーの増減"
ffTotal: "フォロー/フォロワーの" ffTotal: "フォロー/フォロワーの積"
cacheSize: "キャッシュサイズの増減" cacheSize: "キャッシュサイズの増減"
cacheSizeTotal: "キャッシュサイズの" cacheSizeTotal: "キャッシュサイズの積"
files: "ファイル数の増減" files: "ファイル数の増減"
filesTotal: "ファイル数の" filesTotal: "ファイル数の積"
_timelines: _timelines:
home: "ホーム" home: "ホーム"

View file

@ -468,6 +468,14 @@ unableToProcess: "작업을 완료할 수 없습니다"
recentUsed: "최근 사용" recentUsed: "최근 사용"
install: "설치" install: "설치"
uninstall: "삭제" uninstall: "삭제"
installedApps: "인증된 애플리케이션"
nothing: "아무것도 없습니다"
installedDate: "승인한 날짜"
lastUsedDate: "마지막 사용"
state: "상태"
sort: "정렬"
ascendingOrder: "오름차순"
descendingOrder: "내림차순"
_theme: _theme:
explore: "테마 찾아보기" explore: "테마 찾아보기"
install: "테마 설치" install: "테마 설치"
@ -560,7 +568,11 @@ _permissions:
"write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다" "write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다"
_auth: _auth:
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?" shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?"
permissionAsk: "이 앱은 다음의 권한을 요청합니다" permissionAsk: "이 앱은 다음의 권한을 요청합니다"
pleaseGoBack: "앱으로 돌아가서 시도해 주세요"
callback: "앱으로 돌아갑니다"
denied: "접근이 거부되었습니다"
_antennaSources: _antennaSources:
all: "모든 노트" all: "모든 노트"
homeTimeline: "팔로우중인 유저의 노트" homeTimeline: "팔로우중인 유저의 노트"
@ -664,15 +676,10 @@ _charts:
_instanceCharts: _instanceCharts:
requests: "요청" requests: "요청"
users: "유저 수 증감" users: "유저 수 증감"
usersTotal: "누적 유저 수"
notes: "노트 수 증감" notes: "노트 수 증감"
notesTotal: "총 노트 수"
ff: "팔로잉/팔로워 증감" ff: "팔로잉/팔로워 증감"
ffTotal: "팔로잉/팔로워 누적"
cacheSize: "캐시 용량 증감" cacheSize: "캐시 용량 증감"
cacheSizeTotal: "누적 캐시 용량"
files: "파일 수 증감" files: "파일 수 증감"
filesTotal: "누적 파일 수"
_timelines: _timelines:
home: "홈" home: "홈"
local: "로컬" local: "로컬"

View file

@ -468,6 +468,14 @@ unableToProcess: "操作无法完成"
recentUsed: "最近使用" recentUsed: "最近使用"
install: "安装" install: "安装"
uninstall: "卸载" uninstall: "卸载"
installedApps: "已授权的应用"
nothing: "没什么"
installedDate: "授权日期"
lastUsedDate: "最近使用"
state: "状态"
sort: "排序"
ascendingOrder: "升序"
descendingOrder: "降序"
_theme: _theme:
explore: "寻找主题" explore: "寻找主题"
install: "安装主题" install: "安装主题"
@ -560,7 +568,11 @@ _permissions:
"write:user-groups": "操作用户组" "write:user-groups": "操作用户组"
_auth: _auth:
shareAccess: "您要授权允许“{name}”访问您的帐户吗?" shareAccess: "您要授权允许“{name}”访问您的帐户吗?"
shareAccessAsk: "您确定要授权此应用程序访问您的帐户吗?"
permissionAsk: "这个应用程序需要以下权限" permissionAsk: "这个应用程序需要以下权限"
pleaseGoBack: "请返回到应用程序"
callback: "回到应用程序"
denied: "拒绝访问"
_antennaSources: _antennaSources:
all: "所有帖子" all: "所有帖子"
homeTimeline: "已关注用户的帖子" homeTimeline: "已关注用户的帖子"
@ -664,15 +676,15 @@ _charts:
_instanceCharts: _instanceCharts:
requests: "请求" requests: "请求"
users: "用户数量:增加/减少" users: "用户数量:增加/减少"
usersTotal: "用户总" usersTotal: "用户总"
notes: "帖子:增加/减少" notes: "帖子:增加/减少"
notesTotal: "帖子:总数" notesTotal: "帖子总计"
ff: "关注/被关注:数量变化" ff: "关注/被关注:数量变化"
ffTotal: "关注/被关注:总数" ffTotal: "关注/被关注者总计"
cacheSize: "缓存大小:增加/减少" cacheSize: "缓存大小:增加/减少"
cacheSizeTotal: "合计缓存大小" cacheSizeTotal: "缓存大小总计"
files: "文件总数增减" files: "文件总数增减"
filesTotal: "合计文件总数" filesTotal: "文件数总计"
_timelines: _timelines:
home: "首页" home: "首页"
local: "本地" local: "本地"

View file

@ -1,7 +1,7 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>", "author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.27.1", "version": "12.28.0",
"codename": "indigo", "codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -975,6 +975,10 @@ export default Vue.extend({
&:not(.naked) { &:not(.naked) {
background: var(--pageBg); background: var(--pageBg);
} }
&.naked {
background: var(--bg);
}
} }
} }

View file

@ -1,8 +1,8 @@
<template> <template>
<component :is="$store.state.device.animation ? 'transition-group' : 'div'" class="sqadhkmv" name="list" tag="div" :data-direction="direction" :data-reversed="reversed ? 'true' : 'false'"> <component :is="$store.state.device.animation ? 'transition-group' : 'div'" class="sqadhkmv" name="list" tag="div" :data-direction="direction" :data-reversed="reversed ? 'true' : 'false'">
<template v-for="(item, i) in items"> <template v-for="(item, i) in items">
<slot :item="item" :i="i"></slot> <slot :item="item"></slot>
<div class="separator" :key="item.id + '_date'" v-if="showDate(i, item)"> <div class="separator" v-if="showDate(i, item)" :key="item.id + '_date'">
<p class="date"> <p class="date">
<span><fa class="icon" :icon="faAngleUp"/>{{ getDateText(item.createdAt) }}</span> <span><fa class="icon" :icon="faAngleUp"/>{{ getDateText(item.createdAt) }}</span>
<span>{{ getDateText(items[i + 1].createdAt) }}<fa class="icon" :icon="faAngleDown"/></span> <span>{{ getDateText(items[i + 1].createdAt) }}<fa class="icon" :icon="faAngleDown"/></span>

View file

@ -27,6 +27,12 @@
<div class="actions"> <div class="actions">
<button class="_button" @click="revoke(token)"><fa :icon="faTrashAlt"/></button> <button class="_button" @click="revoke(token)"><fa :icon="faTrashAlt"/></button>
</div> </div>
<details>
<summary>{{ $t('details') }}</summary>
<ul>
<li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
</ul>
</details>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,11 +1,14 @@
<template> <template>
<div class="mk-federation"> <div class="mk-federation">
<portal to="icon"><fa :icon="faGlobe"/></portal>
<portal to="title">{{ $t('federation') }}</portal>
<section class="_card instances"> <section class="_card instances">
<div class="_title"><fa :icon="faGlobe"/> {{ $t('instances') }}</div>
<div class="_content"> <div class="_content">
<mk-input v-model="host" :debounce="true"><span>{{ $t('host') }}</span></mk-input>
<div class="inputs" style="display: flex;"> <div class="inputs" style="display: flex;">
<mk-input v-model="host" :debounce="true" style="margin: 0; flex: 1;"><span>{{ $t('host') }}</span></mk-input> <mk-select v-model="state" style="margin: 0; flex: 1;">
<mk-select v-model="state" style="margin: 0;"> <template #label>{{ $t('state') }}</template>
<option value="all">{{ $t('all') }}</option> <option value="all">{{ $t('all') }}</option>
<option value="federating">{{ $t('federating') }}</option> <option value="federating">{{ $t('federating') }}</option>
<option value="subscribing">{{ $t('subscribing') }}</option> <option value="subscribing">{{ $t('subscribing') }}</option>
@ -14,11 +17,32 @@
<option value="blocked">{{ $t('blocked') }}</option> <option value="blocked">{{ $t('blocked') }}</option>
<option value="notResponding">{{ $t('notResponding') }}</option> <option value="notResponding">{{ $t('notResponding') }}</option>
</mk-select> </mk-select>
<mk-select v-model="sort" style="margin: 0; flex: 1;">
<template #label>{{ $t('sort') }}</template>
<option value="+pubSub">{{ $t('pubSub') }} ({{ $t('descendingOrder') }})</option>
<option value="-pubSub">{{ $t('pubSub') }} ({{ $t('ascendingOrder') }})</option>
<option value="+notes">{{ $t('notes') }} ({{ $t('descendingOrder') }})</option>
<option value="-notes">{{ $t('notes') }} ({{ $t('ascendingOrder') }})</option>
<option value="+users">{{ $t('users') }} ({{ $t('descendingOrder') }})</option>
<option value="-users">{{ $t('users') }} ({{ $t('ascendingOrder') }})</option>
<option value="+following">{{ $t('following') }} ({{ $t('descendingOrder') }})</option>
<option value="-following">{{ $t('following') }} ({{ $t('ascendingOrder') }})</option>
<option value="+followers">{{ $t('followers') }} ({{ $t('descendingOrder') }})</option>
<option value="-followers">{{ $t('followers') }} ({{ $t('ascendingOrder') }})</option>
<option value="+caughtAt">{{ $t('caughtAt') }} ({{ $t('descendingOrder') }})</option>
<option value="-caughtAt">{{ $t('caughtAt') }} ({{ $t('ascendingOrder') }})</option>
<option value="+lastCommunicatedAt">{{ $t('lastCommunicatedAt') }} ({{ $t('descendingOrder') }})</option>
<option value="-lastCommunicatedAt">{{ $t('lastCommunicatedAt') }} ({{ $t('ascendingOrder') }})</option>
<option value="+driveUsage">{{ $t('driveUsage') }} ({{ $t('descendingOrder') }})</option>
<option value="-driveUsage">{{ $t('driveUsage') }} ({{ $t('ascendingOrder') }})</option>
<option value="+driveFiles">{{ $t('driveFiles') }} ({{ $t('descendingOrder') }})</option>
<option value="-driveFiles">{{ $t('driveFiles') }} ({{ $t('ascendingOrder') }})</option>
</mk-select>
</div> </div>
</div> </div>
<div class="_content"> <div class="_content">
<mk-pagination :pagination="pagination" #default="{items}" class="instances" ref="instances" :key="host + state"> <mk-pagination :pagination="pagination" #default="{items}" class="instances" ref="instances" :key="host + state">
<div class="instance" v-for="(instance, i) in items" :key="instance.id" @click="info(instance)"> <div class="instance" 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="host"><fa :icon="faCircle" class="indicator" :class="getStatus(instance)"/><b>{{ instance.host }}</b></div>
<div class="status"> <div class="status">
<span class="sub" v-if="instance.followersCount > 0"><fa :icon="faCaretDown" class="icon"/>Sub</span> <span class="sub" v-if="instance.followersCount > 0"><fa :icon="faCaretDown" class="icon"/>Sub</span>

View file

@ -1,5 +1,8 @@
<template> <template>
<div> <div>
<portal to="icon"><fa :icon="faExchangeAlt"/></portal>
<portal to="title">{{ $t('jobQueue') }}</portal>
<x-queue :connection="connection" domain="inbox"> <x-queue :connection="connection" domain="inbox">
<template #title><fa :icon="faExchangeAlt"/> In</template> <template #title><fa :icon="faExchangeAlt"/> In</template>
</x-queue> </x-queue>

View file

@ -19,7 +19,7 @@
<button class="more _button" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages"> <button class="more _button" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('loading') : $t('loadMore') }} <template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('loading') : $t('loadMore') }}
</button> </button>
<x-list class="messages" :items="messages" v-slot="{ item: message, i }" direction="up" reversed> <x-list class="messages" :items="messages" v-slot="{ item: message }" direction="up" reversed>
<x-message :message="message" :is-group="group != null" :key="message.id"/> <x-message :message="message" :is-group="group != null" :key="message.id"/>
</x-list> </x-list>
</div> </div>

View file

@ -68,8 +68,8 @@ export default Vue.extend({
icon(): string { icon(): string {
return this.$route.query.icon; return this.$route.query.icon;
}, },
permission(): string { permission(): string[] {
return this.$route.query.permission; return this.$route.query.permission ? this.$route.query.permission.split(',') : [];
}, },
}, },
methods: { methods: {
@ -79,7 +79,7 @@ export default Vue.extend({
session: this.session, session: this.session,
name: this.name, name: this.name,
iconUrl: this.icon, iconUrl: this.icon,
permission: this.permission || [], permission: this.permission,
}); });
this.state = 'accepted'; this.state = 'accepted';

View file

@ -27,7 +27,7 @@ export const builtinThemes = [
require('./themes/danboard.json5'), require('./themes/danboard.json5'),
require('./themes/olive.json5'), require('./themes/olive.json5'),
require('./themes/tweetdeck.json5'), require('./themes/tweetdeck.json5'),
]; ] as Theme[];
let timeout = null; let timeout = null;
@ -66,16 +66,21 @@ export function applyTheme(theme: Theme, persist = true) {
} }
} }
function compile(theme: Theme): { [key: string]: string } { function compile(theme: Theme): Record<string, string> {
function getColor(code: string): tinycolor.Instance { function getColor(val: string): tinycolor.Instance {
// ref // ref (prop)
if (code[0] == '@') { if (val[0] === '@') {
return getColor(theme.props[code.substr(1)]); return getColor(theme.props[val.substr(1)]);
}
// ref (const)
else if (val[0] === '$') {
return getColor(theme.props[val]);
} }
// func // func
if (code[0] == ':') { else if (val[0] === ':') {
const parts = code.split('<'); const parts = val.split('<');
const func = parts.shift().substr(1); const func = parts.shift().substr(1);
const arg = parseFloat(parts.shift()); const arg = parseFloat(parts.shift());
const color = getColor(parts.join('<')); const color = getColor(parts.join('<'));
@ -87,12 +92,15 @@ function compile(theme: Theme): { [key: string]: string } {
} }
} }
return tinycolor(code); // other case
return tinycolor(val);
} }
const props = {}; const props = {};
for (const [k, v] of Object.entries(theme.props)) { for (const [k, v] of Object.entries(theme.props)) {
if (k.startsWith('$')) continue; // ignore const
props[k] = genValue(getColor(v)); props[k] = genValue(getColor(v));
} }

View file

@ -5,7 +5,7 @@
<div class="otgbylcu"> <div class="otgbylcu">
<textarea v-model="text" :placeholder="$t('placeholder')" @input="onChange"></textarea> <textarea v-model="text" :placeholder="$t('placeholder')" @input="onChange"></textarea>
<button @click="saveMemo" :disabled="!changed">{{ $t('save') }}</button> <button @click="saveMemo" :disabled="!changed" class="_buttonPrimary">{{ $t('save') }}</button>
</div> </div>
</mk-container> </mk-container>
</div> </div>
@ -84,6 +84,7 @@ export default define({
border: none; border: none;
border-bottom: solid var(--lineWidth) var(--faceDivider); border-bottom: solid var(--lineWidth) var(--faceDivider);
border-radius: 0; border-radius: 0;
box-sizing: border-box;
} }
> button { > button {
@ -94,22 +95,8 @@ export default define({
margin: 0; margin: 0;
padding: 0 10px; padding: 0 10px;
height: 28px; height: 28px;
color: #fff;
background: var(--accent) !important;
outline: none; outline: none;
border: none;
border-radius: 4px; border-radius: 4px;
transition: background 0.1s ease;
cursor: pointer;
&:hover {
background: var(--accentLighten10) !important;
}
&:active {
background: var(--accentDarken) !important;
transition: background 0s ease;
}
&:disabled { &:disabled {
opacity: 0.7; opacity: 0.7;

View file

@ -69,7 +69,9 @@ class MyCustomLogger implements Logger {
} }
public logQuery(query: string, parameters?: any[]) { public logQuery(query: string, parameters?: any[]) {
sqlLogger.info(this.highlight(query)); if (program.verbose) {
sqlLogger.info(this.highlight(query));
}
} }
public logQueryError(error: string, query: string, parameters?: any[]) { public logQueryError(error: string, query: string, parameters?: any[]) {
@ -158,7 +160,7 @@ export function initDb(justBorrow = false, sync = false, forceRecreate = false)
} catch (e) {} } catch (e) {}
} }
const log = program.verbose; const log = process.env.NODE_ENV != 'production';
return createConnection({ return createConnection({
type: 'postgres', type: 'postgres',

74
src/docs/theme.ja-JP.md Normal file
View file

@ -0,0 +1,74 @@
# テーマ
テーマを設定して、Misskeyクライアントの見た目を変更できます。
## テーマの設定
設定 > テーマ
## テーマを作成する
テーマコードはJSON5で記述されたテーマオブジェクトです。
テーマは以下のようなオブジェクトです。
``` js
{
id: '17587283-dd92-4a2c-a22c-be0637c9e22a',
name: 'Danboard',
author: 'syuilo',
base: 'light',
props: {
accent: 'rgb(218, 141, 49)',
bg: 'rgb(218, 212, 190)',
fg: 'rgb(115, 108, 92)',
panel: 'rgb(236, 232, 220)',
renote: 'rgb(100, 152, 106)',
link: 'rgb(100, 152, 106)',
mention: '@accent',
hashtag: 'rgb(100, 152, 106)',
header: 'rgba(239, 227, 213, 0.75)',
navBg: 'rgb(216, 206, 182)',
inputBorder: 'rgba(0, 0, 0, 0.1)',
},
}
```
* `id` ... テーマの一意なID。UUIDをおすすめします。
* `name` ... テーマ名
* `author` ... テーマの作者
* `desc` ... テーマの説明(オプション)
* `base` ... 明るいテーマか、暗いテーマか
* `light`にすると明るいテーマになり、`dark`にすると暗いテーマになります。
* テーマはここで設定されたベーステーマを継承します。
* `props` ... テーマのスタイル定義。これから説明します。
### テーマのスタイル定義
`props`下にはテーマのスタイルを定義します。
キーがCSSの変数名になり、バリューで中身を指定します。
なお、この`props`オブジェクトはベーステーマから継承されます。
ベーステーマは、このテーマの`base`が`light`なら[_light.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_light.json5)で、`dark`なら[_dark.json5](https://github.com/syuilo/misskey/blob/develop/src/client/themes/_dark.json5)です。
つまり、このテーマ内の`props`に`panel`というキーが無くても、そこにはベーステーマの`panel`があると見なされます。
#### バリューで使える構文
* 16進数で表された色
* 例: `#00ff00`
* `rgb(r, g, b)`形式で表された色
* 例: `rgb(0, 255, 0)`
* `rgb(r, g, b, a)`形式で表された透明度を含む色
* 例: `rgba(0, 255, 0, 0.5)`
* 他のキーの値の参照
* `@{キー名}`と書くと他のキーの値の参照になります。`{キー名}`は参照したいキーの名前に置き換えます。
* 例: `@panel`
* 定数(後述)の参照
* `${定数名}`と書くと定数の参照になります。`{定数名}`は参照したい定数の名前に置き換えます。
* 例: `$main`
* 関数(後述)
* `:{関数名}<{引数}<{色}`
#### 定数
「CSS変数として出力はしたくないが、他のCSS変数の値として使いまわしたい」値があるときは、定数を使うと便利です。
キー名を`$`で始めると、そのキーはCSS変数として出力されません。
#### 関数
wip

View file

@ -46,8 +46,8 @@ export class NotificationRepository extends Repository<Notification> {
} : {}), } : {}),
...(notification.type === 'app' ? { ...(notification.type === 'app' ? {
body: notification.customBody, body: notification.customBody,
header: notification.customHeader || token!.name, header: notification.customHeader || token?.name,
icon: notification.customIcon || token!.iconUrl, icon: notification.customIcon || token?.iconUrl,
} : {}), } : {}),
}); });
} }

View file

@ -15,12 +15,12 @@ type Params<T extends IEndpointMeta> = {
export type Response = Record<string, any> | void; export type Response = Record<string, any> | void;
type executor<T extends IEndpointMeta> = type executor<T extends IEndpointMeta> =
(params: Params<T>, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken, file?: any, cleanup?: Function) => (params: Params<T>, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any, cleanup?: Function) =>
Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>; Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>;
export default function <T extends IEndpointMeta>(meta: T, cb: executor<T>) export default function <T extends IEndpointMeta>(meta: T, cb: executor<T>)
: (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken, file?: any) => Promise<any> { : (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => Promise<any> {
return (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken, file?: any) => { return (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => {
function cleanup() { function cleanup() {
fs.unlink(file.path, () => {}); fs.unlink(file.path, () => {});
} }

View file

@ -38,5 +38,6 @@ export default define(meta, async (ps, user) => {
name: token.name, name: token.name,
createdAt: token.createdAt, createdAt: token.createdAt,
lastUsedAt: token.lastUsedAt, lastUsedAt: token.lastUsedAt,
permission: token.permission,
}))); })));
}); });

View file

@ -159,6 +159,7 @@ export default define(meta, async (ps, me) => {
github: instance.enableGithubIntegration, github: instance.enableGithubIntegration,
discord: instance.enableDiscordIntegration, discord: instance.enableDiscordIntegration,
serviceWorker: instance.enableServiceWorker, serviceWorker: instance.enableServiceWorker,
miauth: true,
}; };
} }

View file

@ -29,7 +29,7 @@ export const meta = {
export default define(meta, async (ps, user, token) => { export default define(meta, async (ps, user, token) => {
createNotification(user.id, 'app', { createNotification(user.id, 'app', {
appAccessTokenId: token.id, appAccessTokenId: token ? token.id : null,
customBody: ps.body, customBody: ps.body,
customHeader: ps.header, customHeader: ps.header,
customIcon: ps.icon, customIcon: ps.icon,

View file

@ -18,7 +18,7 @@ export default class Connection {
public user?: User; public user?: User;
public following: User['id'][] = []; public following: User['id'][] = [];
public muting: User['id'][] = []; public muting: User['id'][] = [];
public token: AccessToken; public token?: AccessToken;
private wsConnection: websocket.connection; private wsConnection: websocket.connection;
public subscriber: EventEmitter; public subscriber: EventEmitter;
private channels: Channel[] = []; private channels: Channel[] = [];
@ -117,7 +117,7 @@ export default class Connection {
this.subscribingNotes[payload.id]++; this.subscribingNotes[payload.id]++;
if (this.subscribingNotes[payload.id] == 1) { if (this.subscribingNotes[payload.id] === 1) {
this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage);
} }