Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
468ff7037f | ||
|
|
df23504ccf | ||
|
|
66e3cb8eda | ||
|
|
6ddd2389dc | ||
|
|
402efb8c50 | ||
|
|
7b6eae0ce4 | ||
|
|
26ce9725ce | ||
|
|
ebfaa18f12 | ||
|
|
cc81d41a05 | ||
|
|
212176ee5c | ||
|
|
a63ec05e41 | ||
|
|
0dcb527bf3 | ||
|
|
54710f17fc | ||
|
|
e58a6593c0 | ||
|
|
62132570e1 | ||
|
|
9f0b8ba2f8 | ||
|
|
adbe0fbcd1 | ||
|
|
7896242f57 | ||
|
|
4a6722b9e9 | ||
|
|
7c9fb5228b | ||
|
|
81805b01cc | ||
|
|
50824a7245 | ||
|
|
6f2953f3a7 | ||
|
|
dd3f007582 | ||
|
|
a4b2b093fc | ||
|
|
0fbf56219f | ||
|
|
0acacf7a8e | ||
|
|
c84500d914 |
126
docs/setup.fr.md
Normal file
126
docs/setup.fr.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
Guide d'installation et de configuration de Misskey
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
Nous vous remerçions de l'intrêt que vous manifestez pour l'installation de votre propre instance Misskey !
|
||||||
|
Ce guide décrit les étapes à suivre afin d'installer et de configurer une instance Misskey.
|
||||||
|
|
||||||
|
[La version en japonnais est également disponible sur - 日本語版もあります](./setup.ja.md)
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
*1.* Création de l'utilisateur Misskey
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Lancer misskey en tant qu'utilisateur est une mauvaise idée, nous avons besoin de créer un utilisateur dédié.
|
||||||
|
Sur Debian, à titre d'exemple :
|
||||||
|
|
||||||
|
```
|
||||||
|
adduser --disabled-password --disabled-login misskey
|
||||||
|
```
|
||||||
|
|
||||||
|
*2.* Installation des dépendances
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Installez les paquets suivants :
|
||||||
|
|
||||||
|
#### Dépendences :package:
|
||||||
|
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0
|
||||||
|
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
|
||||||
|
|
||||||
|
##### Optionnels
|
||||||
|
* [Redis](https://redis.io/)
|
||||||
|
* Redis est optionnel mais nous vous recommandons vivement de l'installer
|
||||||
|
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche
|
||||||
|
|
||||||
|
*3.* Paramètrage de MongoDB
|
||||||
|
----------------------------------------------------------------
|
||||||
|
En mode root :
|
||||||
|
1. `mongo` Accédez au shell de mango
|
||||||
|
2. `use misskey` Utilisez la base de données misskey
|
||||||
|
3. `db.users.save( {dummy:"dummy"} )` Write dummy data to initialize the db.
|
||||||
|
4. `db.createUser( { user: "misskey", pwd: "<password>", roles: [ { role: "readWrite", db: "misskey" } ] } )` Créez l'utilisateur misskey.
|
||||||
|
5. `exit` Vous avez terminé !
|
||||||
|
|
||||||
|
*4.* Installation de Misskey
|
||||||
|
----------------------------------------------------------------
|
||||||
|
1. `su - misskey` Basculez vers l'utilisateur misskey.
|
||||||
|
2. `git clone -b master git://github.com/syuilo/misskey.git` Clonez la branche master du dépôt misskey.
|
||||||
|
3. `cd misskey` Accédez au dossier misskey.
|
||||||
|
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Télécharge la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
|
||||||
|
5. `npm install` Installez les dépendances de misskey.
|
||||||
|
|
||||||
|
*(optionnel)* Génération des clés VAPID
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Si vous désirez activer ServiceWorker, vous devez générer les clés VAPID :
|
||||||
|
Unless you have set your global node_modules location elsewhere, vous devez lancer ceci en mode root.
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
npm install web-push -g
|
||||||
|
web-push generate-vapid-keys
|
||||||
|
```
|
||||||
|
|
||||||
|
*5.* Création du fichier de configuration
|
||||||
|
----------------------------------------------------------------
|
||||||
|
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le `default.yml`.
|
||||||
|
2. Editez le fichier `default.yml`
|
||||||
|
|
||||||
|
*6.* Construction de Misskey
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Construisez Misskey comme ceci :
|
||||||
|
|
||||||
|
`npm run build`
|
||||||
|
|
||||||
|
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential`, `python`.
|
||||||
|
|
||||||
|
Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
|
||||||
|
|
||||||
|
1. `npm install -g node-gyp`
|
||||||
|
2. `node-gyp configure`
|
||||||
|
3. `node-gyp build`
|
||||||
|
4. `npm run build`
|
||||||
|
|
||||||
|
*7.* C'est tout.
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
|
||||||
|
|
||||||
|
### Lancement conventionnel
|
||||||
|
Lancez tout simplement `npm start`. Bonne chance et amusez-vous bien !
|
||||||
|
|
||||||
|
### Démarrage avec systemd
|
||||||
|
|
||||||
|
1. Créez une service systemd sur : `/etc/systemd/system/misskey.service`
|
||||||
|
2. Editez-le puis copiez et coller ceci dans le fichier :
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=Misskey daemon
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=misskey
|
||||||
|
ExecStart=/usr/bin/npm start
|
||||||
|
WorkingDirectory=/home/misskey/misskey
|
||||||
|
TimeoutSec=60
|
||||||
|
StandardOutput=syslog
|
||||||
|
StandardError=syslog
|
||||||
|
SyslogIdentifier=misskey
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
3. `systemctl daemon-reload ; systemctl enable misskey` Redémarre systemd et active le service misskey.
|
||||||
|
4. `systemctl start misskey` Démarre le service misskey.
|
||||||
|
|
||||||
|
Vous pouvez vérifier si le service a démarré en utilisant la commande `systemctl status misskey`.
|
||||||
|
|
||||||
|
### Méthode de mise à jour vers la plus récente version de Misskey
|
||||||
|
1. `git fetch`
|
||||||
|
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
|
||||||
|
3. `npm install`
|
||||||
|
4. `npm run build`
|
||||||
|
5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Si vous rencontrez des difficultés ou avez d'autres questions, n'hésitez pas à nous contacter !
|
||||||
@@ -379,6 +379,17 @@ common/views/components/poll-editor.vue:
|
|||||||
common/views/components/reaction-picker.vue:
|
common/views/components/reaction-picker.vue:
|
||||||
choose-reaction: "リアクションを選択"
|
choose-reaction: "リアクションを選択"
|
||||||
|
|
||||||
|
common/views/components/emoji-picker.vue:
|
||||||
|
custom-emoji: "カスタム絵文字"
|
||||||
|
people: "人"
|
||||||
|
animals-and-nature: "動物&自然"
|
||||||
|
food-and-drink: "食べ物&飲み物"
|
||||||
|
activity: "アクティビティ"
|
||||||
|
travel-and-places: "場所"
|
||||||
|
objects: "物"
|
||||||
|
symbols: "記号"
|
||||||
|
flags: "旗"
|
||||||
|
|
||||||
common/views/components/signin.vue:
|
common/views/components/signin.vue:
|
||||||
username: "ユーザー名"
|
username: "ユーザー名"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -657,13 +668,6 @@ desktop/views/components/media-video.vue:
|
|||||||
sensitive: "閲覧注意"
|
sensitive: "閲覧注意"
|
||||||
click-to-show: "クリックして表示"
|
click-to-show: "クリックして表示"
|
||||||
|
|
||||||
desktop/views/components/follow-button.vue:
|
|
||||||
following: "フォロー中"
|
|
||||||
follow: "フォロー"
|
|
||||||
request-pending: "フォロー許可待ち"
|
|
||||||
follow-processing: "フォロー処理中"
|
|
||||||
follow-request: "フォロー申請"
|
|
||||||
|
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
|
|
||||||
@@ -1325,7 +1329,7 @@ mobile/views/components/media-video.vue:
|
|||||||
sensitive: "閲覧注意"
|
sensitive: "閲覧注意"
|
||||||
click-to-show: "クリックして表示"
|
click-to-show: "クリックして表示"
|
||||||
|
|
||||||
mobile/views/components/follow-button.vue:
|
common/views/components/follow-button.vue:
|
||||||
following: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.48.0",
|
"version": "10.49.2",
|
||||||
"clientVersion": "2.0.11733",
|
"clientVersion": "2.0.11761",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -198,6 +198,7 @@
|
|||||||
"summaly": "2.2.0",
|
"summaly": "2.2.0",
|
||||||
"systeminformation": "3.47.0",
|
"systeminformation": "3.47.0",
|
||||||
"syuilo-password-strength": "0.0.1",
|
"syuilo-password-strength": "0.0.1",
|
||||||
|
"terser-webpack-plugin": "1.1.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"tinycolor2": "1.4.1",
|
"tinycolor2": "1.4.1",
|
||||||
"tmp": "0.0.33",
|
"tmp": "0.0.33",
|
||||||
|
|||||||
@@ -66,19 +66,6 @@ export default abstract class Chart<T> {
|
|||||||
} else {
|
} else {
|
||||||
this.collection.createIndex({ span: -1, date: -1 }, { unique: true });
|
this.collection.createIndex({ span: -1, date: -1 }, { unique: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region 後方互換性のため
|
|
||||||
this.collection.find({ span: 'day' }, { fields: { _id: true, date: true } }).then(logs => {
|
|
||||||
logs.forEach(log => {
|
|
||||||
this.collection.update({ _id: log._id }, { $set: { date: utc(log.date).hour(0).toDate() } });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.collection.find({ span: 'hour' }, { fields: { _id: true, date: true } }).then(logs => {
|
|
||||||
logs.forEach(log => {
|
|
||||||
this.collection.update({ _id: log._id }, { $set: { date: utc(log.date).toDate() } });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
|||||||
@@ -9,14 +9,11 @@ import './style.styl';
|
|||||||
|
|
||||||
import init from '../init';
|
import init from '../init';
|
||||||
import Index from './views/index.vue';
|
import Index from './views/index.vue';
|
||||||
import * as config from '../config';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init
|
* init
|
||||||
*/
|
*/
|
||||||
init(launch => {
|
init(launch => {
|
||||||
document.title = `${config.name} | %i18n:common.application-authorization%`;
|
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
mode: 'history',
|
mode: 'history',
|
||||||
|
|||||||
@@ -146,6 +146,8 @@
|
|||||||
function refresh() {
|
function refresh() {
|
||||||
localStorage.setItem('shouldFlush', 'false');
|
localStorage.setItem('shouldFlush', 'false');
|
||||||
|
|
||||||
|
localStorage.removeItem('locale');
|
||||||
|
|
||||||
// Random
|
// Random
|
||||||
localStorage.setItem('salt', Math.random().toString().substr(2, 8));
|
localStorage.setItem('salt', Math.random().toString().substr(2, 8));
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default function<T extends object>(data: {
|
|||||||
|
|
||||||
this.bakeProps();
|
this.bakeProps();
|
||||||
|
|
||||||
(this as any).api('i/update_widget', {
|
this.$root.api('i/update_widget', {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
data: this.props
|
data: this.props
|
||||||
});
|
});
|
||||||
|
|||||||
202
src/client/app/common/views/components/emoji-picker.vue
Normal file
202
src/client/app/common/views/components/emoji-picker.vue
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<template>
|
||||||
|
<div class="prlncendiewqqkrevzeruhndoakghvtx">
|
||||||
|
<header>
|
||||||
|
<button v-for="category in categories"
|
||||||
|
:title="category.text"
|
||||||
|
@click="go(category.ref)"
|
||||||
|
:class="{ active: category.isActive }"
|
||||||
|
>
|
||||||
|
<fa :icon="category.icon" fixed-width/>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<div class="emojis" ref="emojis" @scroll.passive="onScroll">
|
||||||
|
<section v-for="category in categories" :ref="category.ref">
|
||||||
|
<header><fa :icon="category.icon" fixed-width/> {{ category.text }}</header>
|
||||||
|
<div v-if="category.name">
|
||||||
|
<button v-for="emoji in Object.entries(lib).filter(([k, v]) => v.category === category.name)"
|
||||||
|
:title="emoji[0]"
|
||||||
|
@click="chosen(emoji[1].char)"
|
||||||
|
>
|
||||||
|
<mk-emoji :emoji="emoji[1].char"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<button v-for="emoji in customEmojis"
|
||||||
|
:title="emoji.name"
|
||||||
|
@click="chosen(`:${emoji.name}:`)"
|
||||||
|
>
|
||||||
|
<img :src="emoji.url" :alt="emoji.name"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
|
import { lib } from 'emojilib';
|
||||||
|
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { faHeart, faFlag } from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n: i18n('common/views/components/emoji-picker.vue'),
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
lib,
|
||||||
|
customEmojis: [],
|
||||||
|
categories: [{
|
||||||
|
ref: 'customEmojiSection',
|
||||||
|
text: this.$t('custom-emoji'),
|
||||||
|
icon: faAsterisk,
|
||||||
|
isActive: true
|
||||||
|
}, {
|
||||||
|
name: 'people',
|
||||||
|
ref: 'peopleSection',
|
||||||
|
text: this.$t('people'),
|
||||||
|
icon: ['far', 'laugh'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'animals_and_nature',
|
||||||
|
ref: 'animalsAndNatureSection',
|
||||||
|
text: this.$t('animals-and-nature'),
|
||||||
|
icon: faLeaf,
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'food_and_drink',
|
||||||
|
ref: 'foodAndDrinkSection',
|
||||||
|
text: this.$t('food-and-drink'),
|
||||||
|
icon: faUtensils,
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'activity',
|
||||||
|
ref: 'activitySection',
|
||||||
|
text: this.$t('activity'),
|
||||||
|
icon: faFutbol,
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'travel_and_places',
|
||||||
|
ref: 'travelAndPlacesSection',
|
||||||
|
text: this.$t('travel-and-places'),
|
||||||
|
icon: faCity,
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'objects',
|
||||||
|
ref: 'objectsSection',
|
||||||
|
text: this.$t('objects'),
|
||||||
|
icon: faDice,
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'symbols',
|
||||||
|
ref: 'symbolsSection',
|
||||||
|
text: this.$t('symbols'),
|
||||||
|
icon: faHeart,
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'flags',
|
||||||
|
ref: 'flagsSection',
|
||||||
|
text: this.$t('flags'),
|
||||||
|
icon: faFlag,
|
||||||
|
isActive: false
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
go(ref) {
|
||||||
|
this.$refs.emojis.scrollTop = this.$refs[ref][0].offsetTop;
|
||||||
|
},
|
||||||
|
|
||||||
|
onScroll(e) {
|
||||||
|
const section = this.categories.forEach(x => {
|
||||||
|
const top = e.target.scrollTop;
|
||||||
|
const el = this.$refs[x.ref][0];
|
||||||
|
x.isActive = el.offsetTop <= top && el.offsetTop + el.offsetHeight > top;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
chosen(emoji) {
|
||||||
|
this.$emit('chosen', emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.prlncendiewqqkrevzeruhndoakghvtx
|
||||||
|
width 350px
|
||||||
|
background var(--face)
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
|
||||||
|
> button
|
||||||
|
flex 1
|
||||||
|
padding 10px 0
|
||||||
|
font-size 16px
|
||||||
|
color var(--text)
|
||||||
|
transition color 0.2s ease
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
transition color 0s
|
||||||
|
|
||||||
|
&.active
|
||||||
|
color var(--primary)
|
||||||
|
transition color 0s
|
||||||
|
|
||||||
|
> .emojis
|
||||||
|
height 300px
|
||||||
|
overflow-y auto
|
||||||
|
overflow-x hidden
|
||||||
|
|
||||||
|
> section
|
||||||
|
> header
|
||||||
|
position sticky
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
z-index 1
|
||||||
|
padding 8px
|
||||||
|
background var(--faceHeader)
|
||||||
|
color var(--text)
|
||||||
|
font-size 12px
|
||||||
|
|
||||||
|
> div
|
||||||
|
display grid
|
||||||
|
grid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
|
||||||
|
gap 4px
|
||||||
|
padding 8px
|
||||||
|
|
||||||
|
> button
|
||||||
|
padding 0
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ''
|
||||||
|
display block
|
||||||
|
width 1px
|
||||||
|
height 0
|
||||||
|
padding-bottom 100%
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
> *
|
||||||
|
transform scale(1.2)
|
||||||
|
transition transform 0s
|
||||||
|
|
||||||
|
> *
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
font-size 28px
|
||||||
|
transition transform 0.2s ease
|
||||||
|
pointer-events none
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -22,7 +22,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
customEmojis: {
|
customEmojis: {
|
||||||
required: false,
|
required: false,
|
||||||
default: []
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
184
src/client/app/common/views/components/follow-button.vue
Normal file
184
src/client/app/common/views/components/follow-button.vue
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<button class="wfliddvnhxvyusikowhxozkyxyenqxqr"
|
||||||
|
:class="{ wait, block, mini, active: isFollowing || hasPendingFollowRequestFromYou }"
|
||||||
|
@click="onClick"
|
||||||
|
:disabled="wait"
|
||||||
|
>
|
||||||
|
<template v-if="!wait">
|
||||||
|
<fa :icon="iconAndText[0]"/> <template v-if="!mini">{{ iconAndText[1] }}</template>
|
||||||
|
</template>
|
||||||
|
<template v-else><fa icon="spinner" pulse fixed-width/></template>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n: i18n('common/views/components/follow-button.vue'),
|
||||||
|
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
block: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
mini: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isFollowing: this.user.isFollowing,
|
||||||
|
hasPendingFollowRequestFromYou: this.user.hasPendingFollowRequestFromYou,
|
||||||
|
wait: false,
|
||||||
|
connection: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
iconAndText(): any[] {
|
||||||
|
return (
|
||||||
|
(this.hasPendingFollowRequestFromYou && this.user.isLocked) ? ['hourglass-half', this.$t('request-pending')] :
|
||||||
|
(this.hasPendingFollowRequestFromYou && !this.user.isLocked) ? ['hourglass-start', this.$t('follow-processing')] :
|
||||||
|
(this.isFollowing) ? ['minus', this.$t('following')] :
|
||||||
|
(!this.isFollowing && this.user.isLocked) ? ['plus', this.$t('follow-request')] :
|
||||||
|
(!this.isFollowing && !this.user.isLocked) ? ['plus', this.$t('follow')] :
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.connection = this.$root.stream.useSharedConnection('main');
|
||||||
|
|
||||||
|
this.connection.on('follow', this.onFollowChange);
|
||||||
|
this.connection.on('unfollow', this.onFollowChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.connection.dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onFollowChange(user) {
|
||||||
|
if (user.id == this.user.id) {
|
||||||
|
this.isFollowing = user.isFollowing;
|
||||||
|
this.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onClick() {
|
||||||
|
this.wait = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.isFollowing) {
|
||||||
|
await this.$root.api('following/delete', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (this.hasPendingFollowRequestFromYou) {
|
||||||
|
await this.$root.api('following/requests/cancel', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
} else if (this.user.isLocked) {
|
||||||
|
await this.$root.api('following/create', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
this.hasPendingFollowRequestFromYou = true;
|
||||||
|
} else {
|
||||||
|
await this.$root.api('following/create', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
this.hasPendingFollowRequestFromYou = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
} finally {
|
||||||
|
this.wait = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.wfliddvnhxvyusikowhxozkyxyenqxqr
|
||||||
|
display block
|
||||||
|
user-select none
|
||||||
|
cursor pointer
|
||||||
|
padding 0 16px
|
||||||
|
margin 0
|
||||||
|
min-width 100px
|
||||||
|
line-height 36px
|
||||||
|
font-size 14px
|
||||||
|
font-weight bold
|
||||||
|
color var(--primary)
|
||||||
|
background transparent
|
||||||
|
outline none
|
||||||
|
border solid 1px var(--primary)
|
||||||
|
border-radius 36px
|
||||||
|
|
||||||
|
&.mini
|
||||||
|
padding 0
|
||||||
|
min-width 0
|
||||||
|
width 32px
|
||||||
|
height 32px
|
||||||
|
font-size 16px
|
||||||
|
border-radius 4px
|
||||||
|
line-height 32px
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
&:after
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
&.block
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
pointer-events none
|
||||||
|
position absolute
|
||||||
|
top -5px
|
||||||
|
right -5px
|
||||||
|
bottom -5px
|
||||||
|
left -5px
|
||||||
|
border 2px solid var(--primaryAlpha03)
|
||||||
|
border-radius 36px
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background var(--primaryAlpha01)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background var(--primaryAlpha02)
|
||||||
|
|
||||||
|
&.active
|
||||||
|
color var(--primaryForeground)
|
||||||
|
background var(--primary)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background var(--primaryLighten10)
|
||||||
|
border-color var(--primaryLighten10)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background var(--primaryDarken10)
|
||||||
|
border-color var(--primaryDarken10)
|
||||||
|
|
||||||
|
&.wait
|
||||||
|
cursor wait !important
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
|
*
|
||||||
|
pointer-events none
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-github-setting">
|
<div class="mk-github-setting">
|
||||||
<p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-github`" target="_blank">{{ $t('detail') }}</a></p>
|
<p>{{ $t('description') }}</p>
|
||||||
<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">{{ $t('connected-to') }}: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
|
<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">{{ $t('connected-to') }}: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
|
||||||
<p>
|
<p>
|
||||||
<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? this.$t('reconnect') : this.$t('connect') }}</a>
|
<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? this.$t('reconnect') : this.$t('connect') }}</a>
|
||||||
@@ -14,15 +14,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import { apiUrl, docsUrl } from '../../../config';
|
import { apiUrl } from '../../../config';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('common/views/components/github-setting.vue'),
|
i18n: i18n('common/views/components/github-setting.vue'),
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form: null,
|
form: null,
|
||||||
apiUrl,
|
apiUrl
|
||||||
docsUrl
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
import muteAndBlock from './mute-and-block.vue';
|
import followButton from './follow-button.vue';
|
||||||
import error from './error.vue';
|
import error from './error.vue';
|
||||||
import apiSettings from './api-settings.vue';
|
|
||||||
import passwordSettings from './password-settings.vue';
|
|
||||||
import driveSettings from './drive-settings.vue';
|
|
||||||
import profileEditor from './profile-editor.vue';
|
|
||||||
import noteSkeleton from './note-skeleton.vue';
|
import noteSkeleton from './note-skeleton.vue';
|
||||||
import theme from './theme.vue';
|
|
||||||
import instance from './instance.vue';
|
import instance from './instance.vue';
|
||||||
import cwButton from './cw-button.vue';
|
import cwButton from './cw-button.vue';
|
||||||
import tagCloud from './tag-cloud.vue';
|
import tagCloud from './tag-cloud.vue';
|
||||||
@@ -27,7 +22,6 @@ import pollEditor from './poll-editor.vue';
|
|||||||
import reactionIcon from './reaction-icon.vue';
|
import reactionIcon from './reaction-icon.vue';
|
||||||
import reactionsViewer from './reactions-viewer.vue';
|
import reactionsViewer from './reactions-viewer.vue';
|
||||||
import time from './time.vue';
|
import time from './time.vue';
|
||||||
import timer from './timer.vue';
|
|
||||||
import mediaList from './media-list.vue';
|
import mediaList from './media-list.vue';
|
||||||
import uploader from './uploader.vue';
|
import uploader from './uploader.vue';
|
||||||
import streamIndicator from './stream-indicator.vue';
|
import streamIndicator from './stream-indicator.vue';
|
||||||
@@ -51,14 +45,9 @@ import uiInfo from './ui/info.vue';
|
|||||||
import formButton from './ui/form/button.vue';
|
import formButton from './ui/form/button.vue';
|
||||||
import formRadio from './ui/form/radio.vue';
|
import formRadio from './ui/form/radio.vue';
|
||||||
|
|
||||||
Vue.component('mk-mute-and-block', muteAndBlock);
|
Vue.component('mk-follow-button', followButton);
|
||||||
Vue.component('mk-error', error);
|
Vue.component('mk-error', error);
|
||||||
Vue.component('mk-api-settings', apiSettings);
|
|
||||||
Vue.component('mk-password-settings', passwordSettings);
|
|
||||||
Vue.component('mk-drive-settings', driveSettings);
|
|
||||||
Vue.component('mk-profile-editor', profileEditor);
|
|
||||||
Vue.component('mk-note-skeleton', noteSkeleton);
|
Vue.component('mk-note-skeleton', noteSkeleton);
|
||||||
Vue.component('mk-theme', theme);
|
|
||||||
Vue.component('mk-instance', instance);
|
Vue.component('mk-instance', instance);
|
||||||
Vue.component('mk-cw-button', cwButton);
|
Vue.component('mk-cw-button', cwButton);
|
||||||
Vue.component('mk-tag-cloud', tagCloud);
|
Vue.component('mk-tag-cloud', tagCloud);
|
||||||
@@ -78,7 +67,6 @@ Vue.component('mk-poll-editor', pollEditor);
|
|||||||
Vue.component('mk-reaction-icon', reactionIcon);
|
Vue.component('mk-reaction-icon', reactionIcon);
|
||||||
Vue.component('mk-reactions-viewer', reactionsViewer);
|
Vue.component('mk-reactions-viewer', reactionsViewer);
|
||||||
Vue.component('mk-time', time);
|
Vue.component('mk-time', time);
|
||||||
Vue.component('mk-timer', timer);
|
|
||||||
Vue.component('mk-media-list', mediaList);
|
Vue.component('mk-media-list', mediaList);
|
||||||
Vue.component('mk-uploader', uploader);
|
Vue.component('mk-uploader', uploader);
|
||||||
Vue.component('mk-stream-indicator', streamIndicator);
|
Vue.component('mk-stream-indicator', streamIndicator);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ $t('empty') }}</p>
|
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ $t('empty') }}</p>
|
||||||
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>{{ $t('no-history') }}</p>
|
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>{{ $t('no-history') }}</p>
|
||||||
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
|
<button class="more" :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('@.load-more') }}
|
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
|
||||||
</button>
|
</button>
|
||||||
<template v-for="(message, i) in _messages">
|
<template v-for="(message, i) in _messages">
|
||||||
<x-message :message="message" :key="message.id"/>
|
<x-message :message="message" :key="message.id"/>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<p class="no-history" v-if="!fetching && messages.length == 0">{{ $t('no-history') }}</p>
|
<p class="no-history" v-if="!fetching && messages.length == 0">{{ $t('no-history') }}</p>
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<span>{{ $t('username') }}</span>
|
<span>{{ $t('username') }}</span>
|
||||||
<span slot="prefix">@</span>
|
<span slot="prefix">@</span>
|
||||||
<span slot="suffix">@{{ host }}</span>
|
<span slot="suffix">@{{ host }}</span>
|
||||||
<p slot="desc" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner .pulse" fixed-width/> {{ $t('checking') }}</p>
|
<p slot="desc" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner" pulse fixed-width/> {{ $t('checking') }}</p>
|
||||||
<p slot="desc" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('available') }}</p>
|
<p slot="desc" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('available') }}</p>
|
||||||
<p slot="desc" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('unavailable') }}</p>
|
<p slot="desc" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('unavailable') }}</p>
|
||||||
<p slot="desc" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('error') }}</p>
|
<p slot="desc" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('error') }}</p>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-stream-indicator">
|
<div class="mk-stream-indicator">
|
||||||
<p v-if="stream.state == 'initializing'">
|
<p v-if="stream.state == 'initializing'">
|
||||||
<fa icon="spinner .pulse"/>
|
<fa icon="spinner" pulse/>
|
||||||
<span>{{ $t('connecting') }}<mk-ellipsis/></span>
|
<span>{{ $t('connecting') }}<mk-ellipsis/></span>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="stream.state == 'reconnecting'">
|
<p v-if="stream.state == 'reconnecting'">
|
||||||
<fa icon="spinner .pulse"/>
|
<fa icon="spinner" pulse/>
|
||||||
<span>{{ $t('reconnecting') }}<mk-ellipsis/></span>
|
<span>{{ $t('reconnecting') }}<mk-ellipsis/></span>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="stream.state == 'connected'">
|
<p v-if="stream.state == 'connected'">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
|
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
|
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<vue-word-cloud
|
<vue-word-cloud
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
<template>
|
|
||||||
<time class="mk-time">
|
|
||||||
{{ hh }}:{{ mm }}:{{ ss }}
|
|
||||||
</time>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
props: {
|
|
||||||
time: {
|
|
||||||
type: [Date, String],
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
tickId: null,
|
|
||||||
hh: null,
|
|
||||||
mm: null,
|
|
||||||
ss: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
_time(): Date {
|
|
||||||
return typeof this.time == 'string' ? new Date(this.time) : this.time;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.tick();
|
|
||||||
this.tickId = setInterval(this.tick, 1000);
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
clearInterval(this.tickId);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
tick() {
|
|
||||||
const now = new Date().getTime();
|
|
||||||
const start = this._time.getTime();
|
|
||||||
const ago = Math.floor((now - start) / 1000);
|
|
||||||
|
|
||||||
this.hh = Math.floor(ago / (60 * 60)).toString().padStart(2, '0');
|
|
||||||
this.mm = Math.floor(ago / 60).toString().padStart(2, '0');
|
|
||||||
this.ss = (ago % 60).toString().padStart(2, '0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
|
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
|
<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
|
||||||
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
||||||
<transition-group v-else tag="div" name="chart">
|
<transition-group v-else tag="div" name="chart">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mk-twitter-setting">
|
<div class="mk-twitter-setting">
|
||||||
<p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-twitter`" target="_blank">{{ $t('detail') }}</a></p>
|
<p>{{ $t('description') }}</p>
|
||||||
<p class="account" v-if="$store.state.i.twitter" :title="`Twitter ID: ${$store.state.i.twitter.userId}`">{{ $t('connected-to') }}: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
|
<p class="account" v-if="$store.state.i.twitter" :title="`Twitter ID: ${$store.state.i.twitter.userId}`">{{ $t('connected-to') }}: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
|
||||||
<p>
|
<p>
|
||||||
<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ $store.state.i.twitter ? this.$t('reconnect') : this.$t('connect') }}</a>
|
<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ $store.state.i.twitter ? this.$t('reconnect') : this.$t('connect') }}</a>
|
||||||
@@ -14,15 +14,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import { apiUrl, docsUrl } from '../../../config';
|
import { apiUrl } from '../../../config';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('common/views/components/twitter-setting.vue'),
|
i18n: i18n('common/views/components/twitter-setting.vue'),
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form: null,
|
form: null,
|
||||||
apiUrl,
|
apiUrl
|
||||||
docsUrl
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<ol v-if="uploads.length > 0">
|
<ol v-if="uploads.length > 0">
|
||||||
<li v-for="ctx in uploads" :key="ctx.id">
|
<li v-for="ctx in uploads" :key="ctx.id">
|
||||||
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
|
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
|
||||||
<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
|
<p class="name"><fa icon="spinner" pulse/>{{ ctx.name }}</p>
|
||||||
<p class="status">
|
<p class="status">
|
||||||
<span class="initing" v-if="ctx.progress == undefined">{{ $t('waiting') }}<mk-ellipsis/></span>
|
<span class="initing" v-if="ctx.progress == undefined">{{ $t('waiting') }}<mk-ellipsis/></span>
|
||||||
<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
|
<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<template v-else-if="!user.isFollowing && user.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
|
<template v-else-if="!user.isFollowing && user.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
|
||||||
<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
|
<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
<template v-else><fa icon="spinner" pulse fixed-width/></template>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,9 +3,15 @@
|
|||||||
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
|
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
|
||||||
<template slot="header"><fa icon="camera"/>{{ $t('title') }}</template>
|
<template slot="header"><fa icon="camera"/>{{ $t('title') }}</template>
|
||||||
|
|
||||||
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div :class="$style.stream" v-if="!fetching && images.length > 0">
|
<div :class="$style.stream" v-if="!fetching && images.length > 0">
|
||||||
<div v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
|
<div v-for="image in images"
|
||||||
|
:class="$style.img"
|
||||||
|
:style="`background-image: url(${image.thumbnailUrl || image.url})`"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragstart(image, $event)"
|
||||||
|
@dragend="onDragend"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
|
<p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
|
||||||
</mk-widget-container>
|
</mk-widget-container>
|
||||||
@@ -31,6 +37,7 @@ export default define({
|
|||||||
connection: null
|
connection: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = this.$root.stream.useSharedConnection('main');
|
this.connection = this.$root.stream.useSharedConnection('main');
|
||||||
|
|
||||||
@@ -44,9 +51,11 @@ export default define({
|
|||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.dispose();
|
this.connection.dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onDriveFileCreated(file) {
|
onDriveFileCreated(file) {
|
||||||
if (/^image\/.+$/.test(file.type)) {
|
if (/^image\/.+$/.test(file.type)) {
|
||||||
@@ -54,6 +63,7 @@ export default define({
|
|||||||
if (this.images.length > 9) this.images.pop();
|
if (this.images.length > 9) this.images.pop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
if (this.props.design == 2) {
|
if (this.props.design == 2) {
|
||||||
this.props.design = 0;
|
this.props.design = 0;
|
||||||
@@ -62,7 +72,16 @@ export default define({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
}
|
},
|
||||||
|
|
||||||
|
onDragstart(file, e) {
|
||||||
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
e.dataTransfer.setData('mk_drive_file', JSON.stringify(file));
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragend(e) {
|
||||||
|
this.browser.isDragSource = false;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
|
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
|
||||||
|
|
||||||
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
|
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div class="feed" v-else>
|
<div class="feed" v-else>
|
||||||
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
|
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="memory">
|
<div class="memory">
|
||||||
<x-pie class="pie" :value="usage"/>
|
<x-pie class="pie" :value="usage"/>
|
||||||
<div>
|
<div>
|
||||||
<p><fa icon="flask"/>Memory</p>
|
<p><fa icon="memory"/>Memory</p>
|
||||||
<p>Total: {{ total | bytes(1) }}</p>
|
<p>Total: {{ total | bytes(1) }}</p>
|
||||||
<p>Used: {{ used | bytes(1) }}</p>
|
<p>Used: {{ used | bytes(1) }}</p>
|
||||||
<p>Free: {{ free | bytes(1) }}</p>
|
<p>Free: {{ free | bytes(1) }}</p>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<template slot="header"><fa icon="server"/>{{ $t('title') }}</template>
|
<template slot="header"><fa icon="server"/>{{ $t('title') }}</template>
|
||||||
<button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
|
<button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
|
||||||
|
|
||||||
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<template v-if="!fetching">
|
<template v-if="!fetching">
|
||||||
<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
|
<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
|
||||||
<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
|
<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<template slot="header"><fa icon="chart-bar"/>{{ $t('title') }}</template>
|
<template slot="header"><fa icon="chart-bar"/>{{ $t('title') }}</template>
|
||||||
<button slot="func" :title="$t('toggle')" @click="toggle"><fa icon="sort"/></button>
|
<button slot="func" :title="$t('toggle')" @click="toggle"><fa icon="sort"/></button>
|
||||||
|
|
||||||
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<x-calendar v-show="view == 0" :data="[].concat(activity)"/>
|
<x-calendar v-show="view == 0" :data="[].concat(activity)"/>
|
||||||
<x-chart v-show="view == 1" :data="[].concat(activity)"/>
|
<x-chart v-show="view == 1" :data="[].concat(activity)"/>
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gcafiosrssbtbnbzqupfmglvzgiaipyv">
|
||||||
|
<x-picker @chosen="chosen"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import contains from '../../../common/scripts/contains';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XPicker: () => import('../../../common/views/components/emoji-picker.vue').then(m => m.default)
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
x: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const width = this.$el.offsetWidth;
|
||||||
|
const height = this.$el.offsetHeight;
|
||||||
|
|
||||||
|
let x = this.x;
|
||||||
|
let y = this.y;
|
||||||
|
|
||||||
|
if (x + width - window.pageXOffset > window.innerWidth) {
|
||||||
|
x = window.innerWidth - width + window.pageXOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y + height - window.pageYOffset > window.innerHeight) {
|
||||||
|
y = window.innerHeight - height + window.pageYOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$el.style.left = x + 'px';
|
||||||
|
this.$el.style.top = y + 'px';
|
||||||
|
|
||||||
|
Array.from(document.querySelectorAll('body *')).forEach(el => {
|
||||||
|
el.addEventListener('mousedown', this.onMousedown);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onMousedown(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
chosen(emoji) {
|
||||||
|
this.$emit('chosen', emoji);
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
Array.from(document.querySelectorAll('body *')).forEach(el => {
|
||||||
|
el.removeEventListener('mousedown', this.onMousedown);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$emit('closed');
|
||||||
|
this.destroyDom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.gcafiosrssbtbnbzqupfmglvzgiaipyv
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
z-index 3000
|
||||||
|
box-shadow 0 2px 12px 0 rgba(0, 0, 0, 0.3)
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
<template>
|
|
||||||
<button class="mk-follow-button"
|
|
||||||
:class="{ wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou, big: size == 'big' }"
|
|
||||||
@click="onClick"
|
|
||||||
:disabled="wait"
|
|
||||||
>
|
|
||||||
<template v-if="!wait">
|
|
||||||
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/><template v-if="size == 'big'"> {{ $t('request-pending') }}</template></template>
|
|
||||||
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/><template v-if="size == 'big'"> {{ $t('follow-processing') }}</template></template>
|
|
||||||
<template v-else-if="u.isFollowing"><fa icon="minus"/><template v-if="size == 'big'"> {{ $t('following') }}</template></template>
|
|
||||||
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow-request') }}</template></template>
|
|
||||||
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow') }}</template></template>
|
|
||||||
</template>
|
|
||||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
import i18n from '../../../i18n';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
i18n: i18n('desktop/views/components/follow-button.vue'),
|
|
||||||
props: {
|
|
||||||
user: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'compact'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
u: this.user,
|
|
||||||
wait: false,
|
|
||||||
connection: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.connection = this.$root.stream.useSharedConnection('main');
|
|
||||||
this.connection.on('follow', this.onFollowChange);
|
|
||||||
this.connection.on('unfollow', this.onFollowChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.connection.dispose();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onFollowChange(user) {
|
|
||||||
if (user.id == this.u.id) {
|
|
||||||
this.u.isFollowing = user.isFollowing;
|
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
|
||||||
this.$forceUpdate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async onClick() {
|
|
||||||
this.wait = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.u.isFollowing) {
|
|
||||||
this.u = await this.$root.api('following/delete', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (this.u.hasPendingFollowRequestFromYou) {
|
|
||||||
this.u = await this.$root.api('following/requests/cancel', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else if (this.u.isLocked) {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.user.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
} finally {
|
|
||||||
this.wait = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-follow-button
|
|
||||||
display block
|
|
||||||
cursor pointer
|
|
||||||
padding 0
|
|
||||||
margin 0
|
|
||||||
width 32px
|
|
||||||
height 32px
|
|
||||||
font-size 1em
|
|
||||||
outline none
|
|
||||||
border-radius 4px
|
|
||||||
|
|
||||||
*
|
|
||||||
pointer-events none
|
|
||||||
|
|
||||||
&:focus
|
|
||||||
&:after
|
|
||||||
content ""
|
|
||||||
pointer-events none
|
|
||||||
position absolute
|
|
||||||
top -5px
|
|
||||||
right -5px
|
|
||||||
bottom -5px
|
|
||||||
left -5px
|
|
||||||
border 2px solid var(--primaryAlpha03)
|
|
||||||
border-radius 8px
|
|
||||||
|
|
||||||
&:not(.active)
|
|
||||||
color var(--primary)
|
|
||||||
border solid 1px var(--primary)
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background var(--primaryAlpha03)
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background var(--primaryAlpha05)
|
|
||||||
|
|
||||||
&.active
|
|
||||||
color var(--primaryForeground)
|
|
||||||
background var(--primary)
|
|
||||||
border solid 1px var(--primary)
|
|
||||||
|
|
||||||
&:not(:disabled)
|
|
||||||
font-weight bold
|
|
||||||
|
|
||||||
&:hover:not(:disabled)
|
|
||||||
background var(--primaryLighten5)
|
|
||||||
border-color var(--primaryLighten5)
|
|
||||||
|
|
||||||
&:active:not(:disabled)
|
|
||||||
background var(--primaryDarken5)
|
|
||||||
border-color var(--primaryDarken5)
|
|
||||||
|
|
||||||
&.wait
|
|
||||||
cursor wait !important
|
|
||||||
opacity 0.7
|
|
||||||
|
|
||||||
&.big
|
|
||||||
width 100%
|
|
||||||
height 38px
|
|
||||||
line-height 38px
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('empty') }}</p>
|
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('empty') }}</p>
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('fetching') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('fetching') }}<mk-ellipsis/></p>
|
||||||
<a class="refresh" @click="refresh">{{ $t('refresh') }}</a>
|
<a class="refresh" @click="refresh">{{ $t('refresh') }}</a>
|
||||||
<button class="close" @click="destroyDom()" :title="$t('title')"><fa icon="times"/></button>
|
<button class="close" @click="destroyDom()" :title="$t('title')"><fa icon="times"/></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ import mediaVideo from './media-video.vue';
|
|||||||
import notifications from './notifications.vue';
|
import notifications from './notifications.vue';
|
||||||
import noteForm from './post-form.vue';
|
import noteForm from './post-form.vue';
|
||||||
import renoteForm from './renote-form.vue';
|
import renoteForm from './renote-form.vue';
|
||||||
import followButton from './follow-button.vue';
|
|
||||||
import notePreview from './note-preview.vue';
|
import notePreview from './note-preview.vue';
|
||||||
import noteDetail from './note-detail.vue';
|
import noteDetail from './note-detail.vue';
|
||||||
import settings from './settings.vue';
|
|
||||||
import calendar from './calendar.vue';
|
import calendar from './calendar.vue';
|
||||||
import activity from './activity.vue';
|
import activity from './activity.vue';
|
||||||
import friendsMaker from './friends-maker.vue';
|
import friendsMaker from './friends-maker.vue';
|
||||||
@@ -39,10 +37,8 @@ Vue.component('mk-media-video', mediaVideo);
|
|||||||
Vue.component('mk-notifications', notifications);
|
Vue.component('mk-notifications', notifications);
|
||||||
Vue.component('mk-post-form', noteForm);
|
Vue.component('mk-post-form', noteForm);
|
||||||
Vue.component('mk-renote-form', renoteForm);
|
Vue.component('mk-renote-form', renoteForm);
|
||||||
Vue.component('mk-follow-button', followButton);
|
|
||||||
Vue.component('mk-note-preview', notePreview);
|
Vue.component('mk-note-preview', notePreview);
|
||||||
Vue.component('mk-note-detail', noteDetail);
|
Vue.component('mk-note-detail', noteDetail);
|
||||||
Vue.component('mk-settings', settings);
|
|
||||||
Vue.component('mk-calendar', calendar);
|
Vue.component('mk-calendar', calendar);
|
||||||
Vue.component('mk-activity', activity);
|
Vue.component('mk-activity', activity);
|
||||||
Vue.component('mk-friends-maker', friendsMaker);
|
Vue.component('mk-friends-maker', friendsMaker);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
:disabled="conversationFetching"
|
:disabled="conversationFetching"
|
||||||
>
|
>
|
||||||
<template v-if="!conversationFetching"><fa icon="ellipsis-v"/></template>
|
<template v-if="!conversationFetching"><fa icon="ellipsis-v"/></template>
|
||||||
<template v-if="conversationFetching"><fa icon="spinner .pulse"/></template>
|
<template v-if="conversationFetching"><fa icon="spinner" pulse/></template>
|
||||||
</button>
|
</button>
|
||||||
<div class="conversation">
|
<div class="conversation">
|
||||||
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>
|
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<footer v-if="more">
|
<footer v-if="more">
|
||||||
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||||
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
|
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
|
||||||
<template v-if="moreFetching"><fa icon="spinner .pulse" fixed-width/></template>
|
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@
|
|||||||
</component>
|
</component>
|
||||||
</div>
|
</div>
|
||||||
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
||||||
<template v-if="fetchingMoreNotifications"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
|
<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
|
||||||
</button>
|
</button>
|
||||||
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
|
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,11 +15,15 @@
|
|||||||
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
||||||
</div>
|
</div>
|
||||||
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
||||||
|
<div class="textarea">
|
||||||
<textarea :class="{ with: (files.length != 0 || poll) }"
|
<textarea :class="{ with: (files.length != 0 || poll) }"
|
||||||
ref="text" v-model="text" :disabled="posting"
|
ref="text" v-model="text" :disabled="posting"
|
||||||
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
|
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
|
||||||
v-autocomplete="'text'"
|
v-autocomplete="'text'"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
<button class="emoji" @click="emoji" ref="emoji">
|
||||||
|
<fa :icon="['far', 'laugh']"/>
|
||||||
|
</button>
|
||||||
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
|
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
|
||||||
<x-draggable :list="files" :options="{ animation: 150 }">
|
<x-draggable :list="files" :options="{ animation: 150 }">
|
||||||
<div v-for="file in files" :key="file.id">
|
<div v-for="file in files" :key="file.id">
|
||||||
@@ -31,12 +35,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
|
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
|
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
|
||||||
<button class="upload" :title="$t('attach-media-from-local')" @click="chooseFile"><fa icon="upload"/></button>
|
<button class="upload" :title="$t('attach-media-from-local')" @click="chooseFile"><fa icon="upload"/></button>
|
||||||
<button class="drive" :title="$t('attach-media-from-drive')" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
<button class="drive" :title="$t('attach-media-from-drive')" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
||||||
<button class="kao" :title="$t('insert-a-kao')" @click="kao"><fa :icon="['far', 'smile']"/></button>
|
<button class="kao" :title="$t('insert-a-kao')" @click="kao"><fa :icon="['far', 'smile']"/></button>
|
||||||
<button class="poll" :title="$t('create-poll')" @click="poll = !poll"><fa icon="chart-pie"/></button>
|
<button class="poll" :title="$t('create-poll')" @click="poll = !poll"><fa icon="chart-pie"/></button>
|
||||||
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa icon="eye-slash"/></button>
|
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa :icon="['far', 'eye-slash']"/></button>
|
||||||
<button class="geo" :title="$t('attach-location-information')" @click="geo ? removeGeo() : setGeo()"><fa icon="map-marker-alt"/></button>
|
<button class="geo" :title="$t('attach-location-information')" @click="geo ? removeGeo() : setGeo()"><fa icon="map-marker-alt"/></button>
|
||||||
<button class="visibility" :title="$t('visibility')" @click="setVisibility" ref="visibilityButton">
|
<button class="visibility" :title="$t('visibility')" @click="setVisibility" ref="visibilityButton">
|
||||||
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
|
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
|
||||||
@@ -377,6 +382,19 @@ export default Vue.extend({
|
|||||||
this.visibleUsers = erase(user, this.visibleUsers);
|
this.visibleUsers = erase(user, this.visibleUsers);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async emoji() {
|
||||||
|
const Picker = await import('./emoji-picker-dialog.vue').then(m => m.default);
|
||||||
|
const button = this.$refs.emoji;
|
||||||
|
const rect = button.getBoundingClientRect();
|
||||||
|
const vm = this.$root.new(Picker, {
|
||||||
|
x: button.offsetWidth + rect.left + window.pageXOffset,
|
||||||
|
y: rect.top + window.pageYOffset
|
||||||
|
});
|
||||||
|
vm.$once('chosen', emoji => {
|
||||||
|
insertTextAtCursor(this.$refs.text, emoji);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
post() {
|
post() {
|
||||||
this.posting = true;
|
this.posting = true;
|
||||||
|
|
||||||
@@ -469,7 +487,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
> .content
|
> .content
|
||||||
> input
|
> input
|
||||||
> textarea
|
> .textarea > textarea
|
||||||
display block
|
display block
|
||||||
width 100%
|
width 100%
|
||||||
padding 12px
|
padding 12px
|
||||||
@@ -498,6 +516,24 @@ export default Vue.extend({
|
|||||||
> input
|
> input
|
||||||
margin-bottom 8px
|
margin-bottom 8px
|
||||||
|
|
||||||
|
> .textarea
|
||||||
|
> .emoji
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
padding 10px
|
||||||
|
font-size 18px
|
||||||
|
color var(--text)
|
||||||
|
opacity 0.5
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
&:active
|
||||||
|
color var(--primary)
|
||||||
|
opacity 1
|
||||||
|
|
||||||
> textarea
|
> textarea
|
||||||
margin 0
|
margin 0
|
||||||
max-width 100%
|
max-width 100%
|
||||||
@@ -505,42 +541,24 @@ export default Vue.extend({
|
|||||||
min-height 84px
|
min-height 84px
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
& + *
|
|
||||||
& + * + *
|
& + * + *
|
||||||
|
& + * + * + *
|
||||||
border-color var(--primaryAlpha02)
|
border-color var(--primaryAlpha02)
|
||||||
transition border-color .1s ease
|
transition border-color .1s ease
|
||||||
|
|
||||||
&:focus
|
&:focus
|
||||||
& + *
|
|
||||||
& + * + *
|
& + * + *
|
||||||
|
& + * + * + *
|
||||||
border-color var(--primaryAlpha05)
|
border-color var(--primaryAlpha05)
|
||||||
transition border-color 0s ease
|
transition border-color 0s ease
|
||||||
|
|
||||||
|
& + .emoji
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
&.with
|
&.with
|
||||||
border-bottom solid 1px var(--primaryAlpha01) !important
|
border-bottom solid 1px var(--primaryAlpha01) !important
|
||||||
border-radius 4px 4px 0 0
|
border-radius 4px 4px 0 0
|
||||||
|
|
||||||
> .visibleUsers
|
|
||||||
margin-bottom 8px
|
|
||||||
font-size 14px
|
|
||||||
|
|
||||||
> span
|
|
||||||
margin-right 16px
|
|
||||||
color var(--primary)
|
|
||||||
|
|
||||||
> .hashtags
|
|
||||||
margin 0 0 8px 0
|
|
||||||
overflow hidden
|
|
||||||
white-space nowrap
|
|
||||||
font-size 14px
|
|
||||||
|
|
||||||
> b
|
|
||||||
color var(--primary)
|
|
||||||
|
|
||||||
> *
|
|
||||||
margin-right 8px
|
|
||||||
white-space nowrap
|
|
||||||
|
|
||||||
> .files
|
> .files
|
||||||
margin 0
|
margin 0
|
||||||
padding 0
|
padding 0
|
||||||
@@ -601,6 +619,27 @@ export default Vue.extend({
|
|||||||
border-radius 0 0 4px 4px
|
border-radius 0 0 4px 4px
|
||||||
transition border-color .3s ease
|
transition border-color .3s ease
|
||||||
|
|
||||||
|
> .visibleUsers
|
||||||
|
margin-bottom 8px
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
|
> span
|
||||||
|
margin-right 16px
|
||||||
|
color var(--primary)
|
||||||
|
|
||||||
|
> .hashtags
|
||||||
|
margin 0 0 8px 0
|
||||||
|
overflow hidden
|
||||||
|
white-space nowrap
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
|
> b
|
||||||
|
color var(--primary)
|
||||||
|
|
||||||
|
> *
|
||||||
|
margin-right 8px
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
> .mk-uploader
|
> .mk-uploader
|
||||||
margin 8px 0 0 0
|
margin 8px 0 0 0
|
||||||
padding 8px
|
padding 8px
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<mk-window ref="window" is-modal width="700px" height="550px" @closed="destroyDom">
|
<mk-window ref="window" is-modal width="700px" height="550px" @closed="destroyDom">
|
||||||
<span slot="header" :class="$style.header"><fa icon="cog"/>{{ $t('settings') }}</span>
|
<span slot="header" :class="$style.header"><fa icon="cog"/>{{ $t('settings') }}</span>
|
||||||
<mk-settings :initial-page="initialPage" @done="close"/>
|
<x-settings :initial-page="initialPage" @done="close"/>
|
||||||
</mk-window>
|
</mk-window>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('desktop/views/components/settings-window.vue'),
|
i18n: i18n('desktop/views/components/settings-window.vue'),
|
||||||
|
|
||||||
|
components: {
|
||||||
|
XSettings: () => import('./settings.vue').then(m => m.default)
|
||||||
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
initialPage: {
|
initialPage: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pages">
|
<div class="pages">
|
||||||
<div class="profile" v-show="page == 'profile'">
|
<div class="profile" v-show="page == 'profile'">
|
||||||
<mk-profile-editor/>
|
<x-profile-editor/>
|
||||||
|
|
||||||
<ui-card>
|
<ui-card>
|
||||||
<div slot="title"><fa :icon="['fab', 'twitter']"/> {{ $t('twitter') }}</div>
|
<div slot="title"><fa :icon="['fab', 'twitter']"/> {{ $t('twitter') }}</div>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<div slot="title"><fa icon="palette"/> {{ $t('theme') }}</div>
|
<div slot="title"><fa icon="palette"/> {{ $t('theme') }}</div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<mk-theme/>
|
<x-theme/>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@
|
|||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
<div class="drive" v-if="page == 'drive'">
|
<div class="drive" v-if="page == 'drive'">
|
||||||
<mk-drive-settings/>
|
<x-drive-settings/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ui-card class="hashtags" v-show="page == 'hashtags'">
|
<ui-card class="hashtags" v-show="page == 'hashtags'">
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
<div class="muteAndBlock" v-show="page == 'muteAndBlock'">
|
<div class="muteAndBlock" v-show="page == 'muteAndBlock'">
|
||||||
<mk-mute-and-block/>
|
<x-mute-and-block/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ui-card class="apps" v-show="page == 'apps'">
|
<ui-card class="apps" v-show="page == 'apps'">
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
<ui-card class="password" v-show="page == 'security'">
|
<ui-card class="password" v-show="page == 'security'">
|
||||||
<div slot="title"><fa icon="unlock-alt"/> {{ $t('password') }}</div>
|
<div slot="title"><fa icon="unlock-alt"/> {{ $t('password') }}</div>
|
||||||
<section>
|
<section>
|
||||||
<mk-password-settings/>
|
<x-password-settings/>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
@@ -237,7 +237,7 @@
|
|||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
<div class="api" v-show="page == 'api'">
|
<div class="api" v-show="page == 'api'">
|
||||||
<mk-api-settings/>
|
<x-api-settings/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ui-card class="other" v-show="page == 'other'">
|
<ui-card class="other" v-show="page == 'other'">
|
||||||
@@ -301,7 +301,13 @@ export default Vue.extend({
|
|||||||
X2fa,
|
X2fa,
|
||||||
XApps,
|
XApps,
|
||||||
XSignins,
|
XSignins,
|
||||||
XTags
|
XTags,
|
||||||
|
XTheme: () => import('../../../common/views/components/theme.vue').then(m => m.default),
|
||||||
|
XDriveSettings: () => import('../../../common/views/components/drive-settings.vue').then(m => m.default),
|
||||||
|
XMuteAndBlock: () => import('../../../common/views/components/mute-and-block.vue').then(m => m.default),
|
||||||
|
XPasswordSettings: () => import('../../../common/views/components/password-settings.vue').then(m => m.default),
|
||||||
|
XProfileEditor: () => import('../../../common/views/components/profile-editor.vue').then(m => m.default),
|
||||||
|
XApiSettings: () => import('../../../common/views/components/api-settings.vue').then(m => m.default),
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
initialPage: {
|
initialPage: {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
|
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
|
||||||
<div class="banner" :style="bannerStyle"></div>
|
<div class="banner" :style="bannerStyle"></div>
|
||||||
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
||||||
<mk-follow-button :user="user" class="follow"/>
|
<mk-follow-button :user="user" class="follow" mini/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
|
<router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
|
||||||
<span class="username">@{{ user | acct }}</span>
|
<span class="username">@{{ user | acct }}</span>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<p>{{ $t('followers') }}</p><span>{{ u.followersCount }}</span>
|
<p>{{ $t('followers') }}</p><span>{{ u.followersCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mk-follow-button v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u"/>
|
<mk-follow-button class="follow-button" v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u" mini/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -154,7 +154,7 @@ export default Vue.extend({
|
|||||||
font-size 1em
|
font-size 1em
|
||||||
color var(--primary)
|
color var(--primary)
|
||||||
|
|
||||||
> .mk-follow-button
|
> .follow-button
|
||||||
position absolute
|
position absolute
|
||||||
top 92px
|
top 92px
|
||||||
right 8px
|
right 8px
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<footer v-if="more">
|
<footer v-if="more">
|
||||||
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||||
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
|
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
|
||||||
<template v-if="moreFetching"><fa icon="spinner .pulse" fixed-width/></template>
|
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</component>
|
</component>
|
||||||
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
||||||
<template v-if="fetchingMoreNotifications"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreNotifications ? this.$t('@.loading') : this.$t('@.load-more') }}
|
<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreNotifications ? this.$t('@.loading') : this.$t('@.load-more') }}
|
||||||
</button>
|
</button>
|
||||||
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
|
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<header :style="bannerStyle">
|
<header :style="bannerStyle">
|
||||||
<div>
|
<div>
|
||||||
<button class="menu" @click="menu" ref="menu"><fa icon="ellipsis-h"/></button>
|
<button class="menu" @click="menu" ref="menu"><fa icon="ellipsis-h"/></button>
|
||||||
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow"/>
|
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow" mini/>
|
||||||
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
||||||
<span class="name">{{ user | userName }}</span>
|
<span class="name">{{ user | userName }}</span>
|
||||||
<span class="acct">@{{ user | acct }}</span>
|
<span class="acct">@{{ user | acct }}</span>
|
||||||
@@ -155,7 +155,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
fileType: image,
|
fileType: image,
|
||||||
limit: 9
|
limit: 9,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
notes.forEach(note => {
|
notes.forEach(note => {
|
||||||
note.files.forEach(file => {
|
note.files.forEach(file => {
|
||||||
@@ -254,6 +255,7 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365,
|
||||||
withFiles: this.withFiles,
|
withFiles: this.withFiles,
|
||||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||||
@@ -274,7 +276,7 @@ export default Vue.extend({
|
|||||||
const promise = this.$root.api('users/notes', {
|
const promise = this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
untilId: (this.$refs.timeline as any).tail().id,
|
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime(),
|
||||||
withFiles: this.withFiles,
|
withFiles: this.withFiles,
|
||||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vahgrswmbzfdlmomxnqftuueyvwaafth">
|
<div class="vahgrswmbzfdlmomxnqftuueyvwaafth">
|
||||||
<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
|
<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
|
||||||
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
|
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
|
||||||
<div v-if="!fetching && users.length > 0">
|
<div v-if="!fetching && users.length > 0">
|
||||||
<router-link v-for="user in users" :to="user | userPage" :key="user.id">
|
<router-link v-for="user in users" :to="user | userPage" :key="user.id">
|
||||||
<img :src="user.avatarUrl" :alt="user | userName" v-user-preview="user.id"/>
|
<img :src="user.avatarUrl" :alt="user | userName" v-user-preview="user.id"/>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="hozptpaliadatkehcmcayizwzwwctpbc">
|
<div class="hozptpaliadatkehcmcayizwzwwctpbc">
|
||||||
<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
|
<p class="title"><fa icon="users"/>{{ $t('title') }}</p>
|
||||||
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
|
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
|
||||||
<template v-if="!fetching && users.length != 0">
|
<template v-if="!fetching && users.length != 0">
|
||||||
<div class="user" v-for="friend in users">
|
<div class="user" v-for="friend in users">
|
||||||
<mk-avatar class="avatar" :user="friend"/>
|
<mk-avatar class="avatar" :user="friend"/>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<router-link class="name" :to="friend | userPage" v-user-preview="friend.id">{{ friend.name }}</router-link>
|
<router-link class="name" :to="friend | userPage" v-user-preview="friend.id">{{ friend.name }}</router-link>
|
||||||
<p class="username">@{{ friend | acct }}</p>
|
<p class="username">@{{ friend | acct }}</p>
|
||||||
</div>
|
</div>
|
||||||
<mk-follow-button :user="friend"/>
|
<mk-follow-button class="follow-button" :user="friend"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p>
|
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p>
|
||||||
@@ -110,7 +110,7 @@ export default Vue.extend({
|
|||||||
color var(--text)
|
color var(--text)
|
||||||
opacity 0.7
|
opacity 0.7
|
||||||
|
|
||||||
> .mk-follow-button
|
> .follow-button
|
||||||
position absolute
|
position absolute
|
||||||
top 16px
|
top 16px
|
||||||
right 16px
|
right 16px
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dzsuvbsrrrwobdxifudxuefculdfiaxd">
|
<div class="dzsuvbsrrrwobdxifudxuefculdfiaxd">
|
||||||
<p class="title"><fa icon="camera"/>{{ $t('title') }}</p>
|
<p class="title"><fa icon="camera"/>{{ $t('title') }}</p>
|
||||||
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
|
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('loading') }}<mk-ellipsis/></p>
|
||||||
<div class="stream" v-if="!fetching && images.length > 0">
|
<div class="stream" v-if="!fetching && images.length > 0">
|
||||||
<div v-for="image in images" class="img"
|
<div v-for="image in images" class="img"
|
||||||
:style="`background-image: url(${image.thumbnailUrl})`"
|
:style="`background-image: url(${image.thumbnailUrl})`"
|
||||||
@@ -27,7 +27,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: true,
|
withFiles: true,
|
||||||
limit: 9
|
limit: 9,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
notes.forEach(note => {
|
notes.forEach(note => {
|
||||||
note.files.forEach(file => {
|
note.files.forEach(file => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="profile" v-if="$store.getters.isSignedIn">
|
<div class="profile" v-if="$store.getters.isSignedIn">
|
||||||
<div class="friend-form" v-if="$store.state.i.id != user.id">
|
<div class="friend-form" v-if="$store.state.i.id != user.id">
|
||||||
<mk-follow-button :user="user" size="big"/>
|
<mk-follow-button :user="user" block/>
|
||||||
<p class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</p>
|
<p class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</p>
|
||||||
<p class="stalk" v-if="user.isFollowing">
|
<p class="stalk" v-if="user.isFollowing">
|
||||||
<span v-if="user.isStalking">{{ $t('stalking') }} <a @click="unstalk"><fa icon="meh"/> {{ $t('unstalk') }}</a></span>
|
<span v-if="user.isStalking">{{ $t('stalking') }} <a @click="unstalk"><fa icon="meh"/> {{ $t('unstalk') }}</a></span>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="action-form">
|
<div class="action-form">
|
||||||
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
|
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
|
||||||
<span v-if="user.isMuted"><fa icon="eye"/> {{ $t('unmute') }}</span>
|
<span v-if="user.isMuted"><fa icon="eye"/> {{ $t('unmute') }}</span>
|
||||||
<span v-else><fa icon="eye-slash"/> {{ $t('mute') }}</span>
|
<span v-else><fa :icon="['far', 'eye-slash']"/> {{ $t('mute') }}</span>
|
||||||
</ui-button>
|
</ui-button>
|
||||||
<ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
|
<ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
|
||||||
<span v-if="user.isBlocking"><fa icon="user"/> {{ $t('unblock') }}</span>
|
<span v-if="user.isBlocking"><fa icon="user"/> {{ $t('unblock') }}</span>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<header>
|
<header>
|
||||||
<span :data-active="mode == 'default'" @click="mode = 'default'"><fa :icon="['far', 'comment-alt']"/> {{ $t('default') }}</span>
|
<span :data-active="mode == 'default'" @click="mode = 'default'"><fa :icon="['far', 'comment-alt']"/> {{ $t('default') }}</span>
|
||||||
<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'"><fa icon="comments"/> {{ $t('with-replies') }}</span>
|
<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'"><fa icon="comments"/> {{ $t('with-replies') }}</span>
|
||||||
<span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa icon="images"/> {{ $t('with-media') }}</span>
|
<span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa :icon="['far', 'images']"/> {{ $t('with-media') }}</span>
|
||||||
</header>
|
</header>
|
||||||
<mk-notes ref="timeline" :more="existMore ? more : null">
|
<mk-notes ref="timeline" :more="existMore ? more : null">
|
||||||
<p class="empty" slot="empty"><fa :icon="['far', 'comments']"/>{{ $t('empty') }}</p>
|
<p class="empty" slot="empty"><fa :icon="['far', 'comments']"/>{{ $t('empty') }}</p>
|
||||||
@@ -63,7 +63,7 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
untilDate: this.date ? this.date.getTime() : undefined,
|
untilDate: this.date ? this.date.getTime() : new Date().getTime() + 1000 * 86400 * 365,
|
||||||
includeReplies: this.mode == 'with-replies',
|
includeReplies: this.mode == 'with-replies',
|
||||||
withFiles: this.mode == 'with-media'
|
withFiles: this.mode == 'with-media'
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
@@ -86,7 +86,7 @@ export default Vue.extend({
|
|||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
includeReplies: this.mode == 'with-replies',
|
includeReplies: this.mode == 'with-replies',
|
||||||
withFiles: this.mode == 'with-media',
|
withFiles: this.mode == 'with-media',
|
||||||
untilId: (this.$refs.timeline as any).tail().id
|
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime()
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.then(notes => {
|
promise.then(notes => {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="photos block">
|
<div class="photos block">
|
||||||
<header><fa icon="images"/> {{ $t('photos') }}</header>
|
<header><fa :icon="['far', 'images']"/> {{ $t('photos') }}</header>
|
||||||
<div>
|
<div>
|
||||||
<div v-for="photo in photos" :style="`background-image: url(${photo.thumbnailUrl})`"></div>
|
<div v-for="photo in photos" :style="`background-image: url(${photo.thumbnailUrl})`"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<mk-poll :note="poll"/>
|
<mk-poll :note="poll"/>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="!fetching && poll == null">{{ $t('nothing') }}</p>
|
<p class="empty" v-if="!fetching && poll == null">{{ $t('nothing') }}</p>
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
</div>
|
</div>
|
||||||
</mk-widget-container>
|
</mk-widget-container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mkw-post-form">
|
<div>
|
||||||
<template v-if="props.design == 0">
|
<mk-widget-container :show-header="props.design == 0">
|
||||||
<p class="title"><fa icon="pencil-alt"/>{{ $t('title') }}</p>
|
<template slot="header"><fa icon="pencil-alt"/>{{ $t('title') }}</template>
|
||||||
</template>
|
|
||||||
<textarea :disabled="posting" v-model="text" @keydown="onKeydown" :placeholder="placeholder"></textarea>
|
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body"
|
||||||
<button @click="post" :disabled="posting">{{ $t('note') }}</button>
|
@dragover.stop="onDragover"
|
||||||
|
@drop.stop="onDrop"
|
||||||
|
>
|
||||||
|
<div class="textarea">
|
||||||
|
<textarea
|
||||||
|
:disabled="posting"
|
||||||
|
v-model="text"
|
||||||
|
@keydown="onKeydown"
|
||||||
|
@paste="onPaste"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
ref="text"
|
||||||
|
v-autocomplete="'text'"
|
||||||
|
></textarea>
|
||||||
|
<button class="emoji" @click="emoji" ref="emoji">
|
||||||
|
<fa :icon="['far', 'laugh']"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="files" v-show="files.length != 0">
|
||||||
|
<x-draggable :list="files" :options="{ animation: 150 }">
|
||||||
|
<div v-for="file in files" :key="file.id">
|
||||||
|
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
|
||||||
|
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
|
||||||
|
</div>
|
||||||
|
</x-draggable>
|
||||||
|
</div>
|
||||||
|
<input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
|
||||||
|
<mk-uploader ref="uploader" @uploaded="attachMedia"/>
|
||||||
|
<footer>
|
||||||
|
<button @click="chooseFile"><fa icon="upload"/></button>
|
||||||
|
<button @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
||||||
|
<button @click="post" :disabled="posting" class="post">{{ $t('note') }}</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</mk-widget-container>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import define from '../../../common/define-widget';
|
import define from '../../../common/define-widget';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
|
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||||
|
import * as XDraggable from 'vuedraggable';
|
||||||
|
|
||||||
export default define({
|
export default define({
|
||||||
name: 'post-form',
|
name: 'post-form',
|
||||||
@@ -19,12 +54,19 @@ export default define({
|
|||||||
})
|
})
|
||||||
}).extend({
|
}).extend({
|
||||||
i18n: i18n('desktop/views/widgets/post-form.vue'),
|
i18n: i18n('desktop/views/widgets/post-form.vue'),
|
||||||
|
|
||||||
|
components: {
|
||||||
|
XDraggable
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
posting: false,
|
posting: false,
|
||||||
text: ''
|
text: '',
|
||||||
|
files: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
placeholder(): string {
|
placeholder(): string {
|
||||||
const xs = [
|
const xs = [
|
||||||
@@ -38,6 +80,7 @@ export default define({
|
|||||||
return xs[Math.floor(Math.random() * xs.length)];
|
return xs[Math.floor(Math.random() * xs.length)];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
func() {
|
func() {
|
||||||
if (this.props.design == 1) {
|
if (this.props.design == 1) {
|
||||||
@@ -47,14 +90,95 @@ export default define({
|
|||||||
}
|
}
|
||||||
this.save();
|
this.save();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
chooseFile() {
|
||||||
|
(this.$refs.file as any).click();
|
||||||
|
},
|
||||||
|
|
||||||
|
chooseFileFromDrive() {
|
||||||
|
this.$chooseDriveFile({
|
||||||
|
multiple: true
|
||||||
|
}).then(files => {
|
||||||
|
files.forEach(this.attachMedia);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
attachMedia(driveFile) {
|
||||||
|
this.files.push(driveFile);
|
||||||
|
this.$emit('change-attached-files', this.files);
|
||||||
|
},
|
||||||
|
|
||||||
|
detachMedia(id) {
|
||||||
|
this.files = this.files.filter(x => x.id != id);
|
||||||
|
this.$emit('change-attached-files', this.files);
|
||||||
|
},
|
||||||
|
|
||||||
onKeydown(e) {
|
onKeydown(e) {
|
||||||
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && !this.posting && this.text) this.post();
|
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && !this.posting && this.text) this.post();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onPaste(e) {
|
||||||
|
Array.from(e.clipboardData.items).forEach((item: any) => {
|
||||||
|
if (item.kind == 'file') {
|
||||||
|
this.upload(item.getAsFile());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeFile() {
|
||||||
|
Array.from((this.$refs.file as any).files).forEach(this.upload);
|
||||||
|
},
|
||||||
|
|
||||||
|
upload(file) {
|
||||||
|
(this.$refs.uploader as any).upload(file);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragover(e) {
|
||||||
|
const isFile = e.dataTransfer.items[0].kind == 'file';
|
||||||
|
const isDriveFile = e.dataTransfer.types[0] == 'mk_drive_file';
|
||||||
|
if (isFile || isDriveFile) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onDrop(e): void {
|
||||||
|
// ファイルだったら
|
||||||
|
if (e.dataTransfer.files.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
Array.from(e.dataTransfer.files).forEach(this.upload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#region ドライブのファイル
|
||||||
|
const driveFile = e.dataTransfer.getData('mk_drive_file');
|
||||||
|
if (driveFile != null && driveFile != '') {
|
||||||
|
const file = JSON.parse(driveFile);
|
||||||
|
this.files.push(file);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
},
|
||||||
|
|
||||||
|
async emoji() {
|
||||||
|
const Picker = await import('../components/emoji-picker-dialog.vue').then(m => m.default);
|
||||||
|
const button = this.$refs.emoji;
|
||||||
|
const rect = button.getBoundingClientRect();
|
||||||
|
const vm = this.$root.new(Picker, {
|
||||||
|
x: button.offsetWidth + rect.left + window.pageXOffset,
|
||||||
|
y: rect.top + window.pageYOffset
|
||||||
|
});
|
||||||
|
vm.$once('chosen', emoji => {
|
||||||
|
insertTextAtCursor(this.$refs.text, emoji);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
post() {
|
post() {
|
||||||
this.posting = true;
|
this.posting = true;
|
||||||
|
|
||||||
this.$root.api('notes/create', {
|
this.$root.api('notes/create', {
|
||||||
text: this.text
|
text: this.text == '' ? undefined : this.text,
|
||||||
|
fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
this.clear();
|
this.clear();
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -63,34 +187,34 @@ export default define({
|
|||||||
this.posting = false;
|
this.posting = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.text = '';
|
this.text = '';
|
||||||
|
this.files = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
.lhcuptdmcdkfwmipgazeawoiuxpzaclc-body
|
||||||
|
> .textarea
|
||||||
|
> .emoji
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
padding 10px
|
||||||
|
font-size 18px
|
||||||
|
color var(--text)
|
||||||
|
opacity 0.5
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
opacity 1
|
||||||
|
|
||||||
.mkw-post-form
|
&:active
|
||||||
background #fff
|
color var(--primary)
|
||||||
overflow hidden
|
opacity 1
|
||||||
border solid 1px rgba(#000, 0.075)
|
|
||||||
border-radius 6px
|
|
||||||
|
|
||||||
> .title
|
|
||||||
z-index 1
|
|
||||||
margin 0
|
|
||||||
padding 0 16px
|
|
||||||
line-height 42px
|
|
||||||
font-size 0.9em
|
|
||||||
font-weight bold
|
|
||||||
color #888
|
|
||||||
box-shadow 0 1px rgba(#000, 0.07)
|
|
||||||
|
|
||||||
> [data-icon]
|
|
||||||
margin-right 4px
|
|
||||||
|
|
||||||
> textarea
|
> textarea
|
||||||
display block
|
display block
|
||||||
@@ -98,16 +222,64 @@ export default define({
|
|||||||
max-width 100%
|
max-width 100%
|
||||||
min-width 100%
|
min-width 100%
|
||||||
padding 16px
|
padding 16px
|
||||||
margin-bottom 28px + 16px
|
color var(--desktopPostFormTextareaFg)
|
||||||
|
outline none
|
||||||
|
background var(--desktopPostFormTextareaBg)
|
||||||
border none
|
border none
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px var(--faceDivider)
|
||||||
|
|
||||||
> button
|
&:focus
|
||||||
|
& + .emoji
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
|
> .files
|
||||||
|
> div
|
||||||
|
padding 4px
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
display block
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
> div
|
||||||
|
float left
|
||||||
|
border solid 4px transparent
|
||||||
|
cursor move
|
||||||
|
|
||||||
|
&:hover > .remove
|
||||||
|
display block
|
||||||
|
|
||||||
|
> .img
|
||||||
|
width 64px
|
||||||
|
height 64px
|
||||||
|
background-size cover
|
||||||
|
background-position center center
|
||||||
|
|
||||||
|
> .remove
|
||||||
|
display none
|
||||||
position absolute
|
position absolute
|
||||||
bottom 8px
|
top -6px
|
||||||
right 8px
|
right -6px
|
||||||
margin 0
|
width 16px
|
||||||
|
height 16px
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
> input[type=file]
|
||||||
|
display none
|
||||||
|
|
||||||
|
> footer
|
||||||
|
display flex
|
||||||
|
padding 8px
|
||||||
|
|
||||||
|
> button:not(.post)
|
||||||
|
color var(--text)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
|
||||||
|
> .post
|
||||||
|
display block
|
||||||
|
margin 0 0 0 auto
|
||||||
padding 0 10px
|
padding 0 10px
|
||||||
height 28px
|
height 28px
|
||||||
color var(--primaryForeground)
|
color var(--primaryForeground)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<button slot="func" :title="$t('title')" @click="fetch"><fa icon="sync"/></button>
|
<button slot="func" :title="$t('title')" @click="fetch"><fa icon="sync"/></button>
|
||||||
|
|
||||||
<div class="mkw-trends--body">
|
<div class="mkw-trends--body">
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div class="note" v-else-if="note != null">
|
<div class="note" v-else-if="note != null">
|
||||||
<p class="text"><router-link :to="note | notePage">{{ note.text }}</router-link></p>
|
<p class="text"><router-link :to="note | notePage">{{ note.text }}</router-link></p>
|
||||||
<p class="author">―<router-link :to="note.user | userPage">@{{ note.user | acct }}</router-link></p>
|
<p class="author">―<router-link :to="note.user | userPage">@{{ note.user | acct }}</router-link></p>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<button slot="func" :title="$t('title')" @click="refresh"><fa icon="sync"/></button>
|
<button slot="func" :title="$t('title')" @click="refresh"><fa icon="sync"/></button>
|
||||||
|
|
||||||
<div class="mkw-users--body">
|
<div class="mkw-users--body">
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<template v-else-if="users.length != 0">
|
<template v-else-if="users.length != 0">
|
||||||
<div class="user" v-for="_user in users">
|
<div class="user" v-for="_user in users">
|
||||||
<mk-avatar class="avatar" :user="_user"/>
|
<mk-avatar class="avatar" :user="_user"/>
|
||||||
@@ -114,11 +114,6 @@ export default define({
|
|||||||
color var(--text)
|
color var(--text)
|
||||||
opacity 0.7
|
opacity 0.7
|
||||||
|
|
||||||
> .mk-follow-button
|
|
||||||
position absolute
|
|
||||||
top 16px
|
|
||||||
right 16px
|
|
||||||
|
|
||||||
> .empty
|
> .empty
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
|
|||||||
@@ -25,47 +25,120 @@ if (localStorage.getItem('theme') == null) {
|
|||||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
|
|
||||||
/* なぜか動かない
|
import {
|
||||||
import faRetweet from '@fortawesome/free-solid-svg-icons/faRetweet';
|
faRetweet,
|
||||||
import faPlus from '@fortawesome/free-solid-svg-icons/faPlus';
|
faPlus,
|
||||||
import faUser from '@fortawesome/free-solid-svg-icons/faUser';
|
faUser,
|
||||||
import faCog from '@fortawesome/free-solid-svg-icons/faCog';
|
faCog,
|
||||||
import faCheck from '@fortawesome/free-solid-svg-icons/faCheck';
|
faCheck,
|
||||||
import faStar from '@fortawesome/free-solid-svg-icons/faStar';
|
faStar,
|
||||||
import faReply from '@fortawesome/free-solid-svg-icons/faReply';
|
faReply,
|
||||||
import faEllipsisH from '@fortawesome/free-solid-svg-icons/faEllipsisH';
|
faEllipsisH,
|
||||||
import faQuoteLeft from '@fortawesome/free-solid-svg-icons/faQuoteLeft';
|
faQuoteLeft,
|
||||||
import faQuoteRight from '@fortawesome/free-solid-svg-icons/faQuoteRight';
|
faQuoteRight,
|
||||||
import faAngleUp from '@fortawesome/free-solid-svg-icons/faAngleUp';
|
faAngleUp,
|
||||||
import faAngleDown from '@fortawesome/free-solid-svg-icons/faAngleDown';
|
faAngleDown,
|
||||||
import faAt from '@fortawesome/free-solid-svg-icons/faAt';
|
faAt,
|
||||||
import faHashtag from '@fortawesome/free-solid-svg-icons/faHashtag';
|
faHashtag,
|
||||||
import faHome from '@fortawesome/free-solid-svg-icons/faHome';
|
faHome,
|
||||||
import faGlobe from '@fortawesome/free-solid-svg-icons/faGlobe';
|
faGlobe,
|
||||||
import faCircle from '@fortawesome/free-solid-svg-icons/faCircle';
|
faCircle,
|
||||||
import faList from '@fortawesome/free-solid-svg-icons/faList';
|
faList,
|
||||||
import faHeart from '@fortawesome/free-solid-svg-icons/faHeart';
|
faHeart,
|
||||||
import faUnlock from '@fortawesome/free-solid-svg-icons/faUnlock';
|
faUnlock,
|
||||||
import faRssSquare from '@fortawesome/free-solid-svg-icons/faRssSquare';
|
faRssSquare,
|
||||||
import faSort from '@fortawesome/free-solid-svg-icons/faSort';
|
faSort,
|
||||||
import faChartPie from '@fortawesome/free-solid-svg-icons/faChartPie';
|
faChartPie,
|
||||||
import faChartBar from '@fortawesome/free-solid-svg-icons/faChartBar';
|
faChartBar,
|
||||||
import faPencilAlt from '@fortawesome/free-solid-svg-icons/faPencilAlt';
|
faPencilAlt,
|
||||||
import faColumns from '@fortawesome/free-solid-svg-icons/faColumns';
|
faColumns,
|
||||||
import faComments from '@fortawesome/free-solid-svg-icons/faComments';
|
faComments,
|
||||||
import faGamepad from '@fortawesome/free-solid-svg-icons/faGamepad';
|
faGamepad,
|
||||||
import faCloud from '@fortawesome/free-solid-svg-icons/faCloud';
|
faCloud,
|
||||||
import faPowerOff from '@fortawesome/free-solid-svg-icons/faPowerOff';
|
faPowerOff,
|
||||||
import faChevronCircleLeft from '@fortawesome/free-solid-svg-icons/faChevronCircleLeft';
|
faChevronCircleLeft,
|
||||||
import faChevronCircleRight from '@fortawesome/free-solid-svg-icons/faChevronCircleRight';
|
faChevronCircleRight,
|
||||||
import faShareAlt from '@fortawesome/free-solid-svg-icons/faShareAlt';
|
faShareAlt,
|
||||||
import faTimes from '@fortawesome/free-solid-svg-icons/faTimes';
|
faTimes,
|
||||||
import faThumbtack from '@fortawesome/free-solid-svg-icons/faThumbtack';
|
faThumbtack,
|
||||||
import faSearch from '@fortawesome/free-solid-svg-icons/faSearch';
|
faSearch,
|
||||||
|
faAngleRight,
|
||||||
|
faWrench,
|
||||||
|
faTerminal,
|
||||||
|
faMoon,
|
||||||
|
faPalette,
|
||||||
|
faSlidersH,
|
||||||
|
faDesktop,
|
||||||
|
faVolumeUp,
|
||||||
|
faLanguage,
|
||||||
|
faInfoCircle,
|
||||||
|
faExclamationTriangle,
|
||||||
|
faKey,
|
||||||
|
faBan,
|
||||||
|
faCogs,
|
||||||
|
faUnlockAlt,
|
||||||
|
faPuzzlePiece,
|
||||||
|
faMobileAlt,
|
||||||
|
faSignInAlt,
|
||||||
|
faSyncAlt,
|
||||||
|
faPaperPlane,
|
||||||
|
faUpload,
|
||||||
|
faMapMarkerAlt,
|
||||||
|
faEnvelope,
|
||||||
|
faLock,
|
||||||
|
faFolderOpen,
|
||||||
|
faBirthdayCake,
|
||||||
|
faImage,
|
||||||
|
faEye,
|
||||||
|
faDownload,
|
||||||
|
faFileImport,
|
||||||
|
faLink,
|
||||||
|
faArrowRight,
|
||||||
|
faICursor,
|
||||||
|
faCaretRight,
|
||||||
|
faReplyAll,
|
||||||
|
faCamera,
|
||||||
|
faMinus,
|
||||||
|
faCaretDown,
|
||||||
|
faCalculator,
|
||||||
|
faUsers,
|
||||||
|
faBars,
|
||||||
|
faFileImage,
|
||||||
|
faPollH,
|
||||||
|
faFolder,
|
||||||
|
faMicrochip,
|
||||||
|
faMemory,
|
||||||
|
faServer,
|
||||||
|
faExclamationCircle,
|
||||||
|
faSpinner,
|
||||||
|
faBroadcastTower
|
||||||
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
import farBell from '@fortawesome/free-regular-svg-icons/faBell';
|
import {
|
||||||
import farEnvelope from '@fortawesome/free-regular-svg-icons/faEnvelope';
|
faBell as farBell,
|
||||||
import farComments from '@fortawesome/free-regular-svg-icons/faComments';
|
faEnvelope as farEnvelope,
|
||||||
|
faComments as farComments,
|
||||||
|
faTrashAlt as farTrashAlt,
|
||||||
|
faWindowRestore as farWindowRestore,
|
||||||
|
faFolder as farFolder,
|
||||||
|
faLaugh as farLaugh,
|
||||||
|
faSmile as farSmile,
|
||||||
|
faEyeSlash as farEyeSlash,
|
||||||
|
faFolderOpen as farFolderOpen,
|
||||||
|
faSave as farSave,
|
||||||
|
faImages as farImages,
|
||||||
|
faChartBar as farChartBar,
|
||||||
|
faCommentAlt as farCommentAlt,
|
||||||
|
faClock as farClock,
|
||||||
|
faCalendarAlt as farCalendarAlt,
|
||||||
|
faHdd as farHdd,
|
||||||
|
} from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
faTwitter as fabTwitter,
|
||||||
|
faGithub as fabGithub,
|
||||||
|
} from '@fortawesome/free-brands-svg-icons';
|
||||||
|
import i18n from './i18n';
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faRetweet,
|
faRetweet,
|
||||||
@@ -104,16 +177,78 @@ library.add(
|
|||||||
faTimes,
|
faTimes,
|
||||||
faThumbtack,
|
faThumbtack,
|
||||||
faSearch,
|
faSearch,
|
||||||
|
faAngleRight,
|
||||||
|
faWrench,
|
||||||
|
faTerminal,
|
||||||
|
faMoon,
|
||||||
|
faPalette,
|
||||||
|
faSlidersH,
|
||||||
|
faDesktop,
|
||||||
|
faVolumeUp,
|
||||||
|
faLanguage,
|
||||||
|
faInfoCircle,
|
||||||
|
faExclamationTriangle,
|
||||||
|
faKey,
|
||||||
|
faBan,
|
||||||
|
faCogs,
|
||||||
|
faUnlockAlt,
|
||||||
|
faPuzzlePiece,
|
||||||
|
faMobileAlt,
|
||||||
|
faSignInAlt,
|
||||||
|
faSyncAlt,
|
||||||
|
faPaperPlane,
|
||||||
|
faUpload,
|
||||||
|
faMapMarkerAlt,
|
||||||
|
faEnvelope,
|
||||||
|
faLock,
|
||||||
|
faFolderOpen,
|
||||||
|
faBirthdayCake,
|
||||||
|
faImage,
|
||||||
|
faEye,
|
||||||
|
faDownload,
|
||||||
|
faFileImport,
|
||||||
|
faLink,
|
||||||
|
faArrowRight,
|
||||||
|
faICursor,
|
||||||
|
faCaretRight,
|
||||||
|
faReplyAll,
|
||||||
|
faCamera,
|
||||||
|
faMinus,
|
||||||
|
faCaretDown,
|
||||||
|
faCalculator,
|
||||||
|
faUsers,
|
||||||
|
faBars,
|
||||||
|
faFileImage,
|
||||||
|
faPollH,
|
||||||
|
faFolder,
|
||||||
|
faMicrochip,
|
||||||
|
faMemory,
|
||||||
|
faServer,
|
||||||
|
faExclamationCircle,
|
||||||
|
faSpinner,
|
||||||
|
faBroadcastTower,
|
||||||
|
|
||||||
farBell,
|
farBell,
|
||||||
farEnvelope,
|
farEnvelope,
|
||||||
farComments,
|
farComments,
|
||||||
|
farTrashAlt,
|
||||||
|
farWindowRestore,
|
||||||
|
farFolder,
|
||||||
|
farLaugh,
|
||||||
|
farSmile,
|
||||||
|
farEyeSlash,
|
||||||
|
farFolderOpen,
|
||||||
|
farSave,
|
||||||
|
farImages,
|
||||||
|
farChartBar,
|
||||||
|
farCommentAlt,
|
||||||
|
farClock,
|
||||||
|
farCalendarAlt,
|
||||||
|
farHdd,
|
||||||
|
|
||||||
|
fabTwitter,
|
||||||
|
fabGithub
|
||||||
);
|
);
|
||||||
*/
|
|
||||||
|
|
||||||
import { fas } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { far } from '@fortawesome/free-regular-svg-icons';
|
|
||||||
|
|
||||||
library.add(fas, far);
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
@@ -269,13 +404,7 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS]) => void,
|
|||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
i18n: new VueI18n({
|
i18n: i18n(),
|
||||||
sync: false,
|
|
||||||
locale: lang,
|
|
||||||
messages: {
|
|
||||||
[lang]: {}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
store: os.store,
|
store: os.store,
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import MkFollowing from './views/pages/following.vue';
|
|||||||
import MkFavorites from './views/pages/favorites.vue';
|
import MkFavorites from './views/pages/favorites.vue';
|
||||||
import MkUserLists from './views/pages/user-lists.vue';
|
import MkUserLists from './views/pages/user-lists.vue';
|
||||||
import MkUserList from './views/pages/user-list.vue';
|
import MkUserList from './views/pages/user-list.vue';
|
||||||
import MkSettings from './views/pages/settings.vue';
|
|
||||||
import MkReversi from './views/pages/games/reversi.vue';
|
import MkReversi from './views/pages/games/reversi.vue';
|
||||||
import MkTag from './views/pages/tag.vue';
|
import MkTag from './views/pages/tag.vue';
|
||||||
import MkShare from './views/pages/share.vue';
|
import MkShare from './views/pages/share.vue';
|
||||||
@@ -137,7 +136,7 @@ init((launch) => {
|
|||||||
routes: [
|
routes: [
|
||||||
{ path: '/', name: 'index', component: MkIndex },
|
{ path: '/', name: 'index', component: MkIndex },
|
||||||
{ path: '/signup', name: 'signup', component: MkSignup },
|
{ path: '/signup', name: 'signup', component: MkSignup },
|
||||||
{ path: '/i/settings', name: 'settings', component: MkSettings },
|
{ path: '/i/settings', name: 'settings', component: () => import('./views/pages/settings.vue').then(m => m.default) },
|
||||||
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
|
{ path: '/i/notifications', name: 'notifications', component: MkNotifications },
|
||||||
{ path: '/i/favorites', name: 'favorites', component: MkFavorites },
|
{ path: '/i/favorites', name: 'favorites', component: MkFavorites },
|
||||||
{ path: '/i/lists', name: 'user-lists', component: MkUserLists },
|
{ path: '/i/lists', name: 'user-lists', component: MkUserLists },
|
||||||
@@ -154,7 +153,7 @@ init((launch) => {
|
|||||||
{ path: '/tags/:tag', component: MkTag },
|
{ path: '/tags/:tag', component: MkTag },
|
||||||
{ path: '/share', component: MkShare },
|
{ path: '/share', component: MkShare },
|
||||||
{ path: '/reversi/:game?', name: 'reversi', component: MkReversi },
|
{ path: '/reversi/:game?', name: 'reversi', component: MkReversi },
|
||||||
{ path: '/@:user', component: MkUser },
|
{ path: '/@:user', component: () => import('./views/pages/user.vue').then(m => m.default) },
|
||||||
{ path: '/@:user/followers', component: MkFollowers },
|
{ path: '/@:user/followers', component: MkFollowers },
|
||||||
{ path: '/@:user/following', component: MkFollowing },
|
{ path: '/@:user/following', component: MkFollowing },
|
||||||
{ path: '/notes/:note', component: MkNote },
|
{ path: '/notes/:note', component: MkNote },
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<span class="created-at" @click="showCreatedAt"><fa :icon="['far', 'clock']"/><mk-time :time="file.createdAt"/></span>
|
<span class="created-at" @click="showCreatedAt"><fa :icon="['far', 'clock']"/><mk-time :time="file.createdAt"/></span>
|
||||||
<template v-if="file.isSensitive">
|
<template v-if="file.isSensitive">
|
||||||
<span class="separator"></span>
|
<span class="separator"></span>
|
||||||
<span class="nsfw"><fa icon="eye-slash"/> {{ $t('nsfw') }}</span>
|
<span class="nsfw"><fa :icon="['far', 'eye-slash']"/> {{ $t('nsfw') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<span class="created-at"><fa :icon="['far', 'clock']"/><mk-time :time="file.createdAt"/></span>
|
<span class="created-at"><fa :icon="['far', 'clock']"/><mk-time :time="file.createdAt"/></span>
|
||||||
<template v-if="file.isSensitive">
|
<template v-if="file.isSensitive">
|
||||||
<span class="separator"></span>
|
<span class="separator"></span>
|
||||||
<span class="nsfw"><fa icon="eye-slash"/> {{ $t('nsfw') }}</span>
|
<span class="nsfw"><fa :icon="['far', 'eye-slash']"/> {{ $t('nsfw') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ export default Vue.extend({
|
|||||||
top 0
|
top 0
|
||||||
bottom 0
|
bottom 0
|
||||||
right 20px
|
right 20px
|
||||||
|
|
||||||
> *
|
|
||||||
height 100%
|
height 100%
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
<template>
|
|
||||||
<button class="mk-follow-button"
|
|
||||||
:class="{ wait: wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou }"
|
|
||||||
@click="onClick"
|
|
||||||
:disabled="wait"
|
|
||||||
>
|
|
||||||
<template v-if="!wait">
|
|
||||||
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/> {{ $t('request-pending') }}</template>
|
|
||||||
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/> {{ $t('follow-processing') }}</template>
|
|
||||||
<template v-else-if="u.isFollowing"><fa icon="minus"/> {{ $t('following') }}</template>
|
|
||||||
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
|
|
||||||
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
|
|
||||||
</template>
|
|
||||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
import i18n from '../../../i18n';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
i18n: i18n('mobile/views/components/follow-button.vue'),
|
|
||||||
props: {
|
|
||||||
user: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
u: this.user,
|
|
||||||
wait: false,
|
|
||||||
connection: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.connection = this.$root.stream.useSharedConnection('main');
|
|
||||||
|
|
||||||
this.connection.on('follow', this.onFollowChange);
|
|
||||||
this.connection.on('unfollow', this.onFollowChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.connection.dispose();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onFollowChange(user) {
|
|
||||||
if (user.id == this.u.id) {
|
|
||||||
this.u.isFollowing = user.isFollowing;
|
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
|
||||||
this.$forceUpdate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async onClick() {
|
|
||||||
this.wait = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.u.isFollowing) {
|
|
||||||
this.u = await this.$root.api('following/delete', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (this.u.hasPendingFollowRequestFromYou) {
|
|
||||||
this.u = await this.$root.api('following/requests/cancel', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else if (this.u.isLocked) {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.user.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
} finally {
|
|
||||||
this.wait = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-follow-button
|
|
||||||
display block
|
|
||||||
user-select none
|
|
||||||
cursor pointer
|
|
||||||
padding 0 16px
|
|
||||||
margin 0
|
|
||||||
min-width 100px
|
|
||||||
line-height 36px
|
|
||||||
font-size 14px
|
|
||||||
font-weight bold
|
|
||||||
color var(--primary)
|
|
||||||
background transparent
|
|
||||||
outline none
|
|
||||||
border solid 1px var(--primary)
|
|
||||||
border-radius 36px
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background var(--primaryAlpha01)
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background var(--primaryAlpha02)
|
|
||||||
|
|
||||||
&.active
|
|
||||||
color var(--primaryForeground)
|
|
||||||
background var(--primary)
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background var(--primaryLighten10)
|
|
||||||
border-color var(--primaryLighten10)
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background var(--primaryDarken10)
|
|
||||||
border-color var(--primaryDarken10)
|
|
||||||
|
|
||||||
&.wait
|
|
||||||
cursor wait !important
|
|
||||||
opacity 0.7
|
|
||||||
|
|
||||||
*
|
|
||||||
pointer-events none
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<mk-user-card v-for="user in users" :key="user.id" :user="user"/>
|
<mk-user-card v-for="user in users" :key="user.id" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('empty') }}</p>
|
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('empty') }}</p>
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('fetching') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('fetching') }}<mk-ellipsis/></p>
|
||||||
<a class="refresh" @click="refresh">{{ $t('refresh') }}</a>
|
<a class="refresh" @click="refresh">{{ $t('refresh') }}</a>
|
||||||
<button class="close" @click="close" :title="$t('title')"><fa icon="times"/></button>
|
<button class="close" @click="close" :title="$t('title')"><fa icon="times"/></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import subNoteContent from './sub-note-content.vue';
|
|||||||
import noteCard from './note-card.vue';
|
import noteCard from './note-card.vue';
|
||||||
import userCard from './user-card.vue';
|
import userCard from './user-card.vue';
|
||||||
import noteDetail from './note-detail.vue';
|
import noteDetail from './note-detail.vue';
|
||||||
import followButton from './follow-button.vue';
|
|
||||||
import friendsMaker from './friends-maker.vue';
|
import friendsMaker from './friends-maker.vue';
|
||||||
import notification from './notification.vue';
|
import notification from './notification.vue';
|
||||||
import notifications from './notifications.vue';
|
import notifications from './notifications.vue';
|
||||||
@@ -19,7 +18,6 @@ import usersList from './users-list.vue';
|
|||||||
import userPreview from './user-preview.vue';
|
import userPreview from './user-preview.vue';
|
||||||
import userTimeline from './user-timeline.vue';
|
import userTimeline from './user-timeline.vue';
|
||||||
import userListTimeline from './user-list-timeline.vue';
|
import userListTimeline from './user-list-timeline.vue';
|
||||||
import activity from './activity.vue';
|
|
||||||
import widgetContainer from './widget-container.vue';
|
import widgetContainer from './widget-container.vue';
|
||||||
import postForm from './post-form.vue';
|
import postForm from './post-form.vue';
|
||||||
|
|
||||||
@@ -33,7 +31,6 @@ Vue.component('mk-sub-note-content', subNoteContent);
|
|||||||
Vue.component('mk-note-card', noteCard);
|
Vue.component('mk-note-card', noteCard);
|
||||||
Vue.component('mk-user-card', userCard);
|
Vue.component('mk-user-card', userCard);
|
||||||
Vue.component('mk-note-detail', noteDetail);
|
Vue.component('mk-note-detail', noteDetail);
|
||||||
Vue.component('mk-follow-button', followButton);
|
|
||||||
Vue.component('mk-friends-maker', friendsMaker);
|
Vue.component('mk-friends-maker', friendsMaker);
|
||||||
Vue.component('mk-notification', notification);
|
Vue.component('mk-notification', notification);
|
||||||
Vue.component('mk-notifications', notifications);
|
Vue.component('mk-notifications', notifications);
|
||||||
@@ -42,6 +39,5 @@ Vue.component('mk-users-list', usersList);
|
|||||||
Vue.component('mk-user-preview', userPreview);
|
Vue.component('mk-user-preview', userPreview);
|
||||||
Vue.component('mk-user-timeline', userTimeline);
|
Vue.component('mk-user-timeline', userTimeline);
|
||||||
Vue.component('mk-user-list-timeline', userListTimeline);
|
Vue.component('mk-user-list-timeline', userListTimeline);
|
||||||
Vue.component('mk-activity', activity);
|
|
||||||
Vue.component('mk-widget-container', widgetContainer);
|
Vue.component('mk-widget-container', widgetContainer);
|
||||||
Vue.component('mk-post-form', postForm);
|
Vue.component('mk-post-form', postForm);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
:disabled="conversationFetching"
|
:disabled="conversationFetching"
|
||||||
>
|
>
|
||||||
<template v-if="!conversationFetching"><fa icon="ellipsis-v"/></template>
|
<template v-if="!conversationFetching"><fa icon="ellipsis-v"/></template>
|
||||||
<template v-if="conversationFetching"><fa icon="spinner .pulse"/></template>
|
<template v-if="conversationFetching"><fa icon="spinner" pulse/></template>
|
||||||
</button>
|
</button>
|
||||||
<div class="conversation">
|
<div class="conversation">
|
||||||
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>
|
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<footer v-if="more">
|
<footer v-if="more">
|
||||||
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||||
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
|
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
|
||||||
<template v-if="moreFetching"><fa icon="spinner .pulse" fixed-width/></template>
|
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
</component>
|
</component>
|
||||||
|
|
||||||
<button class="more" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
<button class="more" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
|
||||||
<template v-if="fetchingMoreNotifications"><fa icon="spinner .pulse" fixed-width/></template>
|
<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>
|
||||||
{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
|
{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<button class="drive" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
<button class="drive" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
||||||
<button class="kao" @click="kao"><fa :icon="['far', 'smile']"/></button>
|
<button class="kao" @click="kao"><fa :icon="['far', 'smile']"/></button>
|
||||||
<button class="poll" @click="poll = true"><fa icon="chart-pie"/></button>
|
<button class="poll" @click="poll = true"><fa icon="chart-pie"/></button>
|
||||||
<button class="poll" @click="useCw = !useCw"><fa icon="eye-slash"/></button>
|
<button class="poll" @click="useCw = !useCw"><fa :icon="['far', 'eye-slash']"/></button>
|
||||||
<button class="geo" @click="geo ? removeGeo() : setGeo()"><fa icon="map-marker-alt"/></button>
|
<button class="geo" @click="geo ? removeGeo() : setGeo()"><fa icon="map-marker-alt"/></button>
|
||||||
<button class="visibility" @click="setVisibility" ref="visibilityButton">
|
<button class="visibility" @click="setVisibility" ref="visibilityButton">
|
||||||
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
|
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</header>
|
</header>
|
||||||
<a class="name" :href="user | userPage" target="_blank">{{ user | userName }}</a>
|
<a class="name" :href="user | userPage" target="_blank">{{ user | userName }}</a>
|
||||||
<p class="username"><mk-acct :user="user"/></p>
|
<p class="username"><mk-acct :user="user"/></p>
|
||||||
<mk-follow-button :user="user"/>
|
<mk-follow-button class="follow-button" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ export default Vue.extend({
|
|||||||
font-size 15px
|
font-size 15px
|
||||||
color #ccc
|
color #ccc
|
||||||
|
|
||||||
> .mk-follow-button
|
> .follow-button
|
||||||
display inline-block
|
display inline-block
|
||||||
margin 8px 0 16px 0
|
margin 8px 0 16px 0
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: this.withMedia,
|
withFiles: this.withMedia,
|
||||||
limit: fetchLimit + 1
|
limit: fetchLimit + 1,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
if (notes.length == fetchLimit + 1) {
|
if (notes.length == fetchLimit + 1) {
|
||||||
notes.pop();
|
notes.pop();
|
||||||
@@ -66,7 +67,7 @@ export default Vue.extend({
|
|||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: this.withMedia,
|
withFiles: this.withMedia,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
untilId: (this.$refs.timeline as any).tail().id
|
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime()
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.then(notes => {
|
promise.then(notes => {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<p class="no" v-if="!fetching && users.length == 0">
|
<p class="no" v-if="!fetching && users.length == 0">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</p>
|
</p>
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
<div class="signin-as" v-html="this.$t('signed-in-as').replace('{}', `<b>${name}</b>`)"></div>
|
<div class="signin-as" v-html="this.$t('signed-in-as').replace('{}', `<b>${name}</b>`)"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<mk-profile-editor/>
|
<x-profile-editor/>
|
||||||
|
|
||||||
<ui-card>
|
<ui-card>
|
||||||
<div slot="title"><fa icon="palette"/> {{ $t('theme') }}</div>
|
<div slot="title"><fa icon="palette"/> {{ $t('theme') }}</div>
|
||||||
<section>
|
<section>
|
||||||
<mk-theme/>
|
<x-theme/>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
@@ -85,9 +85,9 @@
|
|||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
<mk-drive-settings/>
|
<x-drive-settings/>
|
||||||
|
|
||||||
<mk-mute-and-block/>
|
<x-mute-and-block/>
|
||||||
|
|
||||||
<ui-card>
|
<ui-card>
|
||||||
<div slot="title"><fa icon="volume-up"/> {{ $t('sound') }}</div>
|
<div slot="title"><fa icon="volume-up"/> {{ $t('sound') }}</div>
|
||||||
@@ -140,12 +140,12 @@
|
|||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
<mk-api-settings />
|
<x-api-settings />
|
||||||
|
|
||||||
<ui-card>
|
<ui-card>
|
||||||
<div slot="title"><fa icon="unlock-alt"/> {{ $t('password') }}</div>
|
<div slot="title"><fa icon="unlock-alt"/> {{ $t('password') }}</div>
|
||||||
<section>
|
<section>
|
||||||
<mk-password-settings/>
|
<x-password-settings/>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
@@ -182,6 +182,16 @@ import checkForUpdate from '../../../common/scripts/check-for-update';
|
|||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('mobile/views/pages/settings.vue'),
|
i18n: i18n('mobile/views/pages/settings.vue'),
|
||||||
|
|
||||||
|
components: {
|
||||||
|
XTheme: () => import('../../../common/views/components/theme.vue').then(m => m.default),
|
||||||
|
XDriveSettings: () => import('../../../common/views/components/drive-settings.vue').then(m => m.default),
|
||||||
|
XMuteAndBlock: () => import('../../../common/views/components/mute-and-block.vue').then(m => m.default),
|
||||||
|
XPasswordSettings: () => import('../../../common/views/components/password-settings.vue').then(m => m.default),
|
||||||
|
XProfileEditor: () => import('../../../common/views/components/profile-editor.vue').then(m => m.default),
|
||||||
|
XApiSettings: () => import('../../../common/views/components/api-settings.vue').then(m => m.default),
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
apiUrl,
|
apiUrl,
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
menu() {
|
menu() {
|
||||||
let menu = [{
|
let menu = [{
|
||||||
icon: this.user.isMuted ? '<fa icon="eye"/>' : '<fa icon="eye-slash"/>',
|
icon: this.user.isMuted ? ['fas', 'eye'] : ['far', 'eye-slash'],
|
||||||
text: this.user.isMuted ? this.$t('unmute') : this.$t('mute'),
|
text: this.user.isMuted ? this.$t('unmute') : this.$t('mute'),
|
||||||
action: () => {
|
action: () => {
|
||||||
if (this.user.isMuted) {
|
if (this.user.isMuted) {
|
||||||
@@ -138,7 +138,7 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
icon: this.user.isBlocking ? '<fa icon="user"/>' : '<fa icon="user-slash"/>',
|
icon: this.user.isBlocking ? ['fas', 'user'] : ['fas', 'user-slash'],
|
||||||
text: this.user.isBlocking ? this.$t('unblock') : this.$t('block'),
|
text: this.user.isBlocking ? this.$t('unblock') : this.$t('block'),
|
||||||
action: () => {
|
action: () => {
|
||||||
if (this.user.isBlocking) {
|
if (this.user.isBlocking) {
|
||||||
@@ -243,9 +243,6 @@ main
|
|||||||
font-size 18px
|
font-size 18px
|
||||||
color var(--text)
|
color var(--text)
|
||||||
|
|
||||||
> .mk-follow-button
|
|
||||||
margin 0
|
|
||||||
|
|
||||||
> .title
|
> .title
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root followers-you-know">
|
<div class="root followers-you-know">
|
||||||
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div v-if="!fetching && users.length > 0">
|
<div v-if="!fetching && users.length > 0">
|
||||||
<a v-for="user in users" :key="user.id" :href="user | userPage">
|
<a v-for="user in users" :key="user.id" :href="user | userPage">
|
||||||
<img :src="user.avatarUrl" :alt="user | userName"/>
|
<img :src="user.avatarUrl" :alt="user | userName"/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root friends">
|
<div class="root friends">
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div v-if="!fetching && users.length > 0">
|
<div v-if="!fetching && users.length > 0">
|
||||||
<mk-user-card v-for="user in users" :key="user.id" :user="user"/>
|
<mk-user-card v-for="user in users" :key="user.id" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root notes">
|
<div class="root notes">
|
||||||
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div v-if="!fetching && notes.length > 0">
|
<div v-if="!fetching && notes.length > 0">
|
||||||
<mk-note-card v-for="note in notes" :key="note.id" :note="note"/>
|
<mk-note-card v-for="note in notes" :key="note.id" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,7 +22,8 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id
|
userId: this.user.id,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root photos">
|
<div class="root photos">
|
||||||
<p class="initializing" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p class="initializing" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div class="stream" v-if="!fetching && images.length > 0">
|
<div class="stream" v-if="!fetching && images.length > 0">
|
||||||
<a v-for="image in images"
|
<a v-for="image in images"
|
||||||
class="img"
|
class="img"
|
||||||
@@ -29,7 +29,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: true,
|
withFiles: true,
|
||||||
limit: 6
|
limit: 6,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
notes.forEach(note => {
|
notes.forEach(note => {
|
||||||
note.media.forEach(media => {
|
note.media.forEach(media => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<section class="activity">
|
<section class="activity">
|
||||||
<h2><fa icon="chart-bar"/>{{ $t('activity') }}</h2>
|
<h2><fa icon="chart-bar"/>{{ $t('activity') }}</h2>
|
||||||
<div>
|
<div>
|
||||||
<mk-activity :user="user"/>
|
<x-activity :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="frequently-replied-users">
|
<section class="frequently-replied-users">
|
||||||
@@ -49,7 +49,8 @@ export default Vue.extend({
|
|||||||
XNotes,
|
XNotes,
|
||||||
XPhotos,
|
XPhotos,
|
||||||
XFriends,
|
XFriends,
|
||||||
XFollowersYouKnow
|
XFollowersYouKnow,
|
||||||
|
XActivity: () => import('../../components/activity.vue').then(m => m.default)
|
||||||
},
|
},
|
||||||
props: ['user']
|
props: ['user']
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<mk-widget-container :show-header="!props.compact">
|
<mk-widget-container :show-header="!props.compact">
|
||||||
<template slot="header"><fa icon="chart-bar"/>{{ $t('activity') }}</template>
|
<template slot="header"><fa icon="chart-bar"/>{{ $t('activity') }}</template>
|
||||||
<div :class="$style.body">
|
<div :class="$style.body">
|
||||||
<mk-activity :user="$store.state.i"/>
|
<x-activity :user="$store.state.i"/>
|
||||||
</div>
|
</div>
|
||||||
</mk-widget-container>
|
</mk-widget-container>
|
||||||
</div>
|
</div>
|
||||||
@@ -20,6 +20,9 @@ export default define({
|
|||||||
})
|
})
|
||||||
}).extend({
|
}).extend({
|
||||||
i18n: i18n(),
|
i18n: i18n(),
|
||||||
|
components: {
|
||||||
|
XActivity: () => import('../components/activity.vue').then(m => m.default)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
func() {
|
func() {
|
||||||
this.props.compact = !this.props.compact;
|
this.props.compact = !this.props.compact;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
secondary: '$secondary',
|
secondary: '$secondary',
|
||||||
bg: ':darken<8<$secondary',
|
bg: ':darken<8<$secondary',
|
||||||
text: '$text',
|
text: '$text',
|
||||||
|
textHighlighted: ':lighten<7<$text',
|
||||||
|
|
||||||
scrollbarTrack: ':darken<5<$secondary',
|
scrollbarTrack: ':darken<5<$secondary',
|
||||||
scrollbarHandle: ':lighten<5<$secondary',
|
scrollbarHandle: ':lighten<5<$secondary',
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
secondary: '$secondary',
|
secondary: '$secondary',
|
||||||
bg: ':darken<8<$secondary',
|
bg: ':darken<8<$secondary',
|
||||||
text: '$text',
|
text: '$text',
|
||||||
|
textHighlighted: ':darken<7<$text',
|
||||||
|
|
||||||
scrollbarTrack: '#fff',
|
scrollbarTrack: '#fff',
|
||||||
scrollbarHandle: '#00000033',
|
scrollbarHandle: '#00000033',
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
|
|||||||
console.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`);
|
console.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name == 'INVALID_PARAM') {
|
if (e && e.name == 'INVALID_PARAM') {
|
||||||
rej({
|
rej({
|
||||||
code: e.name,
|
code: e.name,
|
||||||
param: e.param,
|
param: e.param,
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 投稿を作成
|
// 投稿を作成
|
||||||
const note = await create(user, {
|
create(user, {
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
files: files,
|
files: files,
|
||||||
poll: ps.poll,
|
poll: ps.poll,
|
||||||
@@ -229,12 +229,14 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
|
|||||||
visibility: ps.visibility,
|
visibility: ps.visibility,
|
||||||
visibleUsers,
|
visibleUsers,
|
||||||
geo: ps.geo
|
geo: ps.geo
|
||||||
});
|
})
|
||||||
|
.then(note => pack(note, user))
|
||||||
const noteObj = await pack(note, user);
|
.then(noteObj => {
|
||||||
|
|
||||||
// Reponse
|
|
||||||
res({
|
res({
|
||||||
createdNote: noteObj
|
createdNote: noteObj
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
rej(e);
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -153,9 +153,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#region Construct query
|
//#region Construct query
|
||||||
const sort = {
|
const sort = { } as any;
|
||||||
_id: -1
|
|
||||||
};
|
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
@@ -168,15 +166,17 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
|||||||
$gt: ps.sinceId
|
$gt: ps.sinceId
|
||||||
};
|
};
|
||||||
} else if (ps.untilId) {
|
} else if (ps.untilId) {
|
||||||
|
sort._id = -1;
|
||||||
query._id = {
|
query._id = {
|
||||||
$lt: ps.untilId
|
$lt: ps.untilId
|
||||||
};
|
};
|
||||||
} else if (ps.sinceDate) {
|
} else if (ps.sinceDate) {
|
||||||
sort._id = 1;
|
sort.createdAt = 1;
|
||||||
query.createdAt = {
|
query.createdAt = {
|
||||||
$gt: new Date(ps.sinceDate)
|
$gt: new Date(ps.sinceDate)
|
||||||
};
|
};
|
||||||
} else if (ps.untilDate) {
|
} else if (ps.untilDate) {
|
||||||
|
sort.createdAt = -1;
|
||||||
query.createdAt = {
|
query.createdAt = {
|
||||||
$lt: new Date(ps.untilDate)
|
$lt: new Date(ps.untilDate)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,27 +116,27 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
|
|||||||
|
|
||||||
// リプライ対象が削除された投稿だったらreject
|
// リプライ対象が削除された投稿だったらreject
|
||||||
if (data.reply && data.reply.deletedAt != null) {
|
if (data.reply && data.reply.deletedAt != null) {
|
||||||
return rej();
|
return rej('Reply target has been deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renote対象が削除された投稿だったらreject
|
// Renote対象が削除された投稿だったらreject
|
||||||
if (data.renote && data.renote.deletedAt != null) {
|
if (data.renote && data.renote.deletedAt != null) {
|
||||||
return rej();
|
return rej('Renote target has been deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renote対象が「ホームまたは全体」以外の公開範囲ならreject
|
// Renote対象が「ホームまたは全体」以外の公開範囲ならreject
|
||||||
if (data.renote && data.renote.visibility != 'public' && data.renote.visibility != 'home') {
|
if (data.renote && data.renote.visibility != 'public' && data.renote.visibility != 'home') {
|
||||||
return rej();
|
return rej('Renote target is not public or home');
|
||||||
}
|
}
|
||||||
|
|
||||||
// リプライ対象が自分以外の非公開の投稿なら禁止
|
// リプライ対象が自分以外の非公開の投稿なら禁止
|
||||||
if (data.reply && data.reply.visibility == 'private' && !data.reply.userId.equals(user._id)) {
|
if (data.reply && data.reply.visibility == 'private' && !data.reply.userId.equals(user._id)) {
|
||||||
return rej();
|
return rej('Reply target is private of others');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renote対象が自分以外の非公開の投稿なら禁止
|
// Renote対象が自分以外の非公開の投稿なら禁止
|
||||||
if (data.renote && data.renote.visibility == 'private' && !data.renote.userId.equals(user._id)) {
|
if (data.renote && data.renote.visibility == 'private' && !data.renote.userId.equals(user._id)) {
|
||||||
return rej();
|
return rej('Renote target is private of others');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.text) {
|
if (data.text) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const { VueLoaderPlugin } = require('vue-loader');
|
|||||||
const WebpackOnBuildPlugin = require('on-build-webpack');
|
const WebpackOnBuildPlugin = require('on-build-webpack');
|
||||||
//const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
|
//const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
|
||||||
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
|
||||||
const constants = require('./src/const.json');
|
const constants = require('./src/const.json');
|
||||||
|
|
||||||
@@ -146,6 +147,9 @@ module.exports = {
|
|||||||
resolveLoader: {
|
resolveLoader: {
|
||||||
modules: ['node_modules']
|
modules: ['node_modules']
|
||||||
},
|
},
|
||||||
|
optimization: {
|
||||||
|
minimizer: [new TerserPlugin()]
|
||||||
|
},
|
||||||
cache: true,
|
cache: true,
|
||||||
devtool: false, //'source-map',
|
devtool: false, //'source-map',
|
||||||
mode: isProduction ? 'production' : 'development'
|
mode: isProduction ? 'production' : 'development'
|
||||||
|
|||||||
Reference in New Issue
Block a user