Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e41f74e77c | ||
|
|
c21caad1c5 | ||
|
|
86fcd3a378 | ||
|
|
2b3687b3cb | ||
|
|
5d61c7c691 | ||
|
|
1bb266e7c7 | ||
|
|
1fca8d322c | ||
|
|
325cd03a59 | ||
|
|
2f7e6baa05 | ||
|
|
d252e066fe | ||
|
|
fe7bd9ab3c | ||
|
|
84e3f41305 | ||
|
|
3e8cccad0d | ||
|
|
a2b94d67f7 | ||
|
|
6ab61e73b0 | ||
|
|
051c6973af | ||
|
|
806a49ec3d | ||
|
|
3829fe128a | ||
|
|
649177985d | ||
|
|
c15148b23c | ||
|
|
261a3f5d91 | ||
|
|
256ba78ba5 | ||
|
|
04aff8866e | ||
|
|
1a51b98700 | ||
|
|
f64100226d | ||
|
|
b7805e48a6 | ||
|
|
0d9556620d | ||
|
|
a51828a7a2 | ||
|
|
7e2009f408 | ||
|
|
008d950a39 | ||
|
|
22d5862afb | ||
|
|
de569147a5 | ||
|
|
a82c3db750 | ||
|
|
80706d10af | ||
|
|
93f01ed4df | ||
|
|
a3a28e5557 | ||
|
|
8948a0d3a4 | ||
|
|
d849ea9b41 | ||
|
|
0144575f3f | ||
|
|
bdbe646ca7 | ||
|
|
1a1483a242 | ||
|
|
962346785b | ||
|
|
a73da3cd70 | ||
|
|
9c27d0ae3f | ||
|
|
525d5218c1 | ||
|
|
e23b13ec7f | ||
|
|
29b000e03c | ||
|
|
6a7b0df810 | ||
|
|
4142de9195 | ||
|
|
9195e1be00 | ||
|
|
75382d13fd | ||
|
|
d444280a28 | ||
|
|
52fc0fe04a | ||
|
|
216bebadf1 | ||
|
|
a5592931cb | ||
|
|
a2228417ff | ||
|
|
3e1e292c3e | ||
|
|
f2f039ae9e | ||
|
|
29dde1eda0 | ||
|
|
45d3792ce0 | ||
|
|
875d0aaebb | ||
|
|
26c9d8ff6f | ||
|
|
5e3372e932 | ||
|
|
f7069dcd18 | ||
|
|
560bb65384 | ||
|
|
50cd6a036e | ||
|
|
441ab2b5f8 | ||
|
|
ba5ed188a1 | ||
|
|
72e672f08d | ||
|
|
120474ec6a | ||
|
|
eee57c47f5 | ||
|
|
4c160869b8 | ||
|
|
3720a7fbe0 | ||
|
|
7afa541a53 | ||
|
|
6f979c8275 | ||
|
|
d399241e65 | ||
|
|
e85dec030a | ||
|
|
d0220764cc | ||
|
|
75c1df9531 | ||
|
|
bca7156d6b | ||
|
|
64277b7157 | ||
|
|
4a72543f65 | ||
|
|
5b84d29807 | ||
|
|
a11061ec2b | ||
|
|
24cfb93b2e | ||
|
|
502b42d63a | ||
|
|
612672b79c | ||
|
|
abc670e1b1 | ||
|
|
d589ccdd01 | ||
|
|
acb07d9f7d | ||
|
|
f4d2186719 | ||
|
|
d0ede5c665 | ||
|
|
554cbb5e9b | ||
|
|
dbd32a56bf | ||
|
|
7f500235c6 | ||
|
|
39a58084c8 | ||
|
|
cde0fde836 | ||
|
|
e70cca0fda | ||
|
|
919bd7eb82 | ||
|
|
312cff3d6f | ||
|
|
0d86eef3d7 | ||
|
|
13acf570e7 | ||
|
|
fa17623fa8 | ||
|
|
06fd525950 | ||
|
|
4805b5115a | ||
|
|
108dcb3e61 | ||
|
|
780d272535 | ||
|
|
02ea4b81a5 | ||
|
|
7c1bdc6d36 | ||
|
|
78c7b8b836 | ||
|
|
227da30acb | ||
|
|
610805026f | ||
|
|
c02399c3d2 | ||
|
|
e0799d4153 | ||
|
|
6df83f1aa9 | ||
|
|
efb5ad1d9b | ||
|
|
716976f016 | ||
|
|
7892f41b84 | ||
|
|
d549e03b3f | ||
|
|
c511ef21ff | ||
|
|
d64dc45899 | ||
|
|
bcb0588409 | ||
|
|
0975959eb9 | ||
|
|
e985a6d9d3 | ||
|
|
b893305974 | ||
|
|
724fdd44e4 | ||
|
|
b480ef669c | ||
|
|
4b145da046 | ||
|
|
83d168ece3 | ||
|
|
ae44fe7818 | ||
|
|
f8981b3acb | ||
|
|
050b324885 | ||
|
|
e74c0df6c6 | ||
|
|
22d0d11895 | ||
|
|
80d0c0cf74 | ||
|
|
518646b925 | ||
|
|
479d7e0087 | ||
|
|
8ea1a555f4 | ||
|
|
04024dc37c | ||
|
|
060ff9288f | ||
|
|
197116ee78 | ||
|
|
a1e0015257 | ||
|
|
7e701ef9e0 | ||
|
|
3d6fb661bb | ||
|
|
fc372496da | ||
|
|
ad7258fe9c | ||
|
|
bd707cb2a8 | ||
|
|
1839b5f205 | ||
|
|
02b47f963c | ||
|
|
f8a7f9378a | ||
|
|
65cb253be4 | ||
|
|
a12356b24b | ||
|
|
6a67ad7f93 | ||
|
|
140a7f0b1c | ||
|
|
00159bc6b5 | ||
|
|
9542260103 | ||
|
|
72074578df | ||
|
|
3b4750a988 | ||
|
|
aeec5f0163 | ||
|
|
9c94d8c8d6 | ||
|
|
581712a2c8 | ||
|
|
b25b51aaca | ||
|
|
fb97e13a61 | ||
|
|
36e154fdb2 | ||
|
|
ca273a24b4 | ||
|
|
d828bf2889 | ||
|
|
87efccef18 | ||
|
|
e0bf522e7f | ||
|
|
5b1cd3bd3c | ||
|
|
f00489196d | ||
|
|
dd53bf7e51 | ||
|
|
35a6da26d2 | ||
|
|
c8c8748a0b | ||
|
|
46d0065a90 | ||
|
|
990b0180a8 | ||
|
|
f3bfb72251 |
@@ -9,6 +9,7 @@ mongodb:
|
||||
db: test-misskey
|
||||
user: admin
|
||||
pass: ''
|
||||
# __REDIS__
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
137
.circleci/config.yml
Normal file
137
.circleci/config.yml
Normal file
@@ -0,0 +1,137 @@
|
||||
version: 2.1
|
||||
|
||||
general:
|
||||
branches:
|
||||
ignore:
|
||||
- l10n_develop
|
||||
- imgbot
|
||||
|
||||
executors:
|
||||
default:
|
||||
working_directory: /tmp/workspace
|
||||
docker:
|
||||
- image: misskey/ci:latest
|
||||
- image: circleci/mongo:latest
|
||||
- image: circleci/redis:latest
|
||||
docker:
|
||||
working_directory: /tmp/workspace
|
||||
docker:
|
||||
- image: docker:latest
|
||||
|
||||
jobs:
|
||||
build:
|
||||
executor: default
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: Restore npm package caches
|
||||
keys:
|
||||
- npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-
|
||||
- npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-
|
||||
- npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-
|
||||
- npm-v1-arch-{{ arch }}-
|
||||
- npm-v1-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: |
|
||||
npm install
|
||||
- run:
|
||||
name: Configure
|
||||
command: |
|
||||
cp .ci/default.yml .config
|
||||
cp .ci/test.yml .config
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
npm run build || (echo -e '\033[0;34mRebuild modules\033[0;39m' && ls -1A node_modules | grep '^[^@]' | xargs npm rebuild && ls -1A node_modules | grep '^@' | xargs -I%1 sh -c 'ls -1A node_modules/'%1' | xargs -P0 -I%2 npm rebuild node_modules/'%1'/%2' && npm run build)
|
||||
ls -1ARl node_modules > ls
|
||||
- save_cache:
|
||||
name: Cache npm packages
|
||||
key: npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-ls-{{ checksum "ls" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- store_artifacts:
|
||||
path: built
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- .
|
||||
test:
|
||||
parameters:
|
||||
without_redis:
|
||||
type: string
|
||||
default: ""
|
||||
executor: default
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- when:
|
||||
condition: <<parameters.without_redis>>
|
||||
steps:
|
||||
- run:
|
||||
name: Configure
|
||||
command: |
|
||||
mv .config/test.yml .config/test_redis.yml
|
||||
touch .config/test.yml
|
||||
cat .config/test_redis.yml | while IFS= read line; do if [[ "$line" = '# __REDIS__' ]]; then break; else echo "$line" >> .config/test.yml; fi; done
|
||||
- run:
|
||||
name: Test
|
||||
command: |
|
||||
npm run test || (npm rebuild && npm run test) || ((node-gyp configure && node-gyp build && npm run build || (echo -e '\033[0;34mRebuild modules\033[0;39m' && ls -1A node_modules | grep '^[^@]' | xargs npm rebuild && ls -1A node_modules | grep '^@' | xargs -I%1 sh -c 'ls -1A node_modules/'%1' | xargs -P0 -I%2 npm rebuild node_modules/'%1'/%2' && npm run build)) && npm run test)
|
||||
ls -1ARl node_modules > ls
|
||||
- save_cache:
|
||||
name: Cache npm packages
|
||||
key: npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-ls-{{ checksum "ls" }}
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
docker:
|
||||
parameters:
|
||||
with_deploy:
|
||||
type: string
|
||||
default: ""
|
||||
executor: docker
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
docker build . | tee docker.log
|
||||
tail -n 1 docker.log | read __Successfully __built tag
|
||||
- when:
|
||||
condition: <<parameters.with_deploy>>
|
||||
steps:
|
||||
- run:
|
||||
name: Deploy
|
||||
command: |
|
||||
if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ]
|
||||
then
|
||||
docker tag $tag misskey/misskey
|
||||
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
|
||||
docker push misskey/misskey
|
||||
else
|
||||
echo -e '\033[0;33mAborted deploying to Docker Hub\033[0;39m'
|
||||
fi
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build
|
||||
- test:
|
||||
requires:
|
||||
- build
|
||||
- test:
|
||||
without_redis: "true"
|
||||
requires:
|
||||
- build
|
||||
- docker:
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
- docker:
|
||||
with_deploy: "true"
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
@@ -35,7 +35,7 @@ before_script:
|
||||
- npm install
|
||||
|
||||
# 設定ファイルを配置
|
||||
- cp ./.travis/default.yml ./.config
|
||||
- cp ./.travis/test.yml ./.config
|
||||
- cp ./.ci/default.yml ./.config
|
||||
- cp ./.ci/test.yml ./.config
|
||||
|
||||
- travis_wait npm run build
|
||||
|
||||
24
Dockerfile
24
Dockerfile
@@ -3,8 +3,9 @@ FROM alpine:3.8 AS base
|
||||
ENV NODE_ENV=production
|
||||
|
||||
RUN apk add --no-cache nodejs nodejs-npm zlib
|
||||
RUN npm i -g npm@latest
|
||||
|
||||
WORKDIR /misskey
|
||||
COPY . ./
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
@@ -21,18 +22,23 @@ RUN apk add --no-cache \
|
||||
pkgconfig \
|
||||
libtool \
|
||||
zlib-dev
|
||||
RUN npm install \
|
||||
&& npm install -g node-gyp \
|
||||
&& node-gyp configure \
|
||||
&& node-gyp build \
|
||||
&& npm run build
|
||||
RUN npm i -g node-gyp
|
||||
|
||||
COPY ./package.json ./
|
||||
RUN npm i
|
||||
|
||||
COPY . ./
|
||||
RUN node-gyp configure \
|
||||
&& node-gyp build \
|
||||
&& npm run build
|
||||
|
||||
FROM base AS runner
|
||||
|
||||
COPY --from=builder /misskey/built ./built
|
||||
COPY --from=builder /misskey/node_modules ./node_modules
|
||||
|
||||
RUN apk add --no-cache tini
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
||||
COPY --from=builder /misskey/node_modules ./node_modules
|
||||
COPY --from=builder /misskey/built ./built
|
||||
COPY . ./
|
||||
|
||||
CMD ["npm", "start"]
|
||||
|
||||
30
README.md
30
README.md
@@ -3,6 +3,7 @@
|
||||
[](https://misskey.xyz/)
|
||||
================================================================
|
||||
|
||||
[](https://circleci.com/gh/syuilo/misskey)
|
||||
[![][travis-badge]][travis-link]
|
||||
[![][dependencies-badge]][dependencies-link]
|
||||
[](http://makeapullrequest.com)
|
||||
@@ -71,39 +72,46 @@ Please see [Contribution guide](./CONTRIBUTING.md).
|
||||
----------------------------------------------------------------
|
||||
<!-- PATREON_START -->
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=CXe9AqlZy9AsYfiWd3OBYVOzvODoN47Litz0Tu4BFpU%3D" alt="Gargron"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D" alt="negao"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1?token-time=2145916800&token-hash=d6P5MWHHsCMxUuBAEPAoVc5wLUR19mIhqAq7Ma9h9rI%3D" alt="ne_moni"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/2?token-time=2145916800&token-hash=mgPdX9TqZxEg4TTPuc477dxhIgYk9246qafjWZEqZ7g%3D" alt="Melilot"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/2?token-time=2145916800&token-hash=rwZ8qvbm_kpA4ib3kc07tVKupXeySpY5ATQFGxfL9v0%3D" alt="Xeltica"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/3384329/8b713330cb27404ea6e9fac50ff96efe/1?token-time=2145916800&token-hash=0eu4-m1gTWA9PhptVZt6rdKcusqcD7RB87rJT23VVFI%3D" alt="べすれい"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=GgJ_NmUB6_nnRNLVGUWjV-WX91On7BOu59LKncYV9fE%3D" alt="gutfuckllc"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1?token-time=2145916800&token-hash=I8lJVM8LeW6TSo5W6uIIRZ42cw83zp1wK_FsbzY0mcQ%3D" alt="mydarkstar"></td>
|
||||
<td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/negao">negao</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||
<td><a href="https://www.patreon.com/AxellaMC">Xeltica</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=3384329">べすれい</a></td>
|
||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/2?token-time=2145916800&token-hash=zElv7ZcPL3viGsXbNG_KWiKrbV0vvw1gk0panx8DJoo%3D" alt="Naoki Kosaka"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3?token-time=2145916800&token-hash=qsdn0-e6yLaLI6hUX9JAkyTR6a5UdnSp7T1foniBvGQ%3D" alt="YUKIMOCHI"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/8241184/39e18850e87a449e9c9a71acb3310ebd/2?token-time=2145916800&token-hash=iUXOQzRyJDv3PJxwS7Mjwg1459dzh2trOq6NFtXu_OM%3D" alt="Acid Chicken"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=UERBN4OyP7Nh5XwwdDg0N0IE5cD6_qUQMO81Z5Wizso%3D" alt="Hiratake"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/10789744/97175095d8f04c0f86225ff47cb98d40/1?token-time=2145916800&token-hash=P4BIzCX2I1CkEP66ottfhsC8Wr6BUSamjA-vq3pLqFI%3D" alt="Naoki Hirayama"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=S1zP0QyLU52Dqq6dtc9qNYyWfW86XrYHiR4NMbeOrnA%3D" alt="dansup"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=tB1e_r8RlZ5sFL0KV_e8dugapxatNBRK1Z3h67TO1g8%3D" alt="Gargron"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1?token-time=2145916800&token-hash=VZUtwrjQa8Jml4twCjHYQQZ64wHEY4oIlGl7Kc-VYUQ%3D" alt="Nokotaro Takeda"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=tMosUojzUYJCH_3t--tvYA-SMCyrS__hzSndyaRSnbo%3D" alt="Takashi Shibuya"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/user?u=5881381">Naoki Kosaka</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
|
||||
<td><a href="https://www.patreon.com/acid_chicken">Acid Chicken</a></td>
|
||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
|
||||
<td><a href="https://www.patreon.com/spinlock">Naoki Hirayama</a></td>
|
||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
</tr><tr>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Tue, 02 Oct 2018 09:25:07 UTC
|
||||
**Last updated:** Wed, 31 Oct 2018 23:21:06 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
||||
38
appveyor.yml
38
appveyor.yml
@@ -1,38 +0,0 @@
|
||||
# appveyor file
|
||||
# http://www.appveyor.com/docs/appveyor-yml
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: 11.0.0
|
||||
|
||||
platform:
|
||||
- x64
|
||||
- Any CPU
|
||||
|
||||
build: off
|
||||
|
||||
install:
|
||||
# Get the latest stable version of Node.js or io.js
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
|
||||
# Update node-gyp
|
||||
# 必須! node-gyp のバージョンを上げないと、ネイティブモジュールのコンパイルに失敗します
|
||||
- npm install -g node-gyp
|
||||
|
||||
- npm install
|
||||
|
||||
init:
|
||||
# git clone の際の改行を変換しないようにします
|
||||
- git config --global core.autocrlf false
|
||||
|
||||
before_test:
|
||||
# 設定ファイルを配置
|
||||
- cp ./.travis/default.yml ./.config
|
||||
- cp ./.travis/test.yml ./.config
|
||||
|
||||
- npm run build
|
||||
|
||||
test_script:
|
||||
- node --version
|
||||
- npm --version
|
||||
- npm test
|
||||
@@ -1,13 +0,0 @@
|
||||
const deleteUser = require('../built/models/user').deleteUser;
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const userId = args[0];
|
||||
|
||||
console.log(`deleting ${userId}...`);
|
||||
|
||||
deleteUser(userId).then(() => {
|
||||
console.log('done');
|
||||
}, e => {
|
||||
console.error(e);
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
const mongo = require('mongodb');
|
||||
const User = require('../built/models/user').default;
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const user = args[0];
|
||||
|
||||
const q = user.startsWith('@') ? {
|
||||
username: user.split('@')[1],
|
||||
host: user.split('@')[2] || null
|
||||
} : { _id: new mongo.ObjectID(user) };
|
||||
|
||||
console.log(`Mark as verfied ${user}...`);
|
||||
|
||||
User.update(q, {
|
||||
$set: {
|
||||
isVerified: true
|
||||
}
|
||||
}).then(() => {
|
||||
console.log(`Done ${user}`);
|
||||
}, e => {
|
||||
console.error(e);
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
const { default: Note } = require('../built/models/note');
|
||||
const { default: Meta } = require('../built/models/meta');
|
||||
const { default: User } = require('../built/models/user');
|
||||
|
||||
async function main() {
|
||||
const meta = await Meta.findOne({});
|
||||
|
||||
const notesCount = await Note.count();
|
||||
|
||||
const usersCount = await User.count();
|
||||
|
||||
const originalNotesCount = await Note.count({
|
||||
'_user.host': null
|
||||
});
|
||||
|
||||
const originalUsersCount = await User.count({
|
||||
host: null
|
||||
});
|
||||
|
||||
const stats = {
|
||||
notesCount,
|
||||
usersCount,
|
||||
originalNotesCount,
|
||||
originalUsersCount
|
||||
};
|
||||
|
||||
if (meta) {
|
||||
await Meta.update({}, {
|
||||
$set: {
|
||||
stats
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await Meta.insert({
|
||||
stats
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
console.log('done');
|
||||
}).catch(console.error);
|
||||
@@ -18,6 +18,10 @@ If you find an untranslated part on Misskey:
|
||||
4. Add the text property using the `foo` keyword below the path that you found or created in step 2. Make sure to type your text in quotation marks. Text should always be inside of quotes.
|
||||
- For example, in this case we add timeline: `timeline: "タイムライン"` to `locales/ja-JP.yml`.
|
||||
|
||||
5. And done!
|
||||
5. When you add text to the ja-JP file (of syuilo/misskey), it will automatically be applied to all other local language files within 24-48 hours. Translations added in ja-JP file should contain the original Japanese strings (example see step 4).
|
||||
|
||||
6. The new strings will automatically appear in the localized language files in the original Japanese text. After that, please go to [CrowdIn](https://crowdin.com/project/misskey) to do the localized translations in your language.
|
||||
|
||||
7. And done!
|
||||
|
||||
For more details, please refer to this [commit](https://github.com/syuilo/misskey/commit/10f6d5980fa7692ccb45fbc5f843458b69b7607c).
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
images: "画像"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "Profil"
|
||||
notification: "Mitteilungen"
|
||||
apps: "In App öffnen"
|
||||
mute: "Stummschalten"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "Sicherheit"
|
||||
signin: "サインイン履歴"
|
||||
password: "Passwort"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "Bitte Passwort eingeben"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "Passwort ändern"
|
||||
enter-current-password: "Derzeitiges Passwort eingeben"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
images: "画像"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "Profile"
|
||||
notification: "Notification"
|
||||
apps: "Apps"
|
||||
mute: "Mute"
|
||||
mute-and-block: "Mute / Block"
|
||||
blocking: "Blocking"
|
||||
security: "Security"
|
||||
signin: "Sign in history"
|
||||
password: "Password"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "Settings saved!"
|
||||
failed: "Failed to setup. Please ensure that the token is correct."
|
||||
info: "From the next time you sign in to Misskey, the token displayed on your device will be necessary too, as well as the password."
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "To access the API, set this token as the key 'i' of request parameters."
|
||||
caution: "Do not enter this token to any apps nor tell this token to others otherwise your account may get compromised."
|
||||
regeneration-of-token: "If your token gets leaked, you can regenerate it."
|
||||
regenerate-token: "Regenerate the token"
|
||||
token: "Token:"
|
||||
enter-password: "Please enter the password"
|
||||
enter-password: "Enter the password"
|
||||
console:
|
||||
title: 'API console'
|
||||
endpoint: 'Endpoint'
|
||||
parameter: 'Parameters'
|
||||
send: 'Send'
|
||||
sending: 'Sending'
|
||||
response: 'Result'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "No linked applications"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "Max"
|
||||
in-use: "In use"
|
||||
stats: "Statistics"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "No muted users"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "Mute / Block"
|
||||
mute: "Mute"
|
||||
block: "Blocking"
|
||||
no-muted-users: "No muted users"
|
||||
no-blocked-users: "No blocked users"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "Change password"
|
||||
enter-current-password: "Enter the current password"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "Mute"
|
||||
muted: "Muting"
|
||||
unmute: "Unmute"
|
||||
block: "Block"
|
||||
unblock: "Unblock"
|
||||
block-confirm: "Are you sure block this user?"
|
||||
push-to-a-list: "Add to list"
|
||||
list-pushed: "Successfully added {user} to {list}."
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "Timeline"
|
||||
media: "Media"
|
||||
is-suspended: "This account has been suspended."
|
||||
mute: "Mute"
|
||||
unmute: "Unmute"
|
||||
block: "Block"
|
||||
unblock: "Unblock"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "Recent notes"
|
||||
images: "Images"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "Perfil"
|
||||
notification: "Notificación"
|
||||
apps: "Aplicaciones"
|
||||
mute: "Silenciar"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "Seguridad"
|
||||
signin: "Historial de inicios de sesión"
|
||||
password: "Contraseña"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "¡Configuraciones guardadas!"
|
||||
failed: "Error al configurar. Por favor asegúrate de que el token es correcto."
|
||||
info: "Desde ahora, ingresa el token que se muestra en tu dispositivo adicionalmente a tu contraseña cuando inicies sesión en Misskey"
|
||||
desktop/views/components/settings.api.vue:
|
||||
intro: "Para acceder al API, configura este token como la letra \"i\" de los parámetros requeridos."
|
||||
caution: "Por favor no muestres este token a otros (no lo ingreses en otro lugar que no sea aquí). De otra forma, tu cuenta puede llegar a ser comprometida."
|
||||
regeneration-of-token: "En el caso no deseado de que este token lo tenga otra persona, puedes regenerarlo."
|
||||
regenerate-token: "Regenerar el token"
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "Por favor ingresa tu contraseña"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "No hay aplicaciones asociadas"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "No hay usuarios silenciados"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "Cambiar contraseña"
|
||||
enter-current-password: "Ingresar contraseña actual"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
images: "画像"
|
||||
|
||||
@@ -28,11 +28,11 @@ common:
|
||||
BSoD:
|
||||
fatal-error: ":( 致命的な問題が発生しました。"
|
||||
update-browser-os: "お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。"
|
||||
error-code: "エラーコード"
|
||||
browser-version: "ブラウザ バージョン"
|
||||
client-version: "クライアント バージョン"
|
||||
error-code: "Code d’erreur"
|
||||
browser-version: "Version du navigateur"
|
||||
client-version: "La version du client"
|
||||
email-support: "問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。"
|
||||
thanks: "Thank you for using Misskey."
|
||||
thanks: "Merci d’avoir choisi d’utiliser Misskey."
|
||||
got-it: "J'ai compris !"
|
||||
customization-tips:
|
||||
title: "Conseils de personnalisation"
|
||||
@@ -62,7 +62,7 @@ common:
|
||||
years_ago: "Il y a {} an·s"
|
||||
month-and-day: "{month} mois/{day} jour"
|
||||
trash: "Corbeille"
|
||||
drive: "ドライブ"
|
||||
drive: "Drive"
|
||||
weekday-short:
|
||||
sunday: "D"
|
||||
monday: "L"
|
||||
@@ -124,12 +124,12 @@ common:
|
||||
reduce-motion: "Réduire les animations dans l’interface utilisateur"
|
||||
this-setting-is-this-device-only: "Uniquement sur cet appareil"
|
||||
do-not-use-in-production: 'Il s’agit d’une version de développement. Ne pas utiliser dans un environnement de production.'
|
||||
is-remote-user: "このユーザー情報はコピーです。"
|
||||
is-remote-user: "Ces informations utilisateur ont été copiées."
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
view-on-remote: "Consulter le profil complet"
|
||||
error:
|
||||
title: '問題が発生しました'
|
||||
retry: 'やり直す'
|
||||
title: 'Une erreur est survenue'
|
||||
retry: 'Réessayer'
|
||||
reversi:
|
||||
drawn: "Partie nulle"
|
||||
my-turn: "C’est votre tour"
|
||||
@@ -185,7 +185,7 @@ common:
|
||||
rename: "Renommer"
|
||||
stack-left: "Vers la gauche"
|
||||
pop-right: "Vers la droite"
|
||||
dev: "アプリの作成に失敗しました。再度お試しください。"
|
||||
dev: "Échec lors de la création de l’application. Veuillez réessayer."
|
||||
auth/views/form.vue:
|
||||
share-access: "Désirez-vous <b>autoriser</b> <i>{{ app.name }}</i> à avoir accès à votre compte ?"
|
||||
permission-ask: "Cette application nécessite les autorisations suivantes :"
|
||||
@@ -542,24 +542,24 @@ desktop/views/components/charts.vue:
|
||||
title: "Graphiques"
|
||||
per-day: "par jour"
|
||||
per-hour: "par heure"
|
||||
federation: "フェデレーション"
|
||||
federation: "Fédération"
|
||||
notes: "Publications"
|
||||
users: "Utilisateurs"
|
||||
drive: "Drive"
|
||||
network: "Réseau"
|
||||
charts:
|
||||
federation-instances: "インスタンスの増減"
|
||||
federation-instances-total: "インスタンスの積算"
|
||||
federation-instances-total: "Nombre total d’instances"
|
||||
notes: "投稿の増減 (統合)"
|
||||
local-notes: "投稿の増減 (ローカル)"
|
||||
remote-notes: "投稿の増減 (リモート)"
|
||||
notes-total: "Total des notes"
|
||||
users: "Nombre d’utilisateurs·trices : augmentation/diminution"
|
||||
users-total: "ユーザーの積算"
|
||||
users-total: "Nombre total des utilisateurs·rices"
|
||||
drive: "ドライブ使用量の増減"
|
||||
drive-total: "ドライブ使用量の積算"
|
||||
drive-total: "Utilisation totale du lecteur"
|
||||
drive-files: "ドライブのファイル数の増減"
|
||||
drive-files-total: "ドライブのファイル数の積算"
|
||||
drive-files-total: "Nombre total de fichiers sur le lecteur"
|
||||
network-requests: "Requêtes"
|
||||
network-time: "Temps de réponse"
|
||||
network-usage: "Traffic"
|
||||
@@ -679,10 +679,10 @@ desktop/views/components/note.vue:
|
||||
reposted-by: "Partagé par {}"
|
||||
reply: "Répondre"
|
||||
renote: "Partager"
|
||||
add-reaction: "リアクション"
|
||||
detail: "詳細"
|
||||
private: "この投稿は非公開です"
|
||||
deleted: "この投稿は削除されました"
|
||||
add-reaction: "Ajouter votre réaction"
|
||||
detail: "Détails"
|
||||
private: "Cette publication est privée"
|
||||
deleted: "Cette publication a été supprimée"
|
||||
desktop/views/components/notes.vue:
|
||||
error: "Échec du chargement."
|
||||
retry: "Réessayer"
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "Profil"
|
||||
notification: "Notification"
|
||||
apps: "Applications"
|
||||
mute: "Mettre en sourdine"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "Sécurité"
|
||||
signin: "Historique de connexion"
|
||||
password: "Mot de Passe"
|
||||
@@ -765,7 +766,7 @@ desktop/views/components/settings.vue:
|
||||
deck-default: "デッキをデフォルトのUIにする"
|
||||
display: "Affichage et design"
|
||||
customize: "Personnaliser l'Accueil"
|
||||
wallpaper: "壁紙"
|
||||
wallpaper: "Arrière plan"
|
||||
choose-wallpaper: "Sélectionner un fond d'écran"
|
||||
delete-wallpaper: "Supprimer le fond d'écran"
|
||||
dark-mode: "Mode nuit"
|
||||
@@ -777,14 +778,14 @@ desktop/views/components/settings.vue:
|
||||
suggest-recent-hashtags: "Afficher les hashtags populaires dans le champs de saisie"
|
||||
show-clock-on-header: "Afficher l'horloge à droite sur le coté supérieur"
|
||||
show-reply-target: "Afficher les réponses"
|
||||
timeline: "タイムライン"
|
||||
timeline: "Chronologie"
|
||||
show-my-renotes: "Afficher mes republications dans le fil"
|
||||
show-renoted-my-notes: "Afficher mes republications dans les fils"
|
||||
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
|
||||
show-maps: "Afficher la carte"
|
||||
deck-column-align: "デッキのカラムの位置"
|
||||
deck-column-align-center: "中央"
|
||||
deck-column-align-left: "左"
|
||||
deck-column-align-center: "Centrer"
|
||||
deck-column-align-left: "À gauche"
|
||||
sound: "Son"
|
||||
enable-sounds: "Activer le son"
|
||||
enable-sounds-desc: "Jouer un son lorsque vous recevez un message. Ce paramètre est sauvegardé dans le navigateur."
|
||||
@@ -825,7 +826,7 @@ desktop/views/components/settings.vue:
|
||||
tools: "Outils"
|
||||
task-manager: "Gestionnaire de tâches"
|
||||
third-parties: "Services tiers"
|
||||
navbar-position: "ナビゲーションバーの位置"
|
||||
navbar-position: "Position de la barre de navigation"
|
||||
navbar-position-top: "En haut"
|
||||
navbar-position-left: "à gauche"
|
||||
navbar-position-right: "à droite"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "L'operation a été complétée avec succès!"
|
||||
failed: "L'operation a échoué. Veuillez vous assurer que le token a été entrer correctement."
|
||||
info: "À partir de maintenant, à chaque fois que vous vous connecter entrez votre mot de passe ainsi que le token généré sur votre appareil."
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "Si votre jeton est compromis, vous pouvez le régénérer."
|
||||
regenerate-token: "Regenerer le token"
|
||||
regenerate-token: "Régénérer le jeton"
|
||||
token: "Jeton :"
|
||||
enter-password: "Veuillez entrer le mot de passe"
|
||||
enter-password: "Entrez le mot de passe"
|
||||
console:
|
||||
title: 'Console API'
|
||||
endpoint: 'Point de terminaison'
|
||||
parameter: 'Paramètres'
|
||||
send: 'Envoyer'
|
||||
sending: 'Envoi en cours'
|
||||
response: 'Résultat'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "Aucune application autorisée"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "Aucun utilisateurs mis en sourdine"
|
||||
in-use: "utilisé"
|
||||
stats: "Statistiques"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "Changer votre mot de passe"
|
||||
enter-current-password: "Entrez votre mot de passe actuel"
|
||||
@@ -938,8 +950,8 @@ desktop/views/pages/admin/admin.vue:
|
||||
dashboard: "Tableau de bord"
|
||||
users: "Utilisateur·rice·s"
|
||||
update: "Mises à jour"
|
||||
announcements: "お知らせ"
|
||||
hashtags: "ハッシュタグ"
|
||||
announcements: "Annonces"
|
||||
hashtags: "Hashtags"
|
||||
desktop/views/pages/admin/admin.dashboard.vue:
|
||||
dashboard: "Tableau de bord"
|
||||
all-users: "Toutes les utilisateurrices"
|
||||
@@ -947,9 +959,9 @@ desktop/views/pages/admin/admin.dashboard.vue:
|
||||
all-notes: "Toutes les publications"
|
||||
original-notes: "Publications sur cette instance"
|
||||
invite: "Invitation"
|
||||
banner-url: "Banner URL"
|
||||
disableRegistration: "Disable new user registration"
|
||||
disableLocalTimeline: "Disable the local timeline"
|
||||
banner-url: "URL de la bannière"
|
||||
disableRegistration: "Désactiver l’enregistrement de nouveaux utilisateurs·rices"
|
||||
disableLocalTimeline: "Désactiver le fil local"
|
||||
desktop/views/pages/admin/admin.suspend-user.vue:
|
||||
suspend-user: "Suspendre un·e utilisateur·rice"
|
||||
suspend: "Suspendre"
|
||||
@@ -967,22 +979,22 @@ desktop/views/pages/admin/admin.unverify-user.vue:
|
||||
unverify: "Ôter la vérification du compte"
|
||||
unverified: "Ce compte n'est pas vérifié"
|
||||
desktop/views/pages/admin/admin.announcements.vue:
|
||||
announcements: "お知らせ"
|
||||
announcements: "Annonces"
|
||||
desktop/views/pages/admin/admin.hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
hided-tags: "Tags cachés"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
is-media-only: "Les publications médias uniquement"
|
||||
is-media-view: "Vue média"
|
||||
edit: "Options"
|
||||
desktop/views/pages/deck/deck.user-column.vue:
|
||||
posts: "投稿"
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
images: "画像"
|
||||
activity: "アクティビティ"
|
||||
timeline: "タイムライン"
|
||||
pinned-notes: "ピン留めされた投稿"
|
||||
push-to-a-list: "リストに追加"
|
||||
posts: "Publications"
|
||||
following: "Suit"
|
||||
followers: "Abonné·e·s"
|
||||
images: "Images"
|
||||
activity: "Activité"
|
||||
timeline: "Chronologie"
|
||||
pinned-notes: "Publications épinglées"
|
||||
push-to-a-list: "Ajouter à la liste"
|
||||
desktop/views/pages/stats/stats.vue:
|
||||
all-users: "Toutes les utilisateurrices"
|
||||
original-users: "Utilisateur·rice·s sur cette instance"
|
||||
@@ -1035,7 +1047,7 @@ desktop/views/pages/user/user.friends.vue:
|
||||
no-users: "Pas d'utilisateurs"
|
||||
desktop/views/pages/user/user.vue:
|
||||
is-suspended: "Ce compte a été suspendu."
|
||||
last-used-at: "最終アクセス"
|
||||
last-used-at: "Actif·ive pour la dernière fois"
|
||||
desktop/views/pages/user/user.photos.vue:
|
||||
title: "Photos"
|
||||
loading: "Chargement en cours"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "Mettre en sourdine"
|
||||
muted: "Muting"
|
||||
unmute: "Enlever la sourdine"
|
||||
block: "Bloquer"
|
||||
unblock: "Débloquer"
|
||||
block-confirm: "Bloquer cet utilisateur ?"
|
||||
push-to-a-list: "Ajouter à la liste"
|
||||
list-pushed: "Vous avez ajouté {user} à la liste {list}."
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1055,10 +1070,10 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "Suit"
|
||||
followers: "Abonné·e·s"
|
||||
is-bot: "Ce compte est un Bot"
|
||||
years-old: "歳"
|
||||
year: "年"
|
||||
month: "月"
|
||||
day: "日"
|
||||
years-old: "ans d’âge"
|
||||
year: "Année"
|
||||
month: "Mois"
|
||||
day: "Jour"
|
||||
desktop/views/pages/user/user.timeline.vue:
|
||||
default: "Publications"
|
||||
with-replies: "Publications et réponses"
|
||||
@@ -1117,8 +1132,8 @@ mobile/views/components/drive.file-detail.vue:
|
||||
hash: "Hash (md5)"
|
||||
exif: "EXIF"
|
||||
nsfw: "CW"
|
||||
mark-as-sensitive: "閲覧注意に設定"
|
||||
unmark-as-sensitive: "閲覧注意を解除"
|
||||
mark-as-sensitive: "Marquer comme sensible"
|
||||
unmark-as-sensitive: "Ne pas marquer comme sensible"
|
||||
mobile/views/components/media-image.vue:
|
||||
sensitive: "Le contenu est NSFW"
|
||||
click-to-show: "Cliquer pour afficher"
|
||||
@@ -1278,8 +1293,8 @@ mobile/views/pages/settings.vue:
|
||||
timeline: "Fil d'actualité"
|
||||
show-reply-target: "Afficher les réponses"
|
||||
show-my-renotes: "Afficher mes republications"
|
||||
show-renoted-my-notes: "自分の投稿のRenoteを表示する"
|
||||
show-local-renotes: "ローカルの投稿のRenoteを表示する"
|
||||
show-renoted-my-notes: "Afficher mes publications partagées"
|
||||
show-local-renotes: "Afficher les publications partagées localement"
|
||||
post-style: "Style de la publication"
|
||||
post-style-standard: "Standard"
|
||||
post-style-smart: "Intelligent"
|
||||
@@ -1312,7 +1327,7 @@ mobile/views/pages/settings.vue:
|
||||
signout: "Déconnexion"
|
||||
sound: "Sons"
|
||||
enable-sounds: "Activer les sons"
|
||||
mark-as-read-all-unread-notes: "すべての投稿を既読にする"
|
||||
mark-as-read-all-unread-notes: "Marquer toutes les publications comme lues"
|
||||
mobile/views/pages/user.vue:
|
||||
follows-you: "Vous suit"
|
||||
following: "Abonnements"
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "Fil d'actualité"
|
||||
media: "Media"
|
||||
is-suspended: "This account has been suspended."
|
||||
mute: "Mettre en sourdine"
|
||||
unmute: "Enlever la sourdine"
|
||||
block: "Bloquer"
|
||||
unblock: "Débloquer"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "Notes récentes"
|
||||
images: "Images"
|
||||
@@ -1368,28 +1387,28 @@ docs:
|
||||
dev/views/index.vue:
|
||||
manage-apps: "Gestion des applications"
|
||||
dev/views/apps.vue:
|
||||
manage-apps: "アプリを管理"
|
||||
create-app: "アプリ作成"
|
||||
app-missing: "アプリなし"
|
||||
manage-apps: "Gestion des applications"
|
||||
create-app: "Créer une app"
|
||||
app-missing: "Aucune application"
|
||||
dev/views/new-app.vue:
|
||||
create-app: "アプリケーションの作成"
|
||||
app-name: "アプリケーション名"
|
||||
app-name-desc: "あなたのアプリの名称。"
|
||||
app-name-ex: "ex) Misskey for iOS"
|
||||
app-overview: "アプリの概要"
|
||||
app-desc: "あなたのアプリの簡単な説明や紹介。"
|
||||
app-desc-ex: "ex) Misskey iOSクライアント。"
|
||||
create-app: "Création d’une application"
|
||||
app-name: "Nom de l’application"
|
||||
app-name-desc: "Le nom de votre application"
|
||||
app-name-ex: "p. ex. Misskey pour iOS"
|
||||
app-overview: "Description courte de l’application"
|
||||
app-desc: "Brève description introductive à votre application."
|
||||
app-desc-ex: "p. ex) Misskey pour iOS"
|
||||
callback-url: "コールバックURL (オプション)"
|
||||
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
|
||||
authority: "権限"
|
||||
authority: "Autorisations "
|
||||
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
|
||||
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"
|
||||
account-read: "アカウントの情報を見る。"
|
||||
account-write: "アカウントの情報を操作する。"
|
||||
note-write: "投稿する。"
|
||||
reaction-write: "リアクションしたりリアクションをキャンセルする。"
|
||||
following-write: "フォローしたりフォロー解除する。"
|
||||
drive-read: "ドライブを見る。"
|
||||
drive-write: "ドライブを操作する。"
|
||||
notification-read: "通知を見る。"
|
||||
notification-write: "通知を操作する。"
|
||||
account-read: "Afficher les informations du compte"
|
||||
account-write: "Modifications des informations du compte"
|
||||
note-write: "Publications."
|
||||
reaction-write: "Ajout et suppression de réactions."
|
||||
following-write: "S’abonner et se désabonner."
|
||||
drive-read: "Lecture du Drive."
|
||||
drive-write: "Téléversement/suppression des fichiers de votre Lecteur."
|
||||
notification-read: "Lire vos notifications."
|
||||
notification-write: "Gestion de vos notifications."
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
images: "画像"
|
||||
|
||||
@@ -832,7 +832,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
@@ -950,13 +951,20 @@ desktop/views/components/settings.2fa.vue:
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
@@ -966,8 +974,12 @@ common/views/components/drive-settings.vue:
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
@@ -1196,6 +1208,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
|
||||
@@ -1524,6 +1539,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "守護神セキュリティ"
|
||||
signin: "こんな感じでサインインしたらしいで"
|
||||
password: "パスワード"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了したで!"
|
||||
failed: "なんか設定に失敗したで。トークンを間違えとらんか確認してや。"
|
||||
info: "次のサインインからは、パスワードに加えてデバイスに出とるトークンを入力してな。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」っちゅうキーでパラメータに付加してリクエストしてや。"
|
||||
caution: "アカウントを不正利用されるかも知れんから、このトークンは第三者に教えたらあかんで(アプリなどにも入力しんといてな)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたとかその可能性があったらトークンを再生成できるで。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "トークン:"
|
||||
enter-password: "パスワードを入力してや"
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "API使うんやったらこのトークンを「i」っちゅうパラメータにくっつけてリクエストできるで。"
|
||||
caution: "アカウント勝手にいじられるかも知れんから、このトークンは教えたらあかんし、アプリにも書いたらあかんで(これはフリちゃうで)"
|
||||
regeneration-of-token: "トークン漏れてもうたんやったらもっかい生成できるで。"
|
||||
regenerate-token: "トークンもっかい生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入れてや"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送る'
|
||||
sending: '応答待っとる'
|
||||
response: 'こんなん返ってきたわ'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはあらへんで"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
in-use: "使うとる"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはおらんで"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "今のパスワードを入れてや"
|
||||
@@ -938,7 +950,7 @@ desktop/views/pages/admin/admin.vue:
|
||||
dashboard: "ダッシュボード"
|
||||
users: "ユーザー"
|
||||
update: "更新"
|
||||
announcements: "お知らせ"
|
||||
announcements: "知っといてや"
|
||||
hashtags: "ハッシュタグ"
|
||||
desktop/views/pages/admin/admin.dashboard.vue:
|
||||
dashboard: "ダッシュボード"
|
||||
@@ -967,7 +979,7 @@ desktop/views/pages/admin/admin.unverify-user.vue:
|
||||
unverify: "公式アカウントにはさせへんで"
|
||||
unverified: "公式アカウントを解除したで"
|
||||
desktop/views/pages/admin/admin.announcements.vue:
|
||||
announcements: "お知らせ"
|
||||
announcements: "知っといてや"
|
||||
desktop/views/pages/admin/admin.hashtags.vue:
|
||||
hided-tags: "Hidden Tags"
|
||||
desktop/views/pages/deck/deck.tl-column.vue:
|
||||
@@ -979,10 +991,10 @@ desktop/views/pages/deck/deck.user-column.vue:
|
||||
following: "フォロー"
|
||||
followers: "フォロワー"
|
||||
images: "画像"
|
||||
activity: "アクティビティ"
|
||||
activity: "やっとること"
|
||||
timeline: "タイムライン"
|
||||
pinned-notes: "ピン留めされた投稿"
|
||||
push-to-a-list: "リストに追加"
|
||||
pinned-notes: "ピン留めしはった投稿"
|
||||
push-to-a-list: "リストに入れたる"
|
||||
desktop/views/pages/stats/stats.vue:
|
||||
all-users: "全てのユーザー"
|
||||
original-users: "ここの人らだけ"
|
||||
@@ -1035,7 +1047,7 @@ desktop/views/pages/user/user.friends.vue:
|
||||
no-users: "よう話すツレは居らん"
|
||||
desktop/views/pages/user/user.vue:
|
||||
is-suspended: "このユーザーはあかんわ。凍結されとる。"
|
||||
last-used-at: "最終アクセス"
|
||||
last-used-at: "最後いつ来はった?"
|
||||
desktop/views/pages/user/user.photos.vue:
|
||||
title: "写真"
|
||||
loading: "読み込んどります"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしとるで"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加したで。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1117,8 +1132,8 @@ mobile/views/components/drive.file-detail.vue:
|
||||
hash: "ハッシュ(md5)"
|
||||
exif: "EXIF"
|
||||
nsfw: "ちょっと見せられへんわ"
|
||||
mark-as-sensitive: "閲覧注意に設定"
|
||||
unmark-as-sensitive: "閲覧注意を解除"
|
||||
mark-as-sensitive: "見たらあかん感じにしとく"
|
||||
unmark-as-sensitive: "やっぱ見せたるわ"
|
||||
mobile/views/components/media-image.vue:
|
||||
sensitive: "見たらあかんで"
|
||||
click-to-show: "押してみ、見せたるわ"
|
||||
@@ -1312,7 +1327,7 @@ mobile/views/pages/settings.vue:
|
||||
signout: "さいなら"
|
||||
sound: "サウンド"
|
||||
enable-sounds: "サウンド鳴らす"
|
||||
mark-as-read-all-unread-notes: "すべての投稿を既読にする"
|
||||
mark-as-read-all-unread-notes: "全部もう読んだわ"
|
||||
mobile/views/pages/user.vue:
|
||||
follows-you: "フォローされとるで"
|
||||
following: "フォロー"
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーはあかんわ。凍結されとる。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近儲かりまっか?"
|
||||
images: "画像"
|
||||
@@ -1369,27 +1388,27 @@ dev/views/index.vue:
|
||||
manage-apps: "アプリの管理"
|
||||
dev/views/apps.vue:
|
||||
manage-apps: "アプリを管理"
|
||||
create-app: "アプリ作成"
|
||||
app-missing: "アプリなし"
|
||||
create-app: "アプリ作る"
|
||||
app-missing: "アプリあらへん"
|
||||
dev/views/new-app.vue:
|
||||
create-app: "アプリケーションの作成"
|
||||
app-name: "アプリケーション名"
|
||||
app-name-desc: "あなたのアプリの名称。"
|
||||
app-name-ex: "ex) Misskey for iOS"
|
||||
app-overview: "アプリの概要"
|
||||
app-desc: "あなたのアプリの簡単な説明や紹介。"
|
||||
app-desc-ex: "ex) Misskey iOSクライアント。"
|
||||
callback-url: "コールバックURL (オプション)"
|
||||
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
|
||||
create-app: "アプリケーション作る"
|
||||
app-name: "アプリケーションの名前"
|
||||
app-name-desc: "あんたのアプリの名前。"
|
||||
app-name-ex: "ex) 関西ミスキー保安協会"
|
||||
app-overview: "このアプリどんなん?"
|
||||
app-desc: "あんたのアプリどんなんか教えて"
|
||||
app-desc-ex: "ex) 関西人なら誰でも口ずさめるこのCMがついにMisskeyへ。"
|
||||
callback-url: "コールバックURL (無くてもええで)"
|
||||
callback-url-desc: "ユーザーが認証フォームで認証した後どこに連れてくかを設定できるで"
|
||||
authority: "権限"
|
||||
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
|
||||
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"
|
||||
account-read: "アカウントの情報を見る。"
|
||||
account-write: "アカウントの情報を操作する。"
|
||||
note-write: "投稿する。"
|
||||
reaction-write: "リアクションしたりリアクションをキャンセルする。"
|
||||
following-write: "フォローしたりフォロー解除する。"
|
||||
drive-read: "ドライブを見る。"
|
||||
drive-write: "ドライブを操作する。"
|
||||
notification-read: "通知を見る。"
|
||||
notification-write: "通知を操作する。"
|
||||
authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな"
|
||||
authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。"
|
||||
account-read: "アカウントの情報見せて"
|
||||
account-write: "アカウントの情報いじらせて"
|
||||
note-write: "投稿させて"
|
||||
reaction-write: "リアクションしたりそれをキャンセルさせて"
|
||||
following-write: "フォローとかフォロー解除させて"
|
||||
drive-read: "ドライブ見せて"
|
||||
drive-write: "ドライブいじらせて"
|
||||
notification-read: "通知見せて"
|
||||
notification-write: "通知いじらせて"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
images: "画像"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "Profiel"
|
||||
notification: "Melding"
|
||||
apps: "Apps"
|
||||
mute: "Dempen"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "Beveiliging"
|
||||
signin: "Inloggeschiedenis"
|
||||
password: "Wachtwoord"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "Instellen voltooid!"
|
||||
failed: "Instellen mislukt. Zorg ervoor dat de sleutel juist is."
|
||||
info: "Vanaf nu moet je ook de op je apparaat getoonde sleutel tonen bij het inloggen op Misskey."
|
||||
desktop/views/components/settings.api.vue:
|
||||
intro: "Als je toegang wilt tot de API, stel deze sleutel dan in als 'i' bij de verzoekparameters."
|
||||
caution: "Laat deze sleutel niet zien aan derde partijen (en voer hem nergens anders in dan hier), anders kan je account gehackt worden."
|
||||
regeneration-of-token: "Mocht deze sleutel tóch uitlekken, dan kun je hem opnieuw genereren."
|
||||
regenerate-token: "Sleutel opnieuw genereren"
|
||||
token: "Sleutel:"
|
||||
enter-password: "Voer je wachtwoord in"
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "Geen gedempte gebruikers"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "Wachtwoord wijzigen"
|
||||
enter-current-password: "Voer je huidige wachtwoord in"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "Dempen"
|
||||
muted: "Dempend"
|
||||
unmute: "Ontdempen"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "Tijdlijn"
|
||||
media: "Media"
|
||||
is-suspended: "Dit account is geschorst."
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "Recente notities"
|
||||
images: "Afbeeldingen"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "Notifikasjon"
|
||||
apps: "Apper"
|
||||
mute: "Demp"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "Passord"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "Media"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "Nylige innlegg"
|
||||
images: "Bilder"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "Profil"
|
||||
notification: "Powiadomienia"
|
||||
apps: "Aplikacje"
|
||||
mute: "Wyciszanie"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "Bezpieczeństwo"
|
||||
signin: "Historia logowań"
|
||||
password: "Hasło"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "Pomyślnie ukończono konfigurację!"
|
||||
failed: "Nie udało się skonfigurować uwierzytelniania dwuetapowego, upewnij się że wprowadziłeś prawidłowy token."
|
||||
info: "Od teraz, wprowadzaj token wyświetlany na urządzeniu przy każdym logowaniu do Misskey."
|
||||
desktop/views/components/settings.api.vue:
|
||||
intro: "Aby uzyskać dostęp do API, ustaw ten token jako klucz 'i' parametrów żądań."
|
||||
caution: "Nie pokazuj tego tokenu osobom trzecim (nie wprowadzaj go nigdzie indziej), aby konto nie trafiło w niepowołane ręce."
|
||||
regeneration-of-token: "W przypadku wycieku tokenu, możesz wygenerować nowy."
|
||||
regenerate-token: "Wygeneruj nowy token"
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "Wprowadź hasło"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "Brak zautoryzowanych aplikacji"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "Brak wyciszonych użytkowników"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "Zmień hasło"
|
||||
enter-current-password: "Wprowadź obecne hasło"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "Wycisz"
|
||||
muted: "Wyciszyłeś"
|
||||
unmute: "Cofnij wyciszenie"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "Dodaj do listy"
|
||||
list-pushed: "Dodałeś(-aś) {user} do {list}."
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "Oś czasu"
|
||||
media: "Multimedia"
|
||||
is-suspended: "To konto zostało zablokowane"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "Ostatnie wpisy"
|
||||
images: "Zdjęcia"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "Linha do tempo"
|
||||
media: "Mídia"
|
||||
is-suspended: "Esta conta foi suspensa"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "Notas recentes"
|
||||
images: "Imagens"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
images: "画像"
|
||||
|
||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
||||
profile: "プロフィール"
|
||||
notification: "通知"
|
||||
apps: "アプリ"
|
||||
mute: "ミュート"
|
||||
mute-and-block: "ミュート/ブロック"
|
||||
blocking: "ブロック"
|
||||
security: "セキュリティ"
|
||||
signin: "サインイン履歴"
|
||||
password: "パスワード"
|
||||
@@ -847,21 +848,32 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "設定が完了しました!"
|
||||
failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
|
||||
info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
|
||||
desktop/views/components/settings.api.vue:
|
||||
common/views/components/api-settings.vue:
|
||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
||||
regenerate-token: "トークンを再生成"
|
||||
token: "Token:"
|
||||
enter-password: "パスワードを入力してください"
|
||||
console:
|
||||
title: 'APIコンソール'
|
||||
endpoint: 'エンドポイント'
|
||||
parameter: 'パラメータ'
|
||||
send: '送信'
|
||||
sending: '応答待ち'
|
||||
response: '結果'
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "連携しているアプリケーションはありません"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
desktop/views/components/settings.mute.vue:
|
||||
no-users: "ミュートしているユーザーはいません"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
block: "ブロック"
|
||||
no-muted-users: "ミュートしているユーザーはいません"
|
||||
no-blocked-users: "ブロックしているユーザーはいません"
|
||||
desktop/views/components/settings.password.vue:
|
||||
reset: "パスワードを変更する"
|
||||
enter-current-password: "現在のパスワードを入力してください"
|
||||
@@ -1048,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
||||
mute: "ミュートする"
|
||||
muted: "ミュートしています"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロックする"
|
||||
unblock: "ブロック解除"
|
||||
block-confirm: "このユーザーをブロックしますか?"
|
||||
push-to-a-list: "リストに追加"
|
||||
list-pushed: "{user}を{list}に追加しました。"
|
||||
desktop/views/pages/user/user.header.vue:
|
||||
@@ -1322,6 +1337,10 @@ mobile/views/pages/user.vue:
|
||||
timeline: "タイムライン"
|
||||
media: "メディア"
|
||||
is-suspended: "このユーザーは凍結されています。"
|
||||
mute: "ミュート"
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "最近の投稿"
|
||||
images: "画像"
|
||||
|
||||
123
package-lock.json
generated
123
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "10.31.0",
|
||||
"version": "10.36.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -544,9 +544,9 @@
|
||||
}
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.3.tgz",
|
||||
"integrity": "sha512-C1wVVr7xhKu6c3Mb27dFzNYR05qvHwgtpN+JOYTGc1pKA7dCEDDYpscn7kul+bCUwa3NoGDbzI1pdznSOa397w=="
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz",
|
||||
"integrity": "sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww=="
|
||||
},
|
||||
"@types/mongodb": {
|
||||
"version": "3.1.12",
|
||||
@@ -691,9 +691,12 @@
|
||||
"integrity": "sha512-XWwqRvaSsf4yq/4SYL5/7n5i2RWqf+jtIRlasj65cuZZDZ01wtkDfAIxrEpYcLvzT1HMqBmsnMEcZjK+OMeojQ=="
|
||||
},
|
||||
"@types/speakeasy": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.2.tgz",
|
||||
"integrity": "sha512-h8KW3wSd3/l4oKRGYPxExCaos5VmjcnwDG3RK25tfcoWQR9iLmM9UbwvF1Pd+UT5aY1Z3LdQGt4xU0u9Zk/C2Q=="
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.3.tgz",
|
||||
"integrity": "sha512-lDc49Aec4WnQPRhW3n90ct14CH0Zyrj48RGMK1SSQP6BOf8HamWg+PG9uj1DVnaa6t+lhQU1j1lhGA7d9baxWw==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/superagent": {
|
||||
"version": "3.8.4",
|
||||
@@ -1259,9 +1262,9 @@
|
||||
}
|
||||
},
|
||||
"apexcharts": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-2.1.5.tgz",
|
||||
"integrity": "sha512-4eKh2HyQVr5ct2t7cWkvDSUyJM9KGw6dRHAlojqo6HJz+XtrnnvL8uP18rzNq4M80P3Ul+yDjpCt5EXlYPfo5Q==",
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-2.1.9.tgz",
|
||||
"integrity": "sha512-Qs/jLUa03wqPR53yMk8QAwq+qrX/Odc3IIXH2WVVjdWyFXS1lYzGSDbVcVDnOKkxoLdAlzPI3icb2bMjskwfXQ==",
|
||||
"requires": {
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"core-js": "^2.5.7",
|
||||
@@ -3332,12 +3335,12 @@
|
||||
}
|
||||
},
|
||||
"data-urls": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz",
|
||||
"integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
|
||||
"integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
|
||||
"requires": {
|
||||
"abab": "^2.0.0",
|
||||
"whatwg-mimetype": "^2.1.0",
|
||||
"whatwg-mimetype": "^2.2.0",
|
||||
"whatwg-url": "^7.0.0"
|
||||
}
|
||||
},
|
||||
@@ -4755,9 +4758,9 @@
|
||||
}
|
||||
},
|
||||
"eslint": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz",
|
||||
"integrity": "sha512-zYCeFQahsxffGl87U2aJ7DPyH8CbWgxBC213Y8+TCanhUTf2gEvfq3EKpHmEcozTLyPmGe9LZdMAwC/CpJBM5A==",
|
||||
"version": "5.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.8.0.tgz",
|
||||
"integrity": "sha512-Zok6Bru3y2JprqTNm14mgQ15YQu/SMDkWdnmHfFg770DIUlmMFd/gqqzCHekxzjHZJxXv3tmTpH0C1icaYJsRQ==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"ajv": "^6.5.3",
|
||||
@@ -5311,9 +5314,9 @@
|
||||
}
|
||||
},
|
||||
"file-type": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.1.0.tgz",
|
||||
"integrity": "sha512-fkjfXnqBRrdUFTS6opakWyMXb+uzDv8zOCqjSOWPbzMLnYnmnUEv/RNY9igkk4nc8TVL44Xd1OCC2fJXH3eb7Q=="
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.2.0.tgz",
|
||||
"integrity": "sha512-eqX81S1PWdLDPW39yyB214TVVOsUQjSmPcyUjeVH6ksH+94Y2YA/ItiIwa53rJiSofJZLK6lGsuCE3rwt0vp4w=="
|
||||
},
|
||||
"filename-regex": {
|
||||
"version": "2.0.1",
|
||||
@@ -7224,14 +7227,14 @@
|
||||
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
|
||||
},
|
||||
"html-minifier": {
|
||||
"version": "3.5.20",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz",
|
||||
"integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==",
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz",
|
||||
"integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==",
|
||||
"requires": {
|
||||
"camel-case": "3.0.x",
|
||||
"clean-css": "4.2.x",
|
||||
"commander": "2.17.x",
|
||||
"he": "1.1.x",
|
||||
"he": "1.2.x",
|
||||
"param-case": "2.1.x",
|
||||
"relateurl": "0.2.x",
|
||||
"uglify-js": "3.4.x"
|
||||
@@ -7242,14 +7245,10 @@
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
|
||||
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.9",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
|
||||
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
|
||||
"requires": {
|
||||
"commander": "~2.17.1",
|
||||
"source-map": "~0.6.1"
|
||||
}
|
||||
"he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8355,9 +8354,9 @@
|
||||
"integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ=="
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "12.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-12.2.0.tgz",
|
||||
"integrity": "sha512-QPOggIJ8fquWPLaYYMoh+zqUmdphDtu1ju0QGTitZT1Yd8I5qenPpXM1etzUegu3MjVp8XPzgZxdn8Yj7e40ig==",
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.0.0.tgz",
|
||||
"integrity": "sha512-Kmq4ASMNkgpY+YufE322EnIKoiz0UWY2DRkKlU7d5YrIW4xiVRhWFrZV1fr6w/ZNxQ50wGAH5gGRzydgnmkkvw==",
|
||||
"requires": {
|
||||
"abab": "^2.0.0",
|
||||
"acorn": "^6.0.2",
|
||||
@@ -8378,6 +8377,7 @@
|
||||
"symbol-tree": "^3.2.2",
|
||||
"tough-cookie": "^2.4.3",
|
||||
"w3c-hr-time": "^1.0.1",
|
||||
"w3c-xmlserializer": "^1.0.0",
|
||||
"webidl-conversions": "^4.0.2",
|
||||
"whatwg-encoding": "^1.0.5",
|
||||
"whatwg-mimetype": "^2.2.0",
|
||||
@@ -15573,9 +15573,9 @@
|
||||
}
|
||||
},
|
||||
"ts-loader": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.2.2.tgz",
|
||||
"integrity": "sha512-vM/TrEKXBqRYq5yLatsXyKFnYSpv53klmGtrILGlNqcMsxPVi8+e4yr1Agbu9oMZepx/4szDVn5QpFo83IQdQg==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.3.0.tgz",
|
||||
"integrity": "sha512-lGSNs7szRFj/rK9T1EQuayE3QNLg6izDUxt5jpmq0RG1rU2bapAt7E7uLckLCUPeO1jwxCiet2oRaWovc53UAg==",
|
||||
"requires": {
|
||||
"chalk": "^2.3.0",
|
||||
"enhanced-resolve": "^4.0.0",
|
||||
@@ -15687,16 +15687,17 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz",
|
||||
"integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA=="
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.4.tgz",
|
||||
"integrity": "sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q=="
|
||||
},
|
||||
"typescript-eslint-parser": {
|
||||
"version": "20.0.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-20.0.0.tgz",
|
||||
"integrity": "sha512-HZEoGA+LnS3etUlVAPX6I8sZ7872Yb0vPvQv6QDCIE44KD3bFmvPEQ4LbiD+qGkcxh6segjVK0v3rxpb2R6oSA==",
|
||||
"version": "20.1.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-20.1.1.tgz",
|
||||
"integrity": "sha512-IJhpqHK60Pz2J5pe8rJUQ10DcMcGwhQnvRFcPV79coEV3bpNfSiHkgpS+sf6zx2ANDWgBhmtZbK9ICOy+v3FKA==",
|
||||
"requires": {
|
||||
"eslint": "4.19.1",
|
||||
"eslint-visitor-keys": "^1.0.0",
|
||||
"typescript-estree": "2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -15829,7 +15830,7 @@
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
|
||||
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
|
||||
},
|
||||
"ignore": {
|
||||
@@ -15911,6 +15912,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.9",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
|
||||
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
|
||||
"requires": {
|
||||
"commander": "~2.17.1",
|
||||
"source-map": "~0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
|
||||
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"uglify-to-browserify": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
|
||||
@@ -16763,6 +16780,16 @@
|
||||
"browser-process-hrtime": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"w3c-xmlserializer": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.0.0.tgz",
|
||||
"integrity": "sha512-0et1+9uXYiIRAecx1D5Z1nk60+vimniGdIKl4XjeqkWi6acoHNlXMv1VR5jV+jF4ooeO08oWbYxeAJOcon1oMA==",
|
||||
"requires": {
|
||||
"domexception": "^1.0.1",
|
||||
"webidl-conversions": "^4.0.2",
|
||||
"xml-name-validator": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"ware": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz",
|
||||
@@ -16808,9 +16835,9 @@
|
||||
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
|
||||
},
|
||||
"webpack": {
|
||||
"version": "4.23.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.23.0.tgz",
|
||||
"integrity": "sha512-Osh/3U9y4swhEKDjy8fF48v2Qx5VC6VvdQ8bEm1HMaVVddiQBw4+mIyDrzVcVRCPT/+4uJFOcklPuoB+I3Zw0w==",
|
||||
"version": "4.23.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.23.1.tgz",
|
||||
"integrity": "sha512-iE5Cu4rGEDk7ONRjisTOjVHv3dDtcFfwitSxT7evtYj/rANJpt1OuC/Kozh1pBa99AUBr1L/LsaNB+D9Xz3CEg==",
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.7.10",
|
||||
"@webassemblyjs/helper-module-context": "1.7.10",
|
||||
|
||||
33
package.json
33
package.json
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.32.0",
|
||||
"clientVersion": "1.0.11138",
|
||||
"version": "10.37.0",
|
||||
"clientVersion": "1.0.11314",
|
||||
"codename": "nighthike",
|
||||
"main": "./built/index.js",
|
||||
"private": true,
|
||||
@@ -58,7 +58,7 @@
|
||||
"@types/koa__cors": "2.2.3",
|
||||
"@types/minio": "7.0.0",
|
||||
"@types/mkdirp": "0.5.2",
|
||||
"@types/mocha": "5.2.3",
|
||||
"@types/mocha": "5.2.5",
|
||||
"@types/mongodb": "3.1.12",
|
||||
"@types/ms": "0.7.30",
|
||||
"@types/node": "10.12.0",
|
||||
@@ -74,7 +74,7 @@
|
||||
"@types/sharp": "0.21.0",
|
||||
"@types/showdown": "1.7.5",
|
||||
"@types/single-line-log": "1.1.0",
|
||||
"@types/speakeasy": "2.0.2",
|
||||
"@types/speakeasy": "2.0.3",
|
||||
"@types/systeminformation": "3.23.0",
|
||||
"@types/tinycolor2": "1.4.1",
|
||||
"@types/tmp": "0.0.33",
|
||||
@@ -84,7 +84,7 @@
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
"animejs": "2.2.0",
|
||||
"apexcharts": "2.1.5",
|
||||
"apexcharts": "2.1.9",
|
||||
"autobind-decorator": "2.1.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
@@ -108,11 +108,11 @@
|
||||
"elasticsearch": "15.1.1",
|
||||
"emojilib": "2.3.0",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eslint": "5.7.0",
|
||||
"eslint": "5.8.0",
|
||||
"eslint-plugin-vue": "4.7.1",
|
||||
"eventemitter3": "3.1.0",
|
||||
"file-loader": "2.0.0",
|
||||
"file-type": "10.1.0",
|
||||
"file-type": "10.2.0",
|
||||
"fuckadblock": "3.2.1",
|
||||
"gulp": "3.9.1",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
@@ -129,13 +129,13 @@
|
||||
"gulp-uglify": "3.0.1",
|
||||
"gulp-util": "3.0.8",
|
||||
"hard-source-webpack-plugin": "0.12.0",
|
||||
"html-minifier": "3.5.20",
|
||||
"html-minifier": "3.5.21",
|
||||
"http-signature": "1.2.0",
|
||||
"insert-text-at-cursor": "0.1.1",
|
||||
"is-root": "2.0.0",
|
||||
"is-url": "1.2.4",
|
||||
"js-yaml": "3.12.0",
|
||||
"jsdom": "12.2.0",
|
||||
"jsdom": "13.0.0",
|
||||
"json5": "2.1.0",
|
||||
"json5-loader": "1.0.1",
|
||||
"koa": "2.6.1",
|
||||
@@ -201,11 +201,11 @@
|
||||
"textarea-caret": "3.1.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tmp": "0.0.33",
|
||||
"ts-loader": "5.2.2",
|
||||
"ts-loader": "5.3.0",
|
||||
"ts-node": "7.0.1",
|
||||
"tslint": "5.10.0",
|
||||
"typescript": "3.1.3",
|
||||
"typescript-eslint-parser": "20.0.0",
|
||||
"typescript": "3.1.4",
|
||||
"typescript-eslint-parser": "20.1.1",
|
||||
"uglify-es": "3.3.9",
|
||||
"url-loader": "1.1.2",
|
||||
"uuid": "3.3.2",
|
||||
@@ -229,17 +229,10 @@
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.3",
|
||||
"webfinger.js": "2.6.6",
|
||||
"webpack": "4.23.0",
|
||||
"webpack": "4.23.1",
|
||||
"webpack-cli": "3.1.2",
|
||||
"websocket": "1.0.28",
|
||||
"ws": "6.1.0",
|
||||
"xev": "2.0.1"
|
||||
},
|
||||
"greenkeeper": {
|
||||
"ignore": [
|
||||
"deepcopy",
|
||||
"cafy",
|
||||
"@types/gulp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
72
src/client/app/common/views/components/api-settings.vue
Normal file
72
src/client/app/common/views/components/api-settings.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:key% API</div>
|
||||
|
||||
<section class="fit-top">
|
||||
<ui-input :value="$store.state.i.token" readonly>
|
||||
<span>%i18n:@token%</span>
|
||||
</ui-input>
|
||||
<p>%i18n:@intro%</p>
|
||||
<ui-info warn>%i18n:@caution%</ui-info>
|
||||
<p>%i18n:@regeneration-of-token%</p>
|
||||
<ui-button @click="regenerateToken">%fa:sync-alt% %i18n:@regenerate-token%</ui-button>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>%fa:terminal% %i18n:@console.title%</header>
|
||||
<ui-input v-model="endpoint">
|
||||
<span>%i18n:@console.endpoint%</span>
|
||||
</ui-input>
|
||||
<ui-textarea v-model="body">
|
||||
<span>%i18n:@console.parameter% (JSON or JSON5)</span>
|
||||
</ui-textarea>
|
||||
<ui-button @click="send" :disabled="sending">
|
||||
<template v-if="sending">%i18n:@console.sending%</template>
|
||||
<template v-else>%fa:paper-plane% %i18n:@console.send%</template>
|
||||
</ui-button>
|
||||
<ui-textarea v-if="res" v-model="res" readonly tall>
|
||||
<span>%i18n:@console.response%</span>
|
||||
</ui-textarea>
|
||||
</section>
|
||||
</ui-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as JSON5 from 'json5';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
endpoint: '',
|
||||
body: '{}',
|
||||
res: null,
|
||||
sending: false
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
regenerateToken() {
|
||||
(this as any).apis.input({
|
||||
title: '%i18n:@enter-password%',
|
||||
type: 'password'
|
||||
}).then(password => {
|
||||
(this as any).api('i/regenerate_token', {
|
||||
password: password
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
send() {
|
||||
this.sending = true;
|
||||
(this as any).api(this.endpoint, JSON5.parse(this.body)).then(res => {
|
||||
this.sending = false;
|
||||
this.res = JSON5.stringify(res, null, 2);
|
||||
}, err => {
|
||||
this.sending = false;
|
||||
this.res = JSON5.stringify(err, null, 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -14,7 +14,8 @@
|
||||
</ol>
|
||||
<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
|
||||
<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
|
||||
<span class="emoji">{{ emoji.emoji }}</span>
|
||||
<span class="emoji" v-if="emoji.url"><img :src="emoji.url" :alt="emoji.emoji"/></span>
|
||||
<span class="emoji" v-else>{{ emoji.emoji }}</span>
|
||||
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
|
||||
<span class="alias" v-if="emoji.alias">({{ emoji.alias }})</span>
|
||||
</li>
|
||||
@@ -169,22 +170,45 @@ export default Vue.extend({
|
||||
}
|
||||
} else if (this.type == 'emoji') {
|
||||
const matched = [];
|
||||
const max = 30;
|
||||
|
||||
const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis;
|
||||
customEmojis.some(x => {
|
||||
if (x.name.startsWith(this.q)) matched.push({
|
||||
name: x.name,
|
||||
emoji: `:${x.name}:`,
|
||||
url: x.url
|
||||
});
|
||||
return matched.length == max;
|
||||
});
|
||||
customEmojis.some(x => {
|
||||
const alias = (x.aliases || []).find(a => a.startsWith(this.q));
|
||||
if (alias) matched.push({
|
||||
alias: x.name,
|
||||
name: alias,
|
||||
emoji: `:${x.name}:`,
|
||||
url: x.url
|
||||
});
|
||||
return matched.length == max;
|
||||
});
|
||||
|
||||
emjdb.some(x => {
|
||||
if (x.name.indexOf(this.q) == 0 && !x.alias && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
|
||||
return matched.length == 30;
|
||||
return matched.length == max;
|
||||
});
|
||||
if (matched.length < 30) {
|
||||
if (matched.length < max) {
|
||||
emjdb.some(x => {
|
||||
if (x.name.indexOf(this.q) == 0 && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
|
||||
return matched.length == 30;
|
||||
return matched.length == max;
|
||||
});
|
||||
}
|
||||
if (matched.length < 30) {
|
||||
if (matched.length < max) {
|
||||
emjdb.some(x => {
|
||||
if (x.name.indexOf(this.q) > -1 && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
|
||||
return matched.length == 30;
|
||||
return matched.length == max;
|
||||
});
|
||||
}
|
||||
|
||||
this.emojis = matched;
|
||||
}
|
||||
},
|
||||
@@ -340,6 +364,10 @@ export default Vue.extend({
|
||||
margin 0 4px 0 0
|
||||
width 24px
|
||||
|
||||
> img
|
||||
width 24px
|
||||
vertical-align bottom
|
||||
|
||||
.name
|
||||
color var(--autocompleteItemText)
|
||||
|
||||
|
||||
19
src/client/app/common/views/components/error.vue
Normal file
19
src/client/app/common/views/components/error.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="wjqjnyhzogztorhrdgcpqlkxhkmuetgj">
|
||||
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
|
||||
<ui-button @click="() => $emit('retry')">%i18n:common.error.retry%</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.wjqjnyhzogztorhrdgcpqlkxhkmuetgj
|
||||
max-width 350px
|
||||
margin 0 auto
|
||||
padding 32px
|
||||
text-align center
|
||||
color var(--text)
|
||||
|
||||
> p
|
||||
margin 0 0 8px 0
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,8 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
import muteAndBlock from './mute-and-block.vue';
|
||||
import error from './error.vue';
|
||||
import apiSettings from './api-settings.vue';
|
||||
import driveSettings from './drive-settings.vue';
|
||||
import profileEditor from './profile-editor.vue';
|
||||
import noteSkeleton from './note-skeleton.vue';
|
||||
@@ -44,9 +47,13 @@ import uiTextarea from './ui/textarea.vue';
|
||||
import uiSwitch from './ui/switch.vue';
|
||||
import uiRadio from './ui/radio.vue';
|
||||
import uiSelect from './ui/select.vue';
|
||||
import uiInfo from './ui/info.vue';
|
||||
import formButton from './ui/form/button.vue';
|
||||
import formRadio from './ui/form/radio.vue';
|
||||
|
||||
Vue.component('mk-mute-and-block', muteAndBlock);
|
||||
Vue.component('mk-error', error);
|
||||
Vue.component('mk-api-settings', apiSettings);
|
||||
Vue.component('mk-drive-settings', driveSettings);
|
||||
Vue.component('mk-profile-editor', profileEditor);
|
||||
Vue.component('mk-note-skeleton', noteSkeleton);
|
||||
@@ -91,5 +98,6 @@ Vue.component('ui-textarea', uiTextarea);
|
||||
Vue.component('ui-switch', uiSwitch);
|
||||
Vue.component('ui-radio', uiRadio);
|
||||
Vue.component('ui-select', uiSelect);
|
||||
Vue.component('ui-info', uiInfo);
|
||||
Vue.component('form-button', formButton);
|
||||
Vue.component('form-radio', formRadio);
|
||||
|
||||
@@ -3,7 +3,6 @@ import * as emojilib from 'emojilib';
|
||||
import { length } from 'stringz';
|
||||
import parse from '../../../../../mfm/parse';
|
||||
import getAcct from '../../../../../misc/acct/render';
|
||||
import { url } from '../../../config';
|
||||
import MkUrl from './url.vue';
|
||||
import MkGoogle from './google.vue';
|
||||
import { concat } from '../../../../../prelude/array';
|
||||
@@ -186,6 +185,21 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
}
|
||||
|
||||
case 'emoji': {
|
||||
//#region カスタム絵文字
|
||||
const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis;
|
||||
const customEmoji = customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
|
||||
if (customEmoji) {
|
||||
return [createElement('img', {
|
||||
attrs: {
|
||||
src: customEmoji.url,
|
||||
alt: token.emoji,
|
||||
title: token.emoji,
|
||||
style: 'height: 2.5em; vertical-align: middle;'
|
||||
}
|
||||
})];
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const emoji = emojilib.lib[token.emoji];
|
||||
return [createElement('span', emoji ? emoji.char : token.content)];
|
||||
}
|
||||
|
||||
52
src/client/app/common/views/components/mute-and-block.vue
Normal file
52
src/client/app/common/views/components/mute-and-block.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<ui-card>
|
||||
<div slot="title">%fa:ban% %i18n:@mute-and-block%</div>
|
||||
|
||||
<section>
|
||||
<header>%i18n:@mute%</header>
|
||||
<ui-info v-if="!muteFetching && mute.length == 0">%i18n:@no-muted-users%</ui-info>
|
||||
<div class="users" v-if="mute.length != 0">
|
||||
<div v-for="user in mute" :key="user.id">
|
||||
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>%i18n:@block%</header>
|
||||
<ui-info v-if="!blockFetching && block.length == 0">%i18n:@no-blocked-users%</ui-info>
|
||||
<div class="users" v-if="block.length != 0">
|
||||
<div v-for="user in block" :key="user.id">
|
||||
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ui-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
muteFetching: true,
|
||||
blockFetching: true,
|
||||
mute: [],
|
||||
block: []
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
(this as any).api('mute/list').then(mute => {
|
||||
this.mute = mute.map(x => x.mutee);
|
||||
this.muteFetching = false;
|
||||
});
|
||||
|
||||
(this as any).api('blocking/list').then(blocking => {
|
||||
this.block = blocking.map(x => x.blockee);
|
||||
this.blockFetching = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -2,11 +2,11 @@
|
||||
<header class="bvonvjxbwzaiskogyhbwgyxvcgserpmu">
|
||||
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle == 'smart'"/>
|
||||
<router-link class="name" :to="note.user | userPage" v-user-preview="note.user.id">{{ note.user | userName }}</router-link>
|
||||
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
|
||||
<span class="is-admin" v-if="note.user.isAdmin">admin</span>
|
||||
<span class="is-bot" v-if="note.user.isBot">bot</span>
|
||||
<span class="is-cat" v-if="note.user.isCat">cat</span>
|
||||
<span class="username"><mk-acct :user="note.user"/></span>
|
||||
<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
|
||||
<div class="info">
|
||||
<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span>
|
||||
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||
@@ -68,10 +68,6 @@ export default Vue.extend({
|
||||
&:hover
|
||||
text-decoration underline
|
||||
|
||||
> .is-verified
|
||||
margin-right 8px
|
||||
color #4dabf7
|
||||
|
||||
> .is-admin
|
||||
> .is-bot
|
||||
> .is-cat
|
||||
@@ -95,6 +91,10 @@ export default Vue.extend({
|
||||
color var(--noteHeaderAcct)
|
||||
flex-shrink 2147483647
|
||||
|
||||
> .is-verified
|
||||
margin 0 .5em 0 0
|
||||
color #4dabf7
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
||||
43
src/client/app/common/views/components/ui/info.vue
Normal file
43
src/client/app/common/views/components/ui/info.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="ymxyweixqwsxauxldgpvecjepnwxbylu" :class="{ warn }">
|
||||
<i v-if="warn">%fa:exclamation-triangle%</i>
|
||||
<i v-else>%fa:info-circle%</i>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
warn: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.ymxyweixqwsxauxldgpvecjepnwxbylu
|
||||
margin 16px 0
|
||||
padding 16px
|
||||
font-size 90%
|
||||
background var(--infoBg)
|
||||
color var(--infoFg)
|
||||
|
||||
&.warn
|
||||
background var(--infoWarnBg)
|
||||
color var(--infoWarnFg)
|
||||
|
||||
&:first-child
|
||||
margin-top 0
|
||||
|
||||
&:last-child
|
||||
margin-bottom 0
|
||||
|
||||
> i
|
||||
margin-right 4px
|
||||
|
||||
</style>
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div class="ui-textarea" :class="{ focused, filled }">
|
||||
<div class="ui-textarea" :class="{ focused, filled, tall }">
|
||||
<div class="input">
|
||||
<span class="label" ref="label"><slot></slot></span>
|
||||
<textarea ref="input"
|
||||
:value="value"
|
||||
:required="required"
|
||||
:readonly="readonly"
|
||||
:pattern="pattern"
|
||||
:autocomplete="autocomplete"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false">
|
||||
</textarea>
|
||||
:value="value"
|
||||
:required="required"
|
||||
:readonly="readonly"
|
||||
:pattern="pattern"
|
||||
:autocomplete="autocomplete"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="text"><slot name="text"></slot></div>
|
||||
</div>
|
||||
@@ -41,7 +41,12 @@ export default Vue.extend({
|
||||
autocomplete: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
tall: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -66,6 +71,9 @@ export default Vue.extend({
|
||||
root(fill)
|
||||
margin 42px 0 32px 0
|
||||
|
||||
&:last-child
|
||||
margin-bottom 0
|
||||
|
||||
> .input
|
||||
padding 12px
|
||||
|
||||
@@ -157,6 +165,11 @@ root(fill)
|
||||
left 0 !important
|
||||
transform scale(0.75)
|
||||
|
||||
&.tall
|
||||
> .input
|
||||
> textarea
|
||||
min-height 200px
|
||||
|
||||
.ui-textarea.fill
|
||||
root(true)
|
||||
|
||||
|
||||
@@ -222,13 +222,15 @@ class Autocomplete {
|
||||
const trimmedBefore = before.substring(0, before.lastIndexOf(':'));
|
||||
const after = source.substr(caret);
|
||||
|
||||
if (value.startsWith(':')) value = value + ' ';
|
||||
|
||||
// 挿入
|
||||
this.text = trimmedBefore + value + after;
|
||||
|
||||
// キャレットを戻す
|
||||
this.vm.$nextTick(() => {
|
||||
this.textarea.focus();
|
||||
const pos = trimmedBefore.length + 1;
|
||||
const pos = trimmedBefore.length + (value.startsWith(':') ? value.length : 1);
|
||||
this.textarea.setSelectionRange(pos, pos);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@ export default Vue.extend({
|
||||
text: '%i18n:@contextmenu.rename%',
|
||||
icon: '%fa:i-cursor%',
|
||||
action: this.rename
|
||||
}/*, null, {
|
||||
}, null, {
|
||||
type: 'item',
|
||||
text: '%i18n:common.delete%',
|
||||
icon: '%fa:R trash-alt%',
|
||||
action: this.deleteFolder
|
||||
}*/], {
|
||||
}], {
|
||||
closed: () => {
|
||||
this.isContextmenuShowing = false;
|
||||
}
|
||||
@@ -207,7 +207,9 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
deleteFolder() {
|
||||
alert('not implemented yet');
|
||||
(this as any).api('drive/folders/delete', {
|
||||
folderId: this.folder.id
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -98,7 +98,7 @@ export default Vue.extend({
|
||||
hierarchyFolders: [],
|
||||
selectedFiles: [],
|
||||
uploadings: [],
|
||||
connection: null
|
||||
connection: null,
|
||||
|
||||
/**
|
||||
* ドロップされようとしているか
|
||||
@@ -122,6 +122,7 @@ export default Vue.extend({
|
||||
this.connection.on('fileDeleted', this.onStreamDriveFileDeleted);
|
||||
this.connection.on('folderCreated', this.onStreamDriveFolderCreated);
|
||||
this.connection.on('folderUpdated', this.onStreamDriveFolderUpdated);
|
||||
this.connection.on('folderDeleted', this.onStreamDriveFolderDeleted);
|
||||
|
||||
if (this.initFolder) {
|
||||
this.move(this.initFolder);
|
||||
@@ -182,6 +183,10 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
onStreamDriveFolderDeleted(folderId) {
|
||||
this.removeFolder(folderId);
|
||||
},
|
||||
|
||||
onChangeUploaderUploads(uploads) {
|
||||
this.uploadings = uploads;
|
||||
},
|
||||
|
||||
@@ -40,8 +40,8 @@ export default Vue.extend({
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
this.connection.on('follow', this.onFollow);
|
||||
this.connection.on('unfollow', this.onUnfollow);
|
||||
this.connection.on('follow', this.onFollowChange);
|
||||
this.connection.on('unfollow', this.onFollowChange);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
@@ -49,17 +49,11 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
methods: {
|
||||
onFollow(user) {
|
||||
if (user.id == this.u.id) {
|
||||
this.u.isFollowing = user.isFollowing;
|
||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
}
|
||||
},
|
||||
|
||||
onUnfollow(user) {
|
||||
onFollowChange(user) {
|
||||
if (user.id == this.u.id) {
|
||||
this.u.isFollowing = user.isFollowing;
|
||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
|
||||
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
|
||||
|
||||
<div v-if="!fetching && requestInitPromise != null" class="error">
|
||||
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
|
||||
<ui-button @click="resolveInitPromise">%i18n:common.error.retry%</ui-button>
|
||||
</div>
|
||||
<mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
|
||||
|
||||
<div class="placeholder" v-if="fetching">
|
||||
<template v-for="i in 10">
|
||||
@@ -215,16 +212,6 @@ export default Vue.extend({
|
||||
> *
|
||||
transition transform .3s ease, opacity .3s ease
|
||||
|
||||
> .error
|
||||
max-width 300px
|
||||
margin 0 auto
|
||||
padding 32px
|
||||
text-align center
|
||||
color var(--text)
|
||||
|
||||
> p
|
||||
margin 0 0 8px 0
|
||||
|
||||
> .placeholder
|
||||
padding 32px
|
||||
opacity 0.3
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="2fa">
|
||||
<p>%i18n:@intro%<a href="%i18n:@url%" target="_blank">%i18n:@detail%</a></p>
|
||||
<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:@caution%</p></div>
|
||||
<p style="margin-top:0;">%i18n:@intro%<a href="%i18n:@url%" target="_blank">%i18n:@detail%</a></p>
|
||||
<ui-info warn>%i18n:@caution%</ui-info>
|
||||
<p v-if="!data && !$store.state.i.twoFactorEnabled"><ui-button @click="register">%i18n:@register%</ui-button></p>
|
||||
<template v-if="$store.state.i.twoFactorEnabled">
|
||||
<p>%i18n:@already-registered%</p>
|
||||
@@ -72,9 +72,3 @@ export default Vue.extend({
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.2fa
|
||||
color #4a535a
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<template>
|
||||
<div class="root api">
|
||||
<ui-input :value="$store.state.i.token" readonly>
|
||||
<span>%i18n:@token%</span>
|
||||
</ui-input>
|
||||
<p>%i18n:@intro%</p>
|
||||
<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:@caution%</p></div>
|
||||
<p>%i18n:@regeneration-of-token%</p>
|
||||
<ui-button @click="regenerateToken">%i18n:@regenerate-token%</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
methods: {
|
||||
regenerateToken() {
|
||||
(this as any).apis.input({
|
||||
title: '%i18n:@enter-password%',
|
||||
type: 'password'
|
||||
}).then(password => {
|
||||
(this as any).api('i/regenerate_token', {
|
||||
password: password
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.root.api
|
||||
code
|
||||
display inline-block
|
||||
padding 4px 6px
|
||||
color #555
|
||||
background #eee
|
||||
border-radius 2px
|
||||
</style>
|
||||
@@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<div class="root">
|
||||
<div class="none ui info" v-if="!fetching && apps.length == 0">
|
||||
<p>%fa:info-circle%%i18n:@no-apps%</p>
|
||||
</div>
|
||||
<ui-info v-if="!fetching && apps.length == 0">%i18n:@no-apps%</ui-info>
|
||||
<div class="apps" v-if="apps.length != 0">
|
||||
<div v-for="app in apps">
|
||||
<p><b>{{ app.name }}</b></p>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="none ui info" v-if="!fetching && users.length == 0">
|
||||
<p>%fa:info-circle%%i18n:@no-users%</p>
|
||||
</div>
|
||||
<div class="users" v-if="users.length != 0">
|
||||
<div v-for="user in users" :key="user.id">
|
||||
<p><b>{{ user | userName }}</b> @{{ user | acct }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
users: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
(this as any).api('mute/list').then(x => {
|
||||
this.users = x.users;
|
||||
this.fetching = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -7,7 +7,7 @@
|
||||
<p :class="{ active: page == 'notification' }" @mousedown="page = 'notification'">%fa:R bell .fw%%i18n:@notification%</p>
|
||||
<p :class="{ active: page == 'drive' }" @mousedown="page = 'drive'">%fa:cloud .fw%%i18n:common.drive%</p>
|
||||
<p :class="{ active: page == 'hashtags' }" @mousedown="page = 'hashtags'">%fa:hashtag .fw%%i18n:@tags%</p>
|
||||
<p :class="{ active: page == 'mute' }" @mousedown="page = 'mute'">%fa:ban .fw%%i18n:@mute%</p>
|
||||
<p :class="{ active: page == 'muteAndBlock' }" @mousedown="page = 'muteAndBlock'">%fa:ban .fw%%i18n:@mute-and-block%</p>
|
||||
<p :class="{ active: page == 'apps' }" @mousedown="page = 'apps'">%fa:puzzle-piece .fw%%i18n:@apps%</p>
|
||||
<p :class="{ active: page == 'security' }" @mousedown="page = 'security'">%fa:unlock-alt .fw%%i18n:@security%</p>
|
||||
<p :class="{ active: page == 'api' }" @mousedown="page = 'api'">%fa:key .fw%API</p>
|
||||
@@ -200,12 +200,9 @@
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card class="mute" v-show="page == 'mute'">
|
||||
<div slot="title">%fa:ban% %i18n:@mute%</div>
|
||||
<section>
|
||||
<x-mute/>
|
||||
</section>
|
||||
</ui-card>
|
||||
<div class="muteAndBlock" v-show="page == 'muteAndBlock'">
|
||||
<mk-mute-and-block/>
|
||||
</div>
|
||||
|
||||
<ui-card class="apps" v-show="page == 'apps'">
|
||||
<div slot="title">%fa:puzzle-piece% %i18n:@apps%</div>
|
||||
@@ -235,12 +232,9 @@
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card class="api" v-show="page == 'api'">
|
||||
<div slot="title">%fa:key% API</div>
|
||||
<section class="fit-top">
|
||||
<x-api/>
|
||||
</section>
|
||||
</ui-card>
|
||||
<div class="api" v-show="page == 'api'">
|
||||
<mk-api-settings/>
|
||||
</div>
|
||||
|
||||
<ui-card class="other" v-show="page == 'other'">
|
||||
<div slot="title">%fa:info-circle% %i18n:@about%</div>
|
||||
@@ -292,10 +286,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XMute from './settings.mute.vue';
|
||||
import XPassword from './settings.password.vue';
|
||||
import X2fa from './settings.2fa.vue';
|
||||
import XApi from './settings.api.vue';
|
||||
import XApps from './settings.apps.vue';
|
||||
import XSignins from './settings.signins.vue';
|
||||
import XTags from './settings.tags.vue';
|
||||
@@ -304,10 +296,8 @@ import checkForUpdate from '../../../common/scripts/check-for-update';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XMute,
|
||||
XPassword,
|
||||
X2fa,
|
||||
XApi,
|
||||
XApps,
|
||||
XSignins,
|
||||
XTags
|
||||
|
||||
@@ -287,7 +287,7 @@ export default Vue.extend({
|
||||
|
||||
e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
|
||||
|
||||
if (!this.dragging) this.draghover = true;
|
||||
if (!this.dragging && isDeckColumn) this.draghover = true;
|
||||
},
|
||||
|
||||
onDragleave() {
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="!fetching && requestInitPromise != null" class="error">
|
||||
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
|
||||
<ui-button @click="resolveInitPromise">%i18n:common.error.retry%</ui-button>
|
||||
</div>
|
||||
<mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
|
||||
|
||||
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
||||
<!--<transition-group name="mk-notes" class="transition" ref="notes">-->
|
||||
@@ -221,13 +218,6 @@ export default Vue.extend({
|
||||
> *
|
||||
transition transform .3s ease, opacity .3s ease
|
||||
|
||||
> .error
|
||||
max-width 300px
|
||||
margin 0 auto
|
||||
padding 16px
|
||||
text-align center
|
||||
color var(--text)
|
||||
|
||||
> .placeholder
|
||||
padding 16px
|
||||
opacity 0.3
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
<div class="action-form">
|
||||
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
|
||||
<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
|
||||
<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
|
||||
<span v-else>%fa:eye-slash% %i18n:@mute%</span>
|
||||
</ui-button>
|
||||
<ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
|
||||
<span v-if="user.isBlocking">%fa:user% %i18n:@unblock%</span>
|
||||
<span v-else>%fa:user-slash% %i18n:@block%</span>
|
||||
</ui-button>
|
||||
<ui-button @click="list">%fa:list% %i18n:@push-to-a-list%</ui-button>
|
||||
</div>
|
||||
@@ -66,6 +70,27 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
block() {
|
||||
if (!window.confirm('%i18n:@block-confirm%')) return;
|
||||
(this as any).api('blocking/create', {
|
||||
userId: this.user.id
|
||||
}).then(() => {
|
||||
this.user.isBlocking = true;
|
||||
}, () => {
|
||||
alert('error');
|
||||
});
|
||||
},
|
||||
|
||||
unblock() {
|
||||
(this as any).api('blocking/delete', {
|
||||
userId: this.user.id
|
||||
}).then(() => {
|
||||
this.user.isBlocking = false;
|
||||
}, () => {
|
||||
alert('error');
|
||||
});
|
||||
},
|
||||
|
||||
list() {
|
||||
const w = (this as any).os.new(MkUserListsWindow);
|
||||
w.$once('choosen', async list => {
|
||||
@@ -114,7 +139,6 @@ export default Vue.extend({
|
||||
> .action-form
|
||||
padding 16px
|
||||
text-align center
|
||||
border-bottom solid 1px var(--faceDivider)
|
||||
|
||||
> *
|
||||
width 100%
|
||||
|
||||
@@ -510,6 +510,14 @@ export default class MiOS extends EventEmitter {
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Misskeyのメタ情報を取得します
|
||||
*/
|
||||
@autobind
|
||||
public getMetaSync() {
|
||||
return this.meta ? this.meta.data : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Misskeyのメタ情報を取得します
|
||||
* @param force キャッシュを無視するか否か
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
user: {
|
||||
@@ -24,6 +25,7 @@ export default Vue.extend({
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
u: this.user,
|
||||
@@ -31,28 +33,24 @@ export default Vue.extend({
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||
|
||||
this.connection.on('follow', this.onFollow);
|
||||
this.connection.on('unfollow', this.onUnfollow);
|
||||
this.connection.on('follow', this.onFollowChange);
|
||||
this.connection.on('unfollow', this.onFollowChange);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.connection.dispose();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
onFollow(user) {
|
||||
if (user.id == this.u.id) {
|
||||
this.u.isFollowing = user.isFollowing;
|
||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
}
|
||||
},
|
||||
|
||||
onUnfollow(user) {
|
||||
onFollowChange(user) {
|
||||
if (user.id == this.u.id) {
|
||||
this.u.isFollowing = user.isFollowing;
|
||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -90,8 +88,6 @@ export default Vue.extend({
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
|
||||
.mk-follow-button
|
||||
display block
|
||||
user-select none
|
||||
|
||||
@@ -12,7 +12,6 @@ import noteCard from './note-card.vue';
|
||||
import userCard from './user-card.vue';
|
||||
import noteDetail from './note-detail.vue';
|
||||
import followButton from './follow-button.vue';
|
||||
import muteButton from './mute-button.vue';
|
||||
import friendsMaker from './friends-maker.vue';
|
||||
import notification from './notification.vue';
|
||||
import notifications from './notifications.vue';
|
||||
@@ -37,7 +36,6 @@ Vue.component('mk-note-card', noteCard);
|
||||
Vue.component('mk-user-card', userCard);
|
||||
Vue.component('mk-note-detail', noteDetail);
|
||||
Vue.component('mk-follow-button', followButton);
|
||||
Vue.component('mk-mute-button', muteButton);
|
||||
Vue.component('mk-friends-maker', friendsMaker);
|
||||
Vue.component('mk-notification', notification);
|
||||
Vue.component('mk-notifications', notifications);
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
<template>
|
||||
<button
|
||||
class="mk-mute-button"
|
||||
:class="{ active: user.isMuted }"
|
||||
@click="onClick">
|
||||
<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
|
||||
<span v-else>%fa:eye% %i18n:@unmute%</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue'
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
if (!this.user.isMuted) {
|
||||
this.mute();
|
||||
} else {
|
||||
this.unmute();
|
||||
}
|
||||
},
|
||||
mute() {
|
||||
(this as any).api('mute/create', { userId: this.user.id})
|
||||
.then(() => { this.user.isMuted = true })
|
||||
.catch(() => { alert('error')})
|
||||
},
|
||||
unmute() {
|
||||
(this as any).api('mute/delete', { userId: this.user.id })
|
||||
.then(() => { this.user.isMuted = false })
|
||||
.catch(() => { alert('error') })
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
|
||||
.mk-mute-button
|
||||
display block
|
||||
user-select none
|
||||
cursor pointer
|
||||
padding 0 16px
|
||||
margin 0
|
||||
min-width 100px
|
||||
line-height 36px
|
||||
font-size 14px
|
||||
font-weight bold
|
||||
color 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)
|
||||
|
||||
</style>
|
||||
@@ -26,6 +26,9 @@
|
||||
<ui-switch v-model="iLikeSushi">%i18n:common.i-like-sushi%</ui-switch>
|
||||
<ui-switch v-model="disableAnimatedMfm">%i18n:common.disable-animated-mfm%</ui-switch>
|
||||
<ui-switch v-model="alwaysShowNsfw">%i18n:common.always-show-nsfw% (%i18n:common.this-setting-is-this-device-only%)</ui-switch>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<ui-switch v-model="games_reversi_showBoardLabels">%i18n:common.show-reversi-board-labels%</ui-switch>
|
||||
<ui-switch v-model="games_reversi_useContrastStones">%i18n:common.use-contrast-reversi-stones%</ui-switch>
|
||||
</section>
|
||||
@@ -82,6 +85,8 @@
|
||||
|
||||
<mk-drive-settings/>
|
||||
|
||||
<mk-mute-and-block/>
|
||||
|
||||
<ui-card>
|
||||
<div slot="title">%fa:volume-up% %i18n:@sound%</div>
|
||||
|
||||
@@ -120,6 +125,8 @@
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<mk-api-settings />
|
||||
|
||||
<ui-card>
|
||||
<div slot="title">%fa:sync-alt% %i18n:@update%</div>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<a class="avatar">
|
||||
<img :src="user.avatarUrl" alt="avatar"/>
|
||||
</a>
|
||||
<mk-mute-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
|
||||
<button class="menu" ref="menu" @click="menu">%fa:ellipsis-h%</button>
|
||||
<mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
|
||||
</div>
|
||||
<div class="title">
|
||||
@@ -67,6 +67,7 @@ import Vue from 'vue';
|
||||
import * as age from 's-age';
|
||||
import parseAcct from '../../../../../misc/acct/parse';
|
||||
import Progress from '../../../common/scripts/loading';
|
||||
import Menu from '../../../common/views/components/menu.vue';
|
||||
import XHome from './user/home.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
@@ -109,7 +110,61 @@ export default Vue.extend({
|
||||
Progress.done();
|
||||
document.title = `${Vue.filter('userName')(this.user)} | ${(this as any).os.instanceName}`;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
menu() {
|
||||
let menu = [{
|
||||
icon: this.user.isMuted ? '%fa:eye%' : '%fa:eye-slash%',
|
||||
text: this.user.isMuted ? '%i18n:@unmute%' : '%i18n:@mute%',
|
||||
action: () => {
|
||||
if (this.user.isMuted) {
|
||||
(this as any).api('mute/delete', {
|
||||
userId: this.user.id
|
||||
}).then(() => {
|
||||
this.user.isMuted = false;
|
||||
}, () => {
|
||||
alert('error');
|
||||
});
|
||||
} else {
|
||||
(this as any).api('mute/create', {
|
||||
userId: this.user.id
|
||||
}).then(() => {
|
||||
this.user.isMuted = true;
|
||||
}, () => {
|
||||
alert('error');
|
||||
});
|
||||
}
|
||||
}
|
||||
}, {
|
||||
icon: this.user.isBlocking ? '%fa:user%' : '%fa:user-slash%',
|
||||
text: this.user.isBlocking ? '%i18n:@unblock%' : '%i18n:@block%',
|
||||
action: () => {
|
||||
if (this.user.isBlocking) {
|
||||
(this as any).api('blocking/delete', {
|
||||
userId: this.user.id
|
||||
}).then(() => {
|
||||
this.user.isBlocking = false;
|
||||
}, () => {
|
||||
alert('error');
|
||||
});
|
||||
} else {
|
||||
(this as any).api('blocking/create', {
|
||||
userId: this.user.id
|
||||
}).then(() => {
|
||||
this.user.isBlocking = true;
|
||||
}, () => {
|
||||
alert('error');
|
||||
});
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
this.os.new(Menu, {
|
||||
source: this.$refs.menu,
|
||||
compact: true,
|
||||
items: menu
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -156,14 +211,10 @@ main
|
||||
max-width 600px
|
||||
|
||||
> .top
|
||||
&:after
|
||||
content ''
|
||||
display block
|
||||
clear both
|
||||
display flex
|
||||
|
||||
> .avatar
|
||||
display block
|
||||
float left
|
||||
width 25%
|
||||
height 40px
|
||||
|
||||
@@ -183,11 +234,15 @@ main
|
||||
border 4px solid $bg
|
||||
border-radius 12px
|
||||
|
||||
> .mk-mute-button
|
||||
float right
|
||||
> .menu
|
||||
margin 0 0 0 auto
|
||||
padding 8px
|
||||
margin-right 8px
|
||||
font-size 18px
|
||||
color var(--text)
|
||||
|
||||
> .mk-follow-button
|
||||
float right
|
||||
margin 0
|
||||
|
||||
> .title
|
||||
margin 8px 0
|
||||
|
||||
@@ -131,6 +131,11 @@
|
||||
remoteInfoBg: '#42321c',
|
||||
remoteInfoFg: '#ffbd3e',
|
||||
|
||||
infoBg: '#253142',
|
||||
infoFg: '#fff',
|
||||
infoWarnBg: '#42321c',
|
||||
infoWarnFg: '#ffbd3e',
|
||||
|
||||
messagingRoomBg: '@bg',
|
||||
messagingRoomInfo: '#fff',
|
||||
messagingRoomDateDividerLine: 'rgba(255, 255, 255, 0.1)',
|
||||
|
||||
@@ -131,6 +131,11 @@
|
||||
remoteInfoBg: '#fff0db',
|
||||
remoteInfoFg: '#573c08',
|
||||
|
||||
infoBg: '#e5f5ff',
|
||||
infoFg: '#72818a',
|
||||
infoWarnBg: '#fff0db',
|
||||
infoWarnFg: '#573c08',
|
||||
|
||||
messagingRoomBg: '#fff',
|
||||
messagingRoomInfo: '#000',
|
||||
messagingRoomDateDividerLine: 'rgba(0, 0, 0, 0.1)',
|
||||
|
||||
@@ -14,11 +14,13 @@ export type Source = {
|
||||
* メンテナの連絡先(URLかmailto形式のURL)
|
||||
*/
|
||||
url: string;
|
||||
email?: string;
|
||||
repository_url?: string;
|
||||
feedback_url?: string;
|
||||
};
|
||||
name?: string;
|
||||
description?: string;
|
||||
languages?: string[];
|
||||
welcome_bg_url?: string;
|
||||
url: string;
|
||||
port: number;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
|
||||
```
|
||||
%URL%/streaming?i=xxxxxxxxxxxxxxx
|
||||
%WS_URL%/streaming?i=xxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
|
||||
@@ -22,7 +22,7 @@
|
||||
認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
|
||||
|
||||
```
|
||||
%URL%/streaming
|
||||
%WS_URL%/streaming
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -9,9 +9,9 @@ export type TextElementHashtag = {
|
||||
};
|
||||
|
||||
export default function(text: string, i: number) {
|
||||
if (!(/^\s#[^\s\.,!\?]+/.test(text) || (i == 0 && /^#[^\s\.,!\?]+/.test(text)))) return null;
|
||||
if (!(/^\s#[^\s\.,!\?#]+/.test(text) || (i == 0 && /^#[^\s\.,!\?#]+/.test(text)))) return null;
|
||||
const isHead = text.startsWith('#');
|
||||
const hashtag = text.match(/^\s?#[^\s\.,!\?]+/)[0];
|
||||
const hashtag = text.match(/^\s?#[^\s\.,!\?#]+/)[0];
|
||||
const res: any[] = !isHead ? [{
|
||||
type: 'text',
|
||||
content: text[0]
|
||||
|
||||
@@ -17,7 +17,8 @@ export default function(text: string, index: number) {
|
||||
const quote = match[1]
|
||||
.split('\n')
|
||||
.map(line => line.replace(/^>+/g, '').trim())
|
||||
.join('\n');
|
||||
.join('\n')
|
||||
.trim();
|
||||
|
||||
return {
|
||||
type: 'quote',
|
||||
|
||||
20
src/misc/get-drive-file-url.ts
Normal file
20
src/misc/get-drive-file-url.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { IDriveFile } from '../models/drive-file';
|
||||
import config from '../config';
|
||||
|
||||
export default function(file: IDriveFile, thumbnail = false): string {
|
||||
if (file == null) return null;
|
||||
|
||||
if (file.metadata.withoutChunks) {
|
||||
if (thumbnail) {
|
||||
return file.metadata.thumbnailUrl || file.metadata.url;
|
||||
} else {
|
||||
return file.metadata.url;
|
||||
}
|
||||
} else {
|
||||
if (thumbnail) {
|
||||
return `${config.drive_url}/${file._id}?thumbnail`;
|
||||
} else {
|
||||
return `${config.drive_url}/${file._id}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
|
||||
const AccessToken = db.get<IAccessToken>('accessTokens');
|
||||
AccessToken.createIndex('token');
|
||||
@@ -15,30 +14,3 @@ export type IAccessToken = {
|
||||
token: string;
|
||||
hash: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* AccessTokenを物理削除します
|
||||
*/
|
||||
export async function deleteAccessToken(accessToken: string | mongo.ObjectID | IAccessToken) {
|
||||
let a: IAccessToken;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(accessToken)) {
|
||||
a = await AccessToken.findOne({
|
||||
_id: accessToken
|
||||
});
|
||||
} else if (typeof accessToken === 'string') {
|
||||
a = await AccessToken.findOne({
|
||||
_id: new mongo.ObjectID(accessToken)
|
||||
});
|
||||
} else {
|
||||
a = accessToken as IAccessToken;
|
||||
}
|
||||
|
||||
if (a == null) return;
|
||||
|
||||
// このAccessTokenを削除
|
||||
await AccessToken.remove({
|
||||
_id: a._id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,27 +22,28 @@ export type IApp = {
|
||||
|
||||
/**
|
||||
* Pack an app for API response
|
||||
*
|
||||
* @param {any} app
|
||||
* @param {any} me?
|
||||
* @param {any} options?
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
export const pack = (
|
||||
app: any,
|
||||
me?: any,
|
||||
options?: {
|
||||
detail?: boolean,
|
||||
includeSecret?: boolean,
|
||||
includeProfileImageIds?: boolean
|
||||
}
|
||||
) => new Promise<any>(async (resolve, reject) => {
|
||||
const opts = options || {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
includeSecret: false,
|
||||
includeProfileImageIds: false
|
||||
};
|
||||
}, options);
|
||||
|
||||
let _app: any;
|
||||
|
||||
const fields = opts.detail ? {} : {
|
||||
name: true
|
||||
};
|
||||
|
||||
// Populate the app if 'app' is ID
|
||||
if (isObjectId(app)) {
|
||||
_app = await App.findOne({
|
||||
@@ -51,7 +52,7 @@ export const pack = (
|
||||
} else if (typeof app === 'string') {
|
||||
_app = await App.findOne({
|
||||
_id: new mongo.ObjectID(app)
|
||||
});
|
||||
}, { fields });
|
||||
} else {
|
||||
_app = deepcopy(app);
|
||||
}
|
||||
|
||||
56
src/models/blocking.ts
Normal file
56
src/models/blocking.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
const deepcopy = require('deepcopy');
|
||||
import { pack as packUser, IUser } from './user';
|
||||
|
||||
const Blocking = db.get<IBlocking>('blocking');
|
||||
Blocking.createIndex('blockerId');
|
||||
Blocking.createIndex('blockeeId');
|
||||
Blocking.createIndex(['blockerId', 'blockeeId'], { unique: true });
|
||||
export default Blocking;
|
||||
|
||||
export type IBlocking = {
|
||||
_id: mongo.ObjectID;
|
||||
createdAt: Date;
|
||||
blockeeId: mongo.ObjectID;
|
||||
blockerId: mongo.ObjectID;
|
||||
};
|
||||
|
||||
export const packMany = (
|
||||
blockings: (string | mongo.ObjectID | IBlocking)[],
|
||||
me?: string | mongo.ObjectID | IUser
|
||||
) => {
|
||||
return Promise.all(blockings.map(x => pack(x, me)));
|
||||
};
|
||||
|
||||
export const pack = (
|
||||
blocking: any,
|
||||
me?: any
|
||||
) => new Promise<any>(async (resolve, reject) => {
|
||||
let _blocking: any;
|
||||
|
||||
// Populate the blocking if 'blocking' is ID
|
||||
if (isObjectId(blocking)) {
|
||||
_blocking = await Blocking.findOne({
|
||||
_id: blocking
|
||||
});
|
||||
} else if (typeof blocking === 'string') {
|
||||
_blocking = await Blocking.findOne({
|
||||
_id: new mongo.ObjectID(blocking)
|
||||
});
|
||||
} else {
|
||||
_blocking = deepcopy(blocking);
|
||||
}
|
||||
|
||||
// Rename _id to id
|
||||
_blocking.id = _blocking._id;
|
||||
delete _blocking._id;
|
||||
|
||||
// Populate blockee
|
||||
_blocking.blockee = await packUser(_blocking.blockeeId, me, {
|
||||
detail: true
|
||||
});
|
||||
|
||||
resolve(_blocking);
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import monkDb, { nativeDbConn } from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
|
||||
const DriveFileThumbnail = monkDb.get<IDriveFileThumbnail>('driveFileThumbnails.files');
|
||||
DriveFileThumbnail.createIndex('metadata.originalId', { sparse: true, unique: true });
|
||||
@@ -28,35 +27,3 @@ export type IDriveFileThumbnail = {
|
||||
contentType: string;
|
||||
metadata: IMetadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* DriveFileThumbnailを物理削除します
|
||||
*/
|
||||
export async function deleteDriveFileThumbnail(driveFile: string | mongo.ObjectID | IDriveFileThumbnail) {
|
||||
let d: IDriveFileThumbnail;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(driveFile)) {
|
||||
d = await DriveFileThumbnail.findOne({
|
||||
_id: driveFile
|
||||
});
|
||||
} else if (typeof driveFile === 'string') {
|
||||
d = await DriveFileThumbnail.findOne({
|
||||
_id: new mongo.ObjectID(driveFile)
|
||||
});
|
||||
} else {
|
||||
d = driveFile as IDriveFileThumbnail;
|
||||
}
|
||||
|
||||
if (d == null) return;
|
||||
|
||||
// このDriveFileThumbnailのチャンクをすべて削除
|
||||
await DriveFileThumbnailChunk.remove({
|
||||
files_id: d._id
|
||||
});
|
||||
|
||||
// このDriveFileThumbnailを削除
|
||||
await DriveFileThumbnail.remove({
|
||||
_id: d._id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import * as mongo from 'mongodb';
|
||||
const deepcopy = require('deepcopy');
|
||||
import { pack as packFolder } from './drive-folder';
|
||||
import config from '../config';
|
||||
import monkDb, { nativeDbConn } from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
import Note, { deleteNote } from './note';
|
||||
import MessagingMessage, { deleteMessagingMessage } from './messaging-message';
|
||||
import User from './user';
|
||||
import DriveFileThumbnail, { deleteDriveFileThumbnail } from './drive-file-thumbnail';
|
||||
import getDriveFileUrl from '../misc/get-drive-file-url';
|
||||
|
||||
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
|
||||
DriveFile.createIndex('md5');
|
||||
@@ -37,7 +33,14 @@ export type IMetadata = {
|
||||
thumbnailUrl?: string;
|
||||
src?: string;
|
||||
deletedAt?: Date;
|
||||
|
||||
/**
|
||||
* このファイルの中身データがMongoDB内に保存されているのか否か
|
||||
* オブジェクトストレージを利用している or リモートサーバーへの直リンクである
|
||||
* な場合は false になります
|
||||
*/
|
||||
withoutChunks?: boolean;
|
||||
|
||||
storage?: string;
|
||||
storageProps?: any;
|
||||
isSensitive?: boolean;
|
||||
@@ -77,71 +80,13 @@ export function validateFileName(name: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* DriveFileを物理削除します
|
||||
*/
|
||||
export async function deleteDriveFile(driveFile: string | mongo.ObjectID | IDriveFile) {
|
||||
let d: IDriveFile;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(driveFile)) {
|
||||
d = await DriveFile.findOne({
|
||||
_id: driveFile
|
||||
});
|
||||
} else if (typeof driveFile === 'string') {
|
||||
d = await DriveFile.findOne({
|
||||
_id: new mongo.ObjectID(driveFile)
|
||||
});
|
||||
} else {
|
||||
d = driveFile as IDriveFile;
|
||||
}
|
||||
|
||||
if (d == null) return;
|
||||
|
||||
// このDriveFileを添付しているNoteをすべて削除
|
||||
await Promise.all((
|
||||
await Note.find({ fileIds: d._id })
|
||||
).map(x => deleteNote(x)));
|
||||
|
||||
// このDriveFileを添付しているMessagingMessageをすべて削除
|
||||
await Promise.all((
|
||||
await MessagingMessage.find({ fileId: d._id })
|
||||
).map(x => deleteMessagingMessage(x)));
|
||||
|
||||
// このDriveFileがアバターやバナーに使われていたらそれらのプロパティをnullにする
|
||||
const u = await User.findOne({ _id: d.metadata.userId });
|
||||
if (u) {
|
||||
if (u.avatarId && u.avatarId.equals(d._id)) {
|
||||
await User.update({ _id: u._id }, { $set: { avatarId: null } });
|
||||
}
|
||||
if (u.bannerId && u.bannerId.equals(d._id)) {
|
||||
await User.update({ _id: u._id }, { $set: { bannerId: null } });
|
||||
}
|
||||
}
|
||||
|
||||
// このDriveFileのDriveFileThumbnailをすべて削除
|
||||
await Promise.all((
|
||||
await DriveFileThumbnail.find({ 'metadata.originalId': d._id })
|
||||
).map(x => deleteDriveFileThumbnail(x)));
|
||||
|
||||
// このDriveFileのチャンクをすべて削除
|
||||
await DriveFileChunk.remove({
|
||||
files_id: d._id
|
||||
});
|
||||
|
||||
// このDriveFileを削除
|
||||
await DriveFile.remove({
|
||||
_id: d._id
|
||||
});
|
||||
}
|
||||
|
||||
export const packMany = async (
|
||||
export const packMany = (
|
||||
files: any[],
|
||||
options?: {
|
||||
detail: boolean
|
||||
}
|
||||
) => {
|
||||
return (await Promise.all(files.map(f => pack(f, options)))).filter(x => x != null);
|
||||
return Promise.all(files.map(f => pack(f, options)));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -190,8 +135,8 @@ export const pack = (
|
||||
|
||||
_target = Object.assign(_target, _file.metadata);
|
||||
|
||||
_target.url = _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
|
||||
_target.thumbnailUrl = _file.metadata.thumbnailUrl ? _file.metadata.thumbnailUrl : _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}?thumbnail`;
|
||||
_target.url = getDriveFileUrl(_file);
|
||||
_target.thumbnailUrl = getDriveFileUrl(_file, true);
|
||||
_target.isRemote = _file.metadata.isRemote;
|
||||
|
||||
if (_target.properties == null) _target.properties = {};
|
||||
@@ -218,6 +163,7 @@ export const pack = (
|
||||
delete _target.storage;
|
||||
delete _target.storageProps;
|
||||
delete _target.isRemote;
|
||||
delete _target._user;
|
||||
|
||||
resolve(_target);
|
||||
});
|
||||
|
||||
@@ -23,51 +23,6 @@ export function isValidFolderName(name: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* DriveFolderを物理削除します
|
||||
*/
|
||||
export async function deleteDriveFolder(driveFolder: string | mongo.ObjectID | IDriveFolder) {
|
||||
let d: IDriveFolder;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(driveFolder)) {
|
||||
d = await DriveFolder.findOne({
|
||||
_id: driveFolder
|
||||
});
|
||||
} else if (typeof driveFolder === 'string') {
|
||||
d = await DriveFolder.findOne({
|
||||
_id: new mongo.ObjectID(driveFolder)
|
||||
});
|
||||
} else {
|
||||
d = driveFolder as IDriveFolder;
|
||||
}
|
||||
|
||||
if (d == null) return;
|
||||
|
||||
// このDriveFolderに格納されているDriveFileがあればすべてルートに移動
|
||||
await DriveFile.update({
|
||||
'metadata.folderId': d._id
|
||||
}, {
|
||||
$set: {
|
||||
'metadata.folderId': null
|
||||
}
|
||||
});
|
||||
|
||||
// このDriveFolderに格納されているDriveFolderがあればすべてルートに移動
|
||||
await DriveFolder.update({
|
||||
parentId: d._id
|
||||
}, {
|
||||
$set: {
|
||||
parentId: null
|
||||
}
|
||||
});
|
||||
|
||||
// このDriveFolderを削除
|
||||
await DriveFolder.remove({
|
||||
_id: d._id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a drive folder for API response
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import isObjectId from '../misc/is-objectid';
|
||||
import { pack as packNote } from './note';
|
||||
|
||||
const Favorite = db.get<IFavorite>('favorites');
|
||||
Favorite.createIndex('userId');
|
||||
Favorite.createIndex(['userId', 'noteId'], { unique: true });
|
||||
export default Favorite;
|
||||
|
||||
@@ -15,38 +16,11 @@ export type IFavorite = {
|
||||
noteId: mongo.ObjectID;
|
||||
};
|
||||
|
||||
/**
|
||||
* Favoriteを物理削除します
|
||||
*/
|
||||
export async function deleteFavorite(favorite: string | mongo.ObjectID | IFavorite) {
|
||||
let f: IFavorite;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(favorite)) {
|
||||
f = await Favorite.findOne({
|
||||
_id: favorite
|
||||
});
|
||||
} else if (typeof favorite === 'string') {
|
||||
f = await Favorite.findOne({
|
||||
_id: new mongo.ObjectID(favorite)
|
||||
});
|
||||
} else {
|
||||
f = favorite as IFavorite;
|
||||
}
|
||||
|
||||
if (f == null) return;
|
||||
|
||||
// このFavoriteを削除
|
||||
await Favorite.remove({
|
||||
_id: f._id
|
||||
});
|
||||
}
|
||||
|
||||
export const packMany = async (
|
||||
export const packMany = (
|
||||
favorites: any[],
|
||||
me: any
|
||||
) => {
|
||||
return (await Promise.all(favorites.map(f => pack(f, me)))).filter(x => x != null);
|
||||
return Promise.all(favorites.map(f => pack(f, me)));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,8 @@ import isObjectId from '../misc/is-objectid';
|
||||
import { pack as packUser } from './user';
|
||||
|
||||
const FollowRequest = db.get<IFollowRequest>('followRequests');
|
||||
FollowRequest.createIndex('followerId');
|
||||
FollowRequest.createIndex('followeeId');
|
||||
FollowRequest.createIndex(['followerId', 'followeeId'], { unique: true });
|
||||
export default FollowRequest;
|
||||
|
||||
@@ -28,33 +30,6 @@ export type IFollowRequest = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* FollowRequestを物理削除します
|
||||
*/
|
||||
export async function deleteFollowRequest(followRequest: string | mongo.ObjectID | IFollowRequest) {
|
||||
let f: IFollowRequest;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(followRequest)) {
|
||||
f = await FollowRequest.findOne({
|
||||
_id: followRequest
|
||||
});
|
||||
} else if (typeof followRequest === 'string') {
|
||||
f = await FollowRequest.findOne({
|
||||
_id: new mongo.ObjectID(followRequest)
|
||||
});
|
||||
} else {
|
||||
f = followRequest as IFollowRequest;
|
||||
}
|
||||
|
||||
if (f == null) return;
|
||||
|
||||
// このFollowingを削除
|
||||
await FollowRequest.remove({
|
||||
_id: f._id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a request for API response
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
|
||||
const Following = db.get<IFollowing>('following');
|
||||
Following.createIndex('followerId');
|
||||
Following.createIndex('followeeId');
|
||||
Following.createIndex(['followerId', 'followeeId'], { unique: true });
|
||||
export default Following;
|
||||
|
||||
@@ -25,30 +26,3 @@ export type IFollowing = {
|
||||
sharedInbox?: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Followingを物理削除します
|
||||
*/
|
||||
export async function deleteFollowing(following: string | mongo.ObjectID | IFollowing) {
|
||||
let f: IFollowing;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(following)) {
|
||||
f = await Following.findOne({
|
||||
_id: following
|
||||
});
|
||||
} else if (typeof following === 'string') {
|
||||
f = await Following.findOne({
|
||||
_id: new mongo.ObjectID(following)
|
||||
});
|
||||
} else {
|
||||
f = following as IFollowing;
|
||||
}
|
||||
|
||||
if (f == null) return;
|
||||
|
||||
// このFollowingを削除
|
||||
await Following.remove({
|
||||
_id: f._id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
|
||||
const MessagingHistory = db.get<IMessagingHistory>('messagingHistories');
|
||||
export default MessagingHistory;
|
||||
@@ -12,30 +11,3 @@ export type IMessagingHistory = {
|
||||
partnerId: mongo.ObjectID;
|
||||
messageId: mongo.ObjectID;
|
||||
};
|
||||
|
||||
/**
|
||||
* MessagingHistoryを物理削除します
|
||||
*/
|
||||
export async function deleteMessagingHistory(messagingHistory: string | mongo.ObjectID | IMessagingHistory) {
|
||||
let m: IMessagingHistory;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(messagingHistory)) {
|
||||
m = await MessagingHistory.findOne({
|
||||
_id: messagingHistory
|
||||
});
|
||||
} else if (typeof messagingHistory === 'string') {
|
||||
m = await MessagingHistory.findOne({
|
||||
_id: new mongo.ObjectID(messagingHistory)
|
||||
});
|
||||
} else {
|
||||
m = messagingHistory as IMessagingHistory;
|
||||
}
|
||||
|
||||
if (m == null) return;
|
||||
|
||||
// このMessagingHistoryを削除
|
||||
await MessagingHistory.remove({
|
||||
_id: m._id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { pack as packUser } from './user';
|
||||
import { pack as packFile } from './drive-file';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
import MessagingHistory, { deleteMessagingHistory } from './messaging-history';
|
||||
import { length } from 'stringz';
|
||||
|
||||
const MessagingMessage = db.get<IMessagingMessage>('messagingMessages');
|
||||
@@ -24,38 +23,6 @@ export function isValidText(text: string): boolean {
|
||||
return length(text.trim()) <= 1000 && text.trim() != '';
|
||||
}
|
||||
|
||||
/**
|
||||
* MessagingMessageを物理削除します
|
||||
*/
|
||||
export async function deleteMessagingMessage(messagingMessage: string | mongo.ObjectID | IMessagingMessage) {
|
||||
let m: IMessagingMessage;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(messagingMessage)) {
|
||||
m = await MessagingMessage.findOne({
|
||||
_id: messagingMessage
|
||||
});
|
||||
} else if (typeof messagingMessage === 'string') {
|
||||
m = await MessagingMessage.findOne({
|
||||
_id: new mongo.ObjectID(messagingMessage)
|
||||
});
|
||||
} else {
|
||||
m = messagingMessage as IMessagingMessage;
|
||||
}
|
||||
|
||||
if (m == null) return;
|
||||
|
||||
// このMessagingMessageを指すMessagingHistoryをすべて削除
|
||||
await Promise.all((
|
||||
await MessagingHistory.find({ messageId: m._id })
|
||||
).map(x => deleteMessagingHistory(x)));
|
||||
|
||||
// このMessagingMessageを削除
|
||||
await MessagingMessage.remove({
|
||||
_id: m._id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a messaging message for API response
|
||||
*/
|
||||
|
||||
@@ -15,4 +15,24 @@ export type IMeta = {
|
||||
disableLocalTimeline?: boolean;
|
||||
hidedTags?: string[];
|
||||
bannerUrl?: string;
|
||||
|
||||
/**
|
||||
* カスタム絵文字定義
|
||||
*/
|
||||
emojis?: {
|
||||
/**
|
||||
* 絵文字名 (例: thinking_ai)
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* エイリアス
|
||||
*/
|
||||
aliases?: string[];
|
||||
|
||||
/**
|
||||
* 絵文字画像のURL
|
||||
*/
|
||||
url: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
const deepcopy = require('deepcopy');
|
||||
import { pack as packUser, IUser } from './user';
|
||||
|
||||
const Mute = db.get<IMute>('mute');
|
||||
Mute.createIndex('muterId');
|
||||
Mute.createIndex('muteeId');
|
||||
Mute.createIndex(['muterId', 'muteeId'], { unique: true });
|
||||
export default Mute;
|
||||
|
||||
@@ -13,29 +17,40 @@ export interface IMute {
|
||||
muteeId: mongo.ObjectID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Muteを物理削除します
|
||||
*/
|
||||
export async function deleteMute(mute: string | mongo.ObjectID | IMute) {
|
||||
let m: IMute;
|
||||
export const packMany = (
|
||||
mutes: (string | mongo.ObjectID | IMute)[],
|
||||
me?: string | mongo.ObjectID | IUser
|
||||
) => {
|
||||
return Promise.all(mutes.map(x => pack(x, me)));
|
||||
};
|
||||
|
||||
// Populate
|
||||
export const pack = (
|
||||
mute: any,
|
||||
me?: any
|
||||
) => new Promise<any>(async (resolve, reject) => {
|
||||
let _mute: any;
|
||||
|
||||
// Populate the mute if 'mute' is ID
|
||||
if (isObjectId(mute)) {
|
||||
m = await Mute.findOne({
|
||||
_mute = await Mute.findOne({
|
||||
_id: mute
|
||||
});
|
||||
} else if (typeof mute === 'string') {
|
||||
m = await Mute.findOne({
|
||||
_mute = await Mute.findOne({
|
||||
_id: new mongo.ObjectID(mute)
|
||||
});
|
||||
} else {
|
||||
m = mute as IMute;
|
||||
_mute = deepcopy(mute);
|
||||
}
|
||||
|
||||
if (m == null) return;
|
||||
// Rename _id to id
|
||||
_mute.id = _mute._id;
|
||||
delete _mute._id;
|
||||
|
||||
// このMuteを削除
|
||||
await Mute.remove({
|
||||
_id: m._id
|
||||
// Populate mutee
|
||||
_mute.mutee = await packUser(_mute.muteeId, me, {
|
||||
detail: true
|
||||
});
|
||||
}
|
||||
|
||||
resolve(_mute);
|
||||
});
|
||||
|
||||
@@ -7,6 +7,8 @@ import Reaction from './note-reaction';
|
||||
import { pack as packUser } from './user';
|
||||
|
||||
const NoteReaction = db.get<INoteReaction>('noteReactions');
|
||||
NoteReaction.createIndex('noteId');
|
||||
NoteReaction.createIndex('userId');
|
||||
NoteReaction.createIndex(['userId', 'noteId'], { unique: true });
|
||||
export default NoteReaction;
|
||||
|
||||
@@ -31,33 +33,6 @@ export const validateReaction = $.str.or([
|
||||
'pudding'
|
||||
]);
|
||||
|
||||
/**
|
||||
* NoteReactionを物理削除します
|
||||
*/
|
||||
export async function deleteNoteReaction(noteReaction: string | mongo.ObjectID | INoteReaction) {
|
||||
let n: INoteReaction;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(noteReaction)) {
|
||||
n = await NoteReaction.findOne({
|
||||
_id: noteReaction
|
||||
});
|
||||
} else if (typeof noteReaction === 'string') {
|
||||
n = await NoteReaction.findOne({
|
||||
_id: new mongo.ObjectID(noteReaction)
|
||||
});
|
||||
} else {
|
||||
n = noteReaction as INoteReaction;
|
||||
}
|
||||
|
||||
if (n == null) return;
|
||||
|
||||
// このNoteReactionを削除
|
||||
await NoteReaction.remove({
|
||||
_id: n._id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a reaction for API response
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,8 @@ import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
|
||||
const NoteUnread = db.get<INoteUnread>('noteUnreads');
|
||||
NoteUnread.createIndex('userId');
|
||||
NoteUnread.createIndex('noteId');
|
||||
NoteUnread.createIndex(['userId', 'noteId'], { unique: true });
|
||||
export default NoteUnread;
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
|
||||
const NoteWatching = db.get<INoteWatching>('noteWatching');
|
||||
NoteWatching.createIndex('userId');
|
||||
NoteWatching.createIndex('noteId');
|
||||
NoteWatching.createIndex(['userId', 'noteId'], { unique: true });
|
||||
export default NoteWatching;
|
||||
|
||||
@@ -12,30 +13,3 @@ export interface INoteWatching {
|
||||
userId: mongo.ObjectID;
|
||||
noteId: mongo.ObjectID;
|
||||
}
|
||||
|
||||
/**
|
||||
* NoteWatchingを物理削除します
|
||||
*/
|
||||
export async function deleteNoteWatching(noteWatching: string | mongo.ObjectID | INoteWatching) {
|
||||
let n: INoteWatching;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(noteWatching)) {
|
||||
n = await NoteWatching.findOne({
|
||||
_id: noteWatching
|
||||
});
|
||||
} else if (typeof noteWatching === 'string') {
|
||||
n = await NoteWatching.findOne({
|
||||
_id: new mongo.ObjectID(noteWatching)
|
||||
});
|
||||
} else {
|
||||
n = noteWatching as INoteWatching;
|
||||
}
|
||||
|
||||
if (n == null) return;
|
||||
|
||||
// このNoteWatchingを削除
|
||||
await NoteWatching.remove({
|
||||
_id: n._id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,13 +6,10 @@ import isObjectId from '../misc/is-objectid';
|
||||
import { length } from 'stringz';
|
||||
import { IUser, pack as packUser } from './user';
|
||||
import { pack as packApp } from './app';
|
||||
import PollVote, { deletePollVote } from './poll-vote';
|
||||
import Reaction, { deleteNoteReaction } from './note-reaction';
|
||||
import PollVote from './poll-vote';
|
||||
import Reaction from './note-reaction';
|
||||
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
||||
import NoteWatching, { deleteNoteWatching } from './note-watching';
|
||||
import NoteReaction from './note-reaction';
|
||||
import Favorite, { deleteFavorite } from './favorite';
|
||||
import Notification, { deleteNotification } from './notification';
|
||||
import Favorite from './favorite';
|
||||
import Following from './following';
|
||||
import config from '../config';
|
||||
|
||||
@@ -108,72 +105,6 @@ export type INote = {
|
||||
_files?: IDriveFile[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Noteを物理削除します
|
||||
*/
|
||||
export async function deleteNote(note: string | mongo.ObjectID | INote) {
|
||||
let n: INote;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(note)) {
|
||||
n = await Note.findOne({
|
||||
_id: note
|
||||
});
|
||||
} else if (typeof note === 'string') {
|
||||
n = await Note.findOne({
|
||||
_id: new mongo.ObjectID(note)
|
||||
});
|
||||
} else {
|
||||
n = note as INote;
|
||||
}
|
||||
|
||||
console.log(n == null ? `Note: delete skipped ${note}` : `Note: deleting ${n._id}`);
|
||||
|
||||
if (n == null) return;
|
||||
|
||||
// このNoteへの返信をすべて削除
|
||||
await Promise.all((
|
||||
await Note.find({ replyId: n._id })
|
||||
).map(x => deleteNote(x)));
|
||||
|
||||
// このNoteのRenoteをすべて削除
|
||||
await Promise.all((
|
||||
await Note.find({ renoteId: n._id })
|
||||
).map(x => deleteNote(x)));
|
||||
|
||||
// この投稿に対するNoteWatchingをすべて削除
|
||||
await Promise.all((
|
||||
await NoteWatching.find({ noteId: n._id })
|
||||
).map(x => deleteNoteWatching(x)));
|
||||
|
||||
// この投稿に対するNoteReactionをすべて削除
|
||||
await Promise.all((
|
||||
await NoteReaction.find({ noteId: n._id })
|
||||
).map(x => deleteNoteReaction(x)));
|
||||
|
||||
// この投稿に対するPollVoteをすべて削除
|
||||
await Promise.all((
|
||||
await PollVote.find({ noteId: n._id })
|
||||
).map(x => deletePollVote(x)));
|
||||
|
||||
// この投稿に対するFavoriteをすべて削除
|
||||
await Promise.all((
|
||||
await Favorite.find({ noteId: n._id })
|
||||
).map(x => deleteFavorite(x)));
|
||||
|
||||
// この投稿に対するNotificationをすべて削除
|
||||
await Promise.all((
|
||||
await Notification.find({ noteId: n._id })
|
||||
).map(x => deleteNotification(x)));
|
||||
|
||||
// このNoteを削除
|
||||
await Note.remove({
|
||||
_id: n._id
|
||||
});
|
||||
|
||||
console.log(`Note: deleted ${n._id}`);
|
||||
}
|
||||
|
||||
export const hideNote = async (packedNote: any, meId: mongo.ObjectID) => {
|
||||
let hide = false;
|
||||
|
||||
@@ -233,7 +164,7 @@ export const hideNote = async (packedNote: any, meId: mongo.ObjectID) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const packMany = async (
|
||||
export const packMany = (
|
||||
notes: (string | mongo.ObjectID | INote)[],
|
||||
me?: string | mongo.ObjectID | IUser,
|
||||
options?: {
|
||||
@@ -241,7 +172,7 @@ export const packMany = async (
|
||||
skipHide?: boolean;
|
||||
}
|
||||
) => {
|
||||
return (await Promise.all(notes.map(n => pack(n, me, options)))).filter(x => x != null);
|
||||
return Promise.all(notes.map(n => pack(n, me, options)));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -328,11 +259,6 @@ export const pack = async (
|
||||
|
||||
// When requested a detailed note data
|
||||
if (opts.detail) {
|
||||
//#region 重いので廃止
|
||||
_note.prev = null;
|
||||
_note.next = null;
|
||||
//#endregion
|
||||
|
||||
if (_note.replyId) {
|
||||
// Populate reply to note
|
||||
_note.reply = pack(_note.replyId, meId, {
|
||||
|
||||
@@ -51,37 +51,10 @@ export interface INotification {
|
||||
isRead: Boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notificationを物理削除します
|
||||
*/
|
||||
export async function deleteNotification(notification: string | mongo.ObjectID | INotification) {
|
||||
let n: INotification;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(notification)) {
|
||||
n = await Notification.findOne({
|
||||
_id: notification
|
||||
});
|
||||
} else if (typeof notification === 'string') {
|
||||
n = await Notification.findOne({
|
||||
_id: new mongo.ObjectID(notification)
|
||||
});
|
||||
} else {
|
||||
n = notification as INotification;
|
||||
}
|
||||
|
||||
if (n == null) return;
|
||||
|
||||
// このNotificationを削除
|
||||
await Notification.remove({
|
||||
_id: n._id
|
||||
});
|
||||
}
|
||||
|
||||
export const packMany = async (
|
||||
export const packMany = (
|
||||
notifications: any[]
|
||||
) => {
|
||||
return (await Promise.all(notifications.map(n => pack(n)))).filter(x => x != null);
|
||||
return Promise.all(notifications.map(n => pack(n)));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
|
||||
const PollVote = db.get<IPollVote>('pollVotes');
|
||||
export default PollVote;
|
||||
@@ -12,30 +11,3 @@ export interface IPollVote {
|
||||
noteId: mongo.ObjectID;
|
||||
choice: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* PollVoteを物理削除します
|
||||
*/
|
||||
export async function deletePollVote(pollVote: string | mongo.ObjectID | IPollVote) {
|
||||
let p: IPollVote;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(pollVote)) {
|
||||
p = await PollVote.findOne({
|
||||
_id: pollVote
|
||||
});
|
||||
} else if (typeof pollVote === 'string') {
|
||||
p = await PollVote.findOne({
|
||||
_id: new mongo.ObjectID(pollVote)
|
||||
});
|
||||
} else {
|
||||
p = pollVote as IPollVote;
|
||||
}
|
||||
|
||||
if (p == null) return;
|
||||
|
||||
// このPollVoteを削除
|
||||
await PollVote.remove({
|
||||
_id: p._id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
|
||||
const SwSubscription = db.get<ISwSubscription>('swSubscriptions');
|
||||
export default SwSubscription;
|
||||
@@ -12,30 +11,3 @@ export interface ISwSubscription {
|
||||
auth: string;
|
||||
publickey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SwSubscriptionを物理削除します
|
||||
*/
|
||||
export async function deleteSwSubscription(swSubscription: string | mongo.ObjectID | ISwSubscription) {
|
||||
let s: ISwSubscription;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(swSubscription)) {
|
||||
s = await SwSubscription.findOne({
|
||||
_id: swSubscription
|
||||
});
|
||||
} else if (typeof swSubscription === 'string') {
|
||||
s = await SwSubscription.findOne({
|
||||
_id: new mongo.ObjectID(swSubscription)
|
||||
});
|
||||
} else {
|
||||
s = swSubscription as ISwSubscription;
|
||||
}
|
||||
|
||||
if (s == null) return;
|
||||
|
||||
// このSwSubscriptionを削除
|
||||
await SwSubscription.remove({
|
||||
_id: s._id
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,33 +14,6 @@ export interface IUserList {
|
||||
userIds: mongo.ObjectID[];
|
||||
}
|
||||
|
||||
/**
|
||||
* UserListを物理削除します
|
||||
*/
|
||||
export async function deleteUserList(userList: string | mongo.ObjectID | IUserList) {
|
||||
let u: IUserList;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(userList)) {
|
||||
u = await UserList.findOne({
|
||||
_id: userList
|
||||
});
|
||||
} else if (typeof userList === 'string') {
|
||||
u = await UserList.findOne({
|
||||
_id: new mongo.ObjectID(userList)
|
||||
});
|
||||
} else {
|
||||
u = userList as IUserList;
|
||||
}
|
||||
|
||||
if (u == null) return;
|
||||
|
||||
// このUserListを削除
|
||||
await UserList.remove({
|
||||
_id: u._id
|
||||
});
|
||||
}
|
||||
|
||||
export const pack = (
|
||||
userList: string | mongo.ObjectID | IUserList
|
||||
) => new Promise<any>(async (resolve, reject) => {
|
||||
|
||||
@@ -1,27 +1,15 @@
|
||||
import * as mongo from 'mongodb';
|
||||
const deepcopy = require('deepcopy');
|
||||
const sequential = require('promise-sequential');
|
||||
import rap from '@prezzemolo/rap';
|
||||
import db from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
import Note, { packMany as packNoteMany, deleteNote } from './note';
|
||||
import Following, { deleteFollowing } from './following';
|
||||
import Mute, { deleteMute } from './mute';
|
||||
import { packMany as packNoteMany } from './note';
|
||||
import Following from './following';
|
||||
import Blocking from './blocking';
|
||||
import Mute from './mute';
|
||||
import { getFriendIds } from '../server/api/common/get-friends';
|
||||
import config from '../config';
|
||||
import AccessToken, { deleteAccessToken } from './access-token';
|
||||
import NoteWatching, { deleteNoteWatching } from './note-watching';
|
||||
import Favorite, { deleteFavorite } from './favorite';
|
||||
import NoteReaction, { deleteNoteReaction } from './note-reaction';
|
||||
import MessagingMessage, { deleteMessagingMessage } from './messaging-message';
|
||||
import MessagingHistory, { deleteMessagingHistory } from './messaging-history';
|
||||
import DriveFile, { deleteDriveFile } from './drive-file';
|
||||
import DriveFolder, { deleteDriveFolder } from './drive-folder';
|
||||
import PollVote, { deletePollVote } from './poll-vote';
|
||||
import SwSubscription, { deleteSwSubscription } from './sw-subscription';
|
||||
import Notification, { deleteNotification } from './notification';
|
||||
import UserList, { deleteUserList } from './user-list';
|
||||
import FollowRequest, { deleteFollowRequest } from './follow-request';
|
||||
import FollowRequest from './follow-request';
|
||||
|
||||
const User = db.get<IUser>('users');
|
||||
|
||||
@@ -167,149 +155,48 @@ export function isValidBirthday(birthday: string): boolean {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* Userを物理削除します
|
||||
*/
|
||||
export async function deleteUser(user: string | mongo.ObjectID | IUser) {
|
||||
let u: IUser;
|
||||
|
||||
// Populate
|
||||
if (isObjectId(user)) {
|
||||
u = await User.findOne({
|
||||
_id: user
|
||||
});
|
||||
} else if (typeof user === 'string') {
|
||||
u = await User.findOne({
|
||||
_id: new mongo.ObjectID(user)
|
||||
});
|
||||
} else {
|
||||
u = user as IUser;
|
||||
}
|
||||
|
||||
console.log(u == null ? `User: delete skipped ${user}` : `User: deleting ${u._id}`);
|
||||
|
||||
if (u == null) return;
|
||||
|
||||
// このユーザーのAccessTokenをすべて削除
|
||||
await Promise.all((
|
||||
await AccessToken.find({ userId: u._id })
|
||||
).map(x => deleteAccessToken(x)));
|
||||
|
||||
// このユーザーのNoteをすべて削除
|
||||
await sequential((
|
||||
await Note.find({ userId: u._id })
|
||||
).map(x => () => deleteNote(x)));
|
||||
|
||||
// このユーザーのNoteReactionをすべて削除
|
||||
await Promise.all((
|
||||
await NoteReaction.find({ userId: u._id })
|
||||
).map(x => deleteNoteReaction(x)));
|
||||
|
||||
// このユーザーのNoteWatchingをすべて削除
|
||||
await Promise.all((
|
||||
await NoteWatching.find({ userId: u._id })
|
||||
).map(x => deleteNoteWatching(x)));
|
||||
|
||||
// このユーザーのPollVoteをすべて削除
|
||||
await Promise.all((
|
||||
await PollVote.find({ userId: u._id })
|
||||
).map(x => deletePollVote(x)));
|
||||
|
||||
// このユーザーのFavoriteをすべて削除
|
||||
await Promise.all((
|
||||
await Favorite.find({ userId: u._id })
|
||||
).map(x => deleteFavorite(x)));
|
||||
|
||||
// このユーザーのMessageをすべて削除
|
||||
await Promise.all((
|
||||
await MessagingMessage.find({ userId: u._id })
|
||||
).map(x => deleteMessagingMessage(x)));
|
||||
|
||||
// このユーザーへのMessageをすべて削除
|
||||
await Promise.all((
|
||||
await MessagingMessage.find({ recipientId: u._id })
|
||||
).map(x => deleteMessagingMessage(x)));
|
||||
|
||||
// このユーザーの関わるMessagingHistoryをすべて削除
|
||||
await Promise.all((
|
||||
await MessagingHistory.find({ $or: [{ partnerId: u._id }, { userId: u._id }] })
|
||||
).map(x => deleteMessagingHistory(x)));
|
||||
|
||||
// このユーザーのDriveFileをすべて削除
|
||||
await Promise.all((
|
||||
await DriveFile.find({ 'metadata.userId': u._id })
|
||||
).map(x => deleteDriveFile(x)));
|
||||
|
||||
// このユーザーのDriveFolderをすべて削除
|
||||
await Promise.all((
|
||||
await DriveFolder.find({ userId: u._id })
|
||||
).map(x => deleteDriveFolder(x)));
|
||||
|
||||
// このユーザーのMuteをすべて削除
|
||||
await Promise.all((
|
||||
await Mute.find({ muterId: u._id })
|
||||
).map(x => deleteMute(x)));
|
||||
|
||||
// このユーザーへのMuteをすべて削除
|
||||
await Promise.all((
|
||||
await Mute.find({ muteeId: u._id })
|
||||
).map(x => deleteMute(x)));
|
||||
|
||||
// このユーザーのFollowingをすべて削除
|
||||
await Promise.all((
|
||||
await Following.find({ followerId: u._id })
|
||||
).map(x => deleteFollowing(x)));
|
||||
|
||||
// このユーザーへのFollowingをすべて削除
|
||||
await Promise.all((
|
||||
await Following.find({ followeeId: u._id })
|
||||
).map(x => deleteFollowing(x)));
|
||||
|
||||
// このユーザーのFollowRequestをすべて削除
|
||||
await Promise.all((
|
||||
await FollowRequest.find({ followerId: u._id })
|
||||
).map(x => deleteFollowRequest(x)));
|
||||
|
||||
// このユーザーへのFollowRequestをすべて削除
|
||||
await Promise.all((
|
||||
await FollowRequest.find({ followeeId: u._id })
|
||||
).map(x => deleteFollowRequest(x)));
|
||||
|
||||
// このユーザーのSwSubscriptionをすべて削除
|
||||
await Promise.all((
|
||||
await SwSubscription.find({ userId: u._id })
|
||||
).map(x => deleteSwSubscription(x)));
|
||||
|
||||
// このユーザーのNotificationをすべて削除
|
||||
await Promise.all((
|
||||
await Notification.find({ notifieeId: u._id })
|
||||
).map(x => deleteNotification(x)));
|
||||
|
||||
// このユーザーが原因となったNotificationをすべて削除
|
||||
await Promise.all((
|
||||
await Notification.find({ notifierId: u._id })
|
||||
).map(x => deleteNotification(x)));
|
||||
|
||||
// このユーザーのUserListをすべて削除
|
||||
await Promise.all((
|
||||
await UserList.find({ userId: u._id })
|
||||
).map(x => deleteUserList(x)));
|
||||
|
||||
// このユーザーが入っているすべてのUserListからこのユーザーを削除
|
||||
await Promise.all((
|
||||
await UserList.find({ userIds: u._id })
|
||||
).map(x =>
|
||||
UserList.update({ _id: x._id }, {
|
||||
$pull: { userIds: u._id }
|
||||
export async function getRelation(me: mongo.ObjectId, target: mongo.ObjectId) {
|
||||
const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
|
||||
Following.findOne({
|
||||
followerId: me,
|
||||
followeeId: target
|
||||
}),
|
||||
Following.findOne({
|
||||
followerId: target,
|
||||
followeeId: me
|
||||
}),
|
||||
FollowRequest.findOne({
|
||||
followerId: me,
|
||||
followeeId: target
|
||||
}),
|
||||
FollowRequest.findOne({
|
||||
followerId: target,
|
||||
followeeId: me
|
||||
}),
|
||||
Blocking.findOne({
|
||||
blockerId: me,
|
||||
blockeeId: target
|
||||
}),
|
||||
Blocking.findOne({
|
||||
blockerId: target,
|
||||
blockeeId: me
|
||||
}),
|
||||
Mute.findOne({
|
||||
muterId: me,
|
||||
muteeId: target
|
||||
})
|
||||
));
|
||||
]);
|
||||
|
||||
// このユーザーを削除
|
||||
await User.remove({
|
||||
_id: u._id
|
||||
});
|
||||
|
||||
console.log(`User: deleted ${u._id}`);
|
||||
return {
|
||||
isFollowing: following1 !== null,
|
||||
isStalking: following1 && following1.stalk,
|
||||
hasPendingFollowRequestFromYou: followReq1 !== null,
|
||||
hasPendingFollowRequestToYou: followReq2 !== null,
|
||||
isFollowed: following2 !== null,
|
||||
isBlocking: toBlocking !== null,
|
||||
isBlocked: fromBlocked !== null,
|
||||
isMuted: mute !== null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,13 +223,16 @@ export const pack = (
|
||||
|
||||
let _user: any;
|
||||
|
||||
const fields = opts.detail ? {
|
||||
} : {
|
||||
settings: false,
|
||||
clientSettings: false,
|
||||
profile: false,
|
||||
keywords: false,
|
||||
domains: false
|
||||
const fields = opts.detail ? {} : {
|
||||
name: true,
|
||||
username: true,
|
||||
host: true,
|
||||
avatarColor: true,
|
||||
avatarUrl: true,
|
||||
isCat: true,
|
||||
isBot: true,
|
||||
isAdmin: true,
|
||||
isVerified: true
|
||||
};
|
||||
|
||||
// Populate the user if 'user' is ID
|
||||
@@ -377,6 +267,8 @@ export const pack = (
|
||||
_user.id = _user._id;
|
||||
delete _user._id;
|
||||
|
||||
delete _user.usernameLower;
|
||||
|
||||
if (_user.host == null) {
|
||||
// Remove private properties
|
||||
delete _user.keypair;
|
||||
@@ -384,7 +276,6 @@ export const pack = (
|
||||
delete _user.token;
|
||||
delete _user.twoFactorTempSecret;
|
||||
delete _user.twoFactorSecret;
|
||||
delete _user.usernameLower;
|
||||
if (_user.twitter) {
|
||||
delete _user.twitter.accessToken;
|
||||
delete _user.twitter.accessTokenSecret;
|
||||
@@ -407,16 +298,6 @@ export const pack = (
|
||||
|
||||
if (_user.avatarUrl == null) {
|
||||
_user.avatarUrl = `${config.drive_url}/default-avatar.jpg`;
|
||||
|
||||
// 互換性のため
|
||||
if (_user.avatarId) {
|
||||
_user.avatarUrl = `${config.drive_url}/${_user.avatarId}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 互換性のため
|
||||
if (_user.bannerId && _user.bannerUrl == null) {
|
||||
_user.bannerUrl = `${config.drive_url}/${_user.bannerId}`;
|
||||
}
|
||||
|
||||
if (!meId || !meId.equals(_user.id) || !opts.detail) {
|
||||
@@ -426,42 +307,17 @@ export const pack = (
|
||||
delete _user.hasUnreadNotification;
|
||||
}
|
||||
|
||||
if (meId && !meId.equals(_user.id)) {
|
||||
const [following1, following2, followReq1, followReq2, mute] = await Promise.all([
|
||||
Following.findOne({
|
||||
followerId: meId,
|
||||
followeeId: _user.id
|
||||
}),
|
||||
Following.findOne({
|
||||
followerId: _user.id,
|
||||
followeeId: meId
|
||||
}),
|
||||
FollowRequest.findOne({
|
||||
followerId: meId,
|
||||
followeeId: _user.id
|
||||
}),
|
||||
FollowRequest.findOne({
|
||||
followerId: _user.id,
|
||||
followeeId: meId
|
||||
}),
|
||||
Mute.findOne({
|
||||
muterId: meId,
|
||||
muteeId: _user.id
|
||||
})
|
||||
]);
|
||||
if (meId && !meId.equals(_user.id) && opts.detail) {
|
||||
const relation = await getRelation(meId, _user.id);
|
||||
|
||||
// Whether the user is following
|
||||
_user.isFollowing = following1 !== null;
|
||||
_user.isStalking = following1 && following1.stalk;
|
||||
|
||||
_user.hasPendingFollowRequestFromYou = followReq1 !== null;
|
||||
_user.hasPendingFollowRequestToYou = followReq2 !== null;
|
||||
|
||||
// Whether the user is followed
|
||||
_user.isFollowed = following2 !== null;
|
||||
|
||||
// Whether the user is muted
|
||||
_user.isMuted = mute !== null;
|
||||
_user.isFollowing = relation.isFollowing;
|
||||
_user.isFollowed = relation.isFollowed;
|
||||
_user.isStalking = relation.isStalking;
|
||||
_user.hasPendingFollowRequestFromYou = relation.hasPendingFollowRequestFromYou;
|
||||
_user.hasPendingFollowRequestToYou = relation.hasPendingFollowRequestToYou;
|
||||
_user.isBlocking = relation.isBlocking;
|
||||
_user.isBlocked = relation.isBlocked;
|
||||
_user.isMuted = relation.isMuted;
|
||||
}
|
||||
|
||||
if (opts.detail) {
|
||||
|
||||
34
src/remote/activitypub/kernel/block/index.ts
Normal file
34
src/remote/activitypub/kernel/block/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import User, { IRemoteUser } from '../../../../models/user';
|
||||
import config from '../../../../config';
|
||||
import * as debug from 'debug';
|
||||
import { IBlock } from '../../type';
|
||||
import block from '../../../../services/blocking/create';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
|
||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||
|
||||
const uri = activity.id || activity;
|
||||
|
||||
log(`Block: ${uri}`);
|
||||
|
||||
if (!id.startsWith(config.url + '/')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const blockee = await User.findOne({
|
||||
_id: new mongo.ObjectID(id.split('/').pop())
|
||||
});
|
||||
|
||||
if (blockee === null) {
|
||||
throw new Error('blockee not found');
|
||||
}
|
||||
|
||||
if (blockee.host != null) {
|
||||
throw new Error('ブロックしようとしているユーザーはローカルユーザーではありません');
|
||||
}
|
||||
|
||||
block(actor, blockee);
|
||||
};
|
||||
@@ -10,6 +10,7 @@ import accept from './accept';
|
||||
import reject from './reject';
|
||||
import add from './add';
|
||||
import remove from './remove';
|
||||
import block from './block';
|
||||
|
||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
||||
switch (activity.type) {
|
||||
@@ -53,6 +54,10 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
||||
await undo(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Block':
|
||||
await block(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Collection':
|
||||
case 'OrderedCollection':
|
||||
// TODO
|
||||
|
||||
34
src/remote/activitypub/kernel/undo/block.ts
Normal file
34
src/remote/activitypub/kernel/undo/block.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import User, { IRemoteUser } from '../../../../models/user';
|
||||
import config from '../../../../config';
|
||||
import * as debug from 'debug';
|
||||
import { IBlock } from '../../type';
|
||||
import unblock from '../../../../services/blocking/delete';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
|
||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||
|
||||
const uri = activity.id || activity;
|
||||
|
||||
log(`UnBlock: ${uri}`);
|
||||
|
||||
if (!id.startsWith(config.url + '/')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const blockee = await User.findOne({
|
||||
_id: new mongo.ObjectID(id.split('/').pop())
|
||||
});
|
||||
|
||||
if (blockee === null) {
|
||||
throw new Error('blockee not found');
|
||||
}
|
||||
|
||||
if (blockee.host != null) {
|
||||
throw new Error('ブロック解除しようとしているユーザーはローカルユーザーではありません');
|
||||
}
|
||||
|
||||
unblock(actor, blockee);
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
import * as debug from 'debug';
|
||||
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
import { IUndo, IFollow } from '../../type';
|
||||
import { IUndo, IFollow, IBlock } from '../../type';
|
||||
import unfollow from './follow';
|
||||
import unblock from './block';
|
||||
import Resolver from '../../resolver';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
@@ -31,6 +32,9 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
|
||||
case 'Follow':
|
||||
unfollow(actor, object as IFollow);
|
||||
break;
|
||||
case 'Block':
|
||||
unblock(actor, object as IBlock);
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -15,6 +15,7 @@ import { URL } from 'url';
|
||||
import { resolveNote } from './note';
|
||||
import registerInstance from '../../../services/register-instance';
|
||||
import Instance from '../../../models/instance';
|
||||
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
@@ -209,8 +210,8 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
|
||||
|
||||
const avatarId = avatar ? avatar._id : null;
|
||||
const bannerId = banner ? banner._id : null;
|
||||
const avatarUrl = (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null;
|
||||
const bannerUrl = (banner && banner.metadata.url) ? banner.metadata.url : null;
|
||||
const avatarUrl = getDriveFileUrl(avatar, true);
|
||||
const bannerUrl = getDriveFileUrl(banner, false);
|
||||
|
||||
await User.update({ _id: user._id }, {
|
||||
$set: {
|
||||
@@ -303,8 +304,8 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
|
||||
featured: person.featured,
|
||||
avatarId: avatar ? avatar._id : null,
|
||||
bannerId: banner ? banner._id : null,
|
||||
avatarUrl: (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null,
|
||||
bannerUrl: banner && banner.metadata.url ? banner.metadata.url : null,
|
||||
avatarUrl: getDriveFileUrl(avatar, true),
|
||||
bannerUrl: getDriveFileUrl(banner, false),
|
||||
description: htmlToMFM(person.summary),
|
||||
followersCount,
|
||||
followingCount,
|
||||
|
||||
8
src/remote/activitypub/renderer/block.ts
Normal file
8
src/remote/activitypub/renderer/block.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import config from '../../../config';
|
||||
import { ILocalUser, IRemoteUser } from "../../../models/user";
|
||||
|
||||
export default (blocker?: ILocalUser, blockee?: IRemoteUser) => ({
|
||||
type: 'Block',
|
||||
actor: `${config.url}/users/${blocker._id}`,
|
||||
object: blockee.uri
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
import config from '../../../config';
|
||||
import { IDriveFile } from '../../../models/drive-file';
|
||||
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
||||
|
||||
export default (file: IDriveFile) => ({
|
||||
type: 'Document',
|
||||
mediaType: file.contentType,
|
||||
url: file.metadata.url || `${config.drive_url}/${file._id}`
|
||||
url: getDriveFileUrl(file)
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import config from '../../../config';
|
||||
import { IDriveFile } from '../../../models/drive-file';
|
||||
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
||||
|
||||
export default (file: IDriveFile) => ({
|
||||
type: 'Image',
|
||||
url: file.metadata.url || `${config.drive_url}/${file._id}`,
|
||||
url: getDriveFileUrl(file),
|
||||
sensitive: file.metadata.isSensitive
|
||||
});
|
||||
|
||||
@@ -108,6 +108,10 @@ export interface IAnnounce extends IActivity {
|
||||
type: 'Announce';
|
||||
}
|
||||
|
||||
export interface IBlock extends IActivity {
|
||||
type: 'Block';
|
||||
}
|
||||
|
||||
export type Object =
|
||||
ICollection |
|
||||
IOrderedCollection |
|
||||
@@ -120,4 +124,5 @@ export type Object =
|
||||
IAdd |
|
||||
IRemove |
|
||||
ILike |
|
||||
IAnnounce;
|
||||
IAnnounce |
|
||||
IBlock;
|
||||
|
||||
@@ -1,36 +1,83 @@
|
||||
import { toUnicode, toASCII } from 'punycode';
|
||||
import User, { IUser } from '../models/user';
|
||||
import User, { IUser, IRemoteUser } from '../models/user';
|
||||
import webFinger from './webfinger';
|
||||
import config from '../config';
|
||||
import { createPerson } from './activitypub/models/person';
|
||||
import { createPerson, updatePerson } from './activitypub/models/person';
|
||||
import { URL } from 'url';
|
||||
import * as debug from 'debug';
|
||||
|
||||
export default async (username: string, _host: string, option?: any): Promise<IUser> => {
|
||||
const log = debug('misskey:remote:resolve-user');
|
||||
|
||||
export default async (username: string, _host: string, option?: any, resync?: boolean): Promise<IUser> => {
|
||||
const usernameLower = username.toLowerCase();
|
||||
|
||||
if (_host == null) {
|
||||
return await User.findOne({ usernameLower });
|
||||
log(`return local user: ${usernameLower}`);
|
||||
return await User.findOne({ usernameLower, host: null });
|
||||
}
|
||||
|
||||
const hostAscii = toASCII(_host).toLowerCase();
|
||||
const host = toUnicode(hostAscii);
|
||||
|
||||
if (config.host == host) {
|
||||
log(`return local user: ${usernameLower}`);
|
||||
return await User.findOne({ usernameLower, host: null });
|
||||
}
|
||||
|
||||
let user = await User.findOne({ usernameLower, host }, option);
|
||||
const user = await User.findOne({ usernameLower, host }, option);
|
||||
|
||||
const acctLower = `${usernameLower}@${hostAscii}`;
|
||||
|
||||
if (user === null) {
|
||||
const acctLower = `${usernameLower}@${hostAscii}`;
|
||||
const self = await resolveSelf(acctLower);
|
||||
|
||||
const finger = await webFinger(acctLower);
|
||||
const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self');
|
||||
if (!self) {
|
||||
throw new Error('self link not found');
|
||||
}
|
||||
|
||||
user = await createPerson(self.href);
|
||||
log(`return new remote user: ${acctLower}`);
|
||||
return await createPerson(self.href);
|
||||
}
|
||||
|
||||
if (resync) {
|
||||
log(`try resync: ${acctLower}`);
|
||||
const self = await resolveSelf(acctLower);
|
||||
|
||||
if ((user as IRemoteUser).uri !== self.href) {
|
||||
// if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping.
|
||||
log(`uri missmatch: ${acctLower}`);
|
||||
console.log(`recovery missmatch uri for (username=${username}, host=${host}) from ${(user as IRemoteUser).uri} to ${self.href}`);
|
||||
|
||||
// validate uri
|
||||
const uri = new URL(self.href);
|
||||
if (uri.hostname !== hostAscii) {
|
||||
throw new Error(`Invalied uri`);
|
||||
}
|
||||
|
||||
await User.update({
|
||||
usernameLower,
|
||||
host: host
|
||||
}, {
|
||||
$set: {
|
||||
uri: self.href
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log(`uri is fine: ${acctLower}`);
|
||||
}
|
||||
|
||||
await updatePerson(self.href);
|
||||
|
||||
log(`return resynced remote user: ${acctLower}`);
|
||||
return await User.findOne({ uri: self.href });
|
||||
}
|
||||
|
||||
log(`return existing remote user: ${acctLower}`);
|
||||
return user;
|
||||
};
|
||||
|
||||
async function resolveSelf(acctLower: string) {
|
||||
log(`WebFinger for ${acctLower}`);
|
||||
const finger = await webFinger(acctLower);
|
||||
const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self');
|
||||
if (!self) {
|
||||
throw new Error('self link not found');
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,15 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
|
||||
console.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`);
|
||||
}
|
||||
} catch (e) {
|
||||
rej(e);
|
||||
if (e.name == 'INVALID_PARAM') {
|
||||
rej({
|
||||
code: e.name,
|
||||
param: e.param,
|
||||
reason: e.message
|
||||
});
|
||||
} else {
|
||||
rej(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ export const meta = {
|
||||
}
|
||||
}),
|
||||
|
||||
emojis: $.arr($.obj()).optional.note({
|
||||
desc: {
|
||||
'ja-JP': 'カスタム絵文字定義'
|
||||
}
|
||||
}),
|
||||
|
||||
disableRegistration: $.bool.optional.nullable.note({
|
||||
desc: {
|
||||
'ja-JP': '招待制か否か'
|
||||
@@ -53,6 +59,10 @@ export default (params: any) => new Promise(async (res, rej) => {
|
||||
set.broadcasts = ps.broadcasts;
|
||||
}
|
||||
|
||||
if (ps.emojis) {
|
||||
set.emojis = ps.emojis;
|
||||
}
|
||||
|
||||
if (typeof ps.disableRegistration === 'boolean') {
|
||||
set.disableRegistration = ps.disableRegistration;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ async function fetchAny(uri: string) {
|
||||
// URIがこのサーバーを指しているなら、ローカルユーザーIDとしてDBからフェッチ
|
||||
if (uri.startsWith(config.url + '/')) {
|
||||
const id = new mongo.ObjectID(uri.split('/').pop());
|
||||
const [ user, note ] = await Promise.all([
|
||||
const [user, note] = await Promise.all([
|
||||
User.findOne({ _id: id }),
|
||||
Note.findOne({ _id: id })
|
||||
]);
|
||||
@@ -52,7 +52,7 @@ async function fetchAny(uri: string) {
|
||||
|
||||
// URI(AP Object id)としてDB検索
|
||||
{
|
||||
const [ user, note ] = await Promise.all([
|
||||
const [user, note] = await Promise.all([
|
||||
User.findOne({ uri: uri }),
|
||||
Note.findOne({ uri: uri })
|
||||
]);
|
||||
@@ -68,7 +68,7 @@ async function fetchAny(uri: string) {
|
||||
// /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する
|
||||
// これはDBに存在する可能性があるため再度DB検索
|
||||
if (uri !== object.id) {
|
||||
const [ user, note ] = await Promise.all([
|
||||
const [user, note] = await Promise.all([
|
||||
User.findOne({ uri: object.id }),
|
||||
Note.findOne({ uri: object.id })
|
||||
]);
|
||||
|
||||
@@ -44,6 +44,7 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
|
||||
|
||||
// Response
|
||||
res(await pack(app, null, {
|
||||
detail: true,
|
||||
includeSecret: true
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
|
||||
|
||||
// Send response
|
||||
res(await pack(ap, user, {
|
||||
detail: true,
|
||||
includeSecret: isSecure && ap.userId.equals(user._id)
|
||||
}));
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user