Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -9,6 +9,7 @@ mongodb:
|
|||||||
db: test-misskey
|
db: test-misskey
|
||||||
user: admin
|
user: admin
|
||||||
pass: ''
|
pass: ''
|
||||||
|
# __REDIS__
|
||||||
redis:
|
redis:
|
||||||
host: localhost
|
host: localhost
|
||||||
port: 6379
|
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
|
- npm install
|
||||||
|
|
||||||
# 設定ファイルを配置
|
# 設定ファイルを配置
|
||||||
- cp ./.travis/default.yml ./.config
|
- cp ./.ci/default.yml ./.config
|
||||||
- cp ./.travis/test.yml ./.config
|
- cp ./.ci/test.yml ./.config
|
||||||
|
|
||||||
- travis_wait npm run build
|
- travis_wait npm run build
|
||||||
|
|||||||
24
Dockerfile
24
Dockerfile
@@ -3,8 +3,9 @@ FROM alpine:3.8 AS base
|
|||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
RUN apk add --no-cache nodejs nodejs-npm zlib
|
RUN apk add --no-cache nodejs nodejs-npm zlib
|
||||||
|
RUN npm i -g npm@latest
|
||||||
|
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
COPY . ./
|
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
|
|
||||||
@@ -21,18 +22,23 @@ RUN apk add --no-cache \
|
|||||||
pkgconfig \
|
pkgconfig \
|
||||||
libtool \
|
libtool \
|
||||||
zlib-dev
|
zlib-dev
|
||||||
RUN npm install \
|
RUN npm i -g node-gyp
|
||||||
&& npm install -g node-gyp \
|
|
||||||
&& node-gyp configure \
|
COPY ./package.json ./
|
||||||
&& node-gyp build \
|
RUN npm i
|
||||||
&& npm run build
|
|
||||||
|
COPY . ./
|
||||||
|
RUN node-gyp configure \
|
||||||
|
&& node-gyp build \
|
||||||
|
&& npm run build
|
||||||
|
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
|
|
||||||
COPY --from=builder /misskey/built ./built
|
|
||||||
COPY --from=builder /misskey/node_modules ./node_modules
|
|
||||||
|
|
||||||
RUN apk add --no-cache tini
|
RUN apk add --no-cache tini
|
||||||
ENTRYPOINT ["/sbin/tini", "--"]
|
ENTRYPOINT ["/sbin/tini", "--"]
|
||||||
|
|
||||||
|
COPY --from=builder /misskey/node_modules ./node_modules
|
||||||
|
COPY --from=builder /misskey/built ./built
|
||||||
|
COPY . ./
|
||||||
|
|
||||||
CMD ["npm", "start"]
|
CMD ["npm", "start"]
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -3,6 +3,7 @@
|
|||||||
[](https://misskey.xyz/)
|
[](https://misskey.xyz/)
|
||||||
================================================================
|
================================================================
|
||||||
|
|
||||||
|
[](https://circleci.com/gh/syuilo/misskey)
|
||||||
[![][travis-badge]][travis-link]
|
[![][travis-badge]][travis-link]
|
||||||
[![][dependencies-badge]][dependencies-link]
|
[![][dependencies-badge]][dependencies-link]
|
||||||
[](http://makeapullrequest.com)
|
[](http://makeapullrequest.com)
|
||||||
@@ -71,39 +72,46 @@ Please see [Contribution guide](./CONTRIBUTING.md).
|
|||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
<!-- PATREON_START -->
|
<!-- PATREON_START -->
|
||||||
<table><tr>
|
<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/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/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/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/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://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>
|
</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/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/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/user?u=3384329">べすれい</a></td>
|
||||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</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=12718187">Peter G.</a></td>
|
||||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
|
||||||
</tr></table>
|
</tr></table>
|
||||||
<table><tr>
|
<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/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/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>
|
<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>
|
</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/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/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>
|
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||||
</tr></table>
|
</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 -->
|
<!-- PATREON_END -->
|
||||||
|
|
||||||
:four_leaf_clover: Copyright
|
: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.
|
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`.
|
- 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).
|
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: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "現在のパスワードを入力してください"
|
enter-current-password: "現在のパスワードを入力してください"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "Profil"
|
profile: "Profil"
|
||||||
notification: "Mitteilungen"
|
notification: "Mitteilungen"
|
||||||
apps: "In App öffnen"
|
apps: "In App öffnen"
|
||||||
mute: "Stummschalten"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "Sicherheit"
|
security: "Sicherheit"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "Passwort"
|
password: "Passwort"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "Passwort ändern"
|
reset: "Passwort ändern"
|
||||||
enter-current-password: "Derzeitiges Passwort eingeben"
|
enter-current-password: "Derzeitiges Passwort eingeben"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "Profile"
|
profile: "Profile"
|
||||||
notification: "Notification"
|
notification: "Notification"
|
||||||
apps: "Apps"
|
apps: "Apps"
|
||||||
mute: "Mute"
|
mute-and-block: "Mute / Block"
|
||||||
|
blocking: "Blocking"
|
||||||
security: "Security"
|
security: "Security"
|
||||||
signin: "Sign in history"
|
signin: "Sign in history"
|
||||||
password: "Password"
|
password: "Password"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "Max"
|
max: "Max"
|
||||||
in-use: "In use"
|
in-use: "In use"
|
||||||
stats: "Statistics"
|
stats: "Statistics"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "No muted users"
|
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:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "Change password"
|
reset: "Change password"
|
||||||
enter-current-password: "Enter the current password"
|
enter-current-password: "Enter the current password"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "Mute"
|
mute: "Mute"
|
||||||
muted: "Muting"
|
muted: "Muting"
|
||||||
unmute: "Unmute"
|
unmute: "Unmute"
|
||||||
|
block: "Block"
|
||||||
|
unblock: "Unblock"
|
||||||
|
block-confirm: "Are you sure block this user?"
|
||||||
push-to-a-list: "Add to list"
|
push-to-a-list: "Add to list"
|
||||||
list-pushed: "Successfully added {user} to {list}."
|
list-pushed: "Successfully added {user} to {list}."
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "Timeline"
|
timeline: "Timeline"
|
||||||
media: "Media"
|
media: "Media"
|
||||||
is-suspended: "This account has been suspended."
|
is-suspended: "This account has been suspended."
|
||||||
|
mute: "Mute"
|
||||||
|
unmute: "Unmute"
|
||||||
|
block: "Block"
|
||||||
|
unblock: "Unblock"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "Recent notes"
|
recent-notes: "Recent notes"
|
||||||
images: "Images"
|
images: "Images"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "Perfil"
|
profile: "Perfil"
|
||||||
notification: "Notificación"
|
notification: "Notificación"
|
||||||
apps: "Aplicaciones"
|
apps: "Aplicaciones"
|
||||||
mute: "Silenciar"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "Seguridad"
|
security: "Seguridad"
|
||||||
signin: "Historial de inicios de sesión"
|
signin: "Historial de inicios de sesión"
|
||||||
password: "Contraseña"
|
password: "Contraseña"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "No hay usuarios silenciados"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "Cambiar contraseña"
|
reset: "Cambiar contraseña"
|
||||||
enter-current-password: "Ingresar contraseña actual"
|
enter-current-password: "Ingresar contraseña actual"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ common:
|
|||||||
BSoD:
|
BSoD:
|
||||||
fatal-error: ":( 致命的な問題が発生しました。"
|
fatal-error: ":( 致命的な問題が発生しました。"
|
||||||
update-browser-os: "お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。"
|
update-browser-os: "お使いのブラウザ(またはOS)のバージョンを更新すると解決する可能性があります。"
|
||||||
error-code: "エラーコード"
|
error-code: "Code d’erreur"
|
||||||
browser-version: "ブラウザ バージョン"
|
browser-version: "Version du navigateur"
|
||||||
client-version: "クライアント バージョン"
|
client-version: "La version du client"
|
||||||
email-support: "問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。"
|
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 !"
|
got-it: "J'ai compris !"
|
||||||
customization-tips:
|
customization-tips:
|
||||||
title: "Conseils de personnalisation"
|
title: "Conseils de personnalisation"
|
||||||
@@ -62,7 +62,7 @@ common:
|
|||||||
years_ago: "Il y a {} an·s"
|
years_ago: "Il y a {} an·s"
|
||||||
month-and-day: "{month} mois/{day} jour"
|
month-and-day: "{month} mois/{day} jour"
|
||||||
trash: "Corbeille"
|
trash: "Corbeille"
|
||||||
drive: "ドライブ"
|
drive: "Drive"
|
||||||
weekday-short:
|
weekday-short:
|
||||||
sunday: "D"
|
sunday: "D"
|
||||||
monday: "L"
|
monday: "L"
|
||||||
@@ -124,12 +124,12 @@ common:
|
|||||||
reduce-motion: "Réduire les animations dans l’interface utilisateur"
|
reduce-motion: "Réduire les animations dans l’interface utilisateur"
|
||||||
this-setting-is-this-device-only: "Uniquement sur cet appareil"
|
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.'
|
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: "この投稿情報はコピーです。"
|
is-remote-post: "この投稿情報はコピーです。"
|
||||||
view-on-remote: "正確な情報を見る"
|
view-on-remote: "Consulter le profil complet"
|
||||||
error:
|
error:
|
||||||
title: '問題が発生しました'
|
title: 'Une erreur est survenue'
|
||||||
retry: 'やり直す'
|
retry: 'Réessayer'
|
||||||
reversi:
|
reversi:
|
||||||
drawn: "Partie nulle"
|
drawn: "Partie nulle"
|
||||||
my-turn: "C’est votre tour"
|
my-turn: "C’est votre tour"
|
||||||
@@ -185,7 +185,7 @@ common:
|
|||||||
rename: "Renommer"
|
rename: "Renommer"
|
||||||
stack-left: "Vers la gauche"
|
stack-left: "Vers la gauche"
|
||||||
pop-right: "Vers la droite"
|
pop-right: "Vers la droite"
|
||||||
dev: "アプリの作成に失敗しました。再度お試しください。"
|
dev: "Échec lors de la création de l’application. Veuillez réessayer."
|
||||||
auth/views/form.vue:
|
auth/views/form.vue:
|
||||||
share-access: "Désirez-vous <b>autoriser</b> <i>{{ app.name }}</i> à avoir accès à votre compte ?"
|
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 :"
|
permission-ask: "Cette application nécessite les autorisations suivantes :"
|
||||||
@@ -542,24 +542,24 @@ desktop/views/components/charts.vue:
|
|||||||
title: "Graphiques"
|
title: "Graphiques"
|
||||||
per-day: "par jour"
|
per-day: "par jour"
|
||||||
per-hour: "par heure"
|
per-hour: "par heure"
|
||||||
federation: "フェデレーション"
|
federation: "Fédération"
|
||||||
notes: "Publications"
|
notes: "Publications"
|
||||||
users: "Utilisateurs"
|
users: "Utilisateurs"
|
||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
network: "Réseau"
|
network: "Réseau"
|
||||||
charts:
|
charts:
|
||||||
federation-instances: "インスタンスの増減"
|
federation-instances: "インスタンスの増減"
|
||||||
federation-instances-total: "インスタンスの積算"
|
federation-instances-total: "Nombre total d’instances"
|
||||||
notes: "投稿の増減 (統合)"
|
notes: "投稿の増減 (統合)"
|
||||||
local-notes: "投稿の増減 (ローカル)"
|
local-notes: "投稿の増減 (ローカル)"
|
||||||
remote-notes: "投稿の増減 (リモート)"
|
remote-notes: "投稿の増減 (リモート)"
|
||||||
notes-total: "Total des notes"
|
notes-total: "Total des notes"
|
||||||
users: "Nombre d’utilisateurs·trices : augmentation/diminution"
|
users: "Nombre d’utilisateurs·trices : augmentation/diminution"
|
||||||
users-total: "ユーザーの積算"
|
users-total: "Nombre total des utilisateurs·rices"
|
||||||
drive: "ドライブ使用量の増減"
|
drive: "ドライブ使用量の増減"
|
||||||
drive-total: "ドライブ使用量の積算"
|
drive-total: "Utilisation totale du lecteur"
|
||||||
drive-files: "ドライブのファイル数の増減"
|
drive-files: "ドライブのファイル数の増減"
|
||||||
drive-files-total: "ドライブのファイル数の積算"
|
drive-files-total: "Nombre total de fichiers sur le lecteur"
|
||||||
network-requests: "Requêtes"
|
network-requests: "Requêtes"
|
||||||
network-time: "Temps de réponse"
|
network-time: "Temps de réponse"
|
||||||
network-usage: "Traffic"
|
network-usage: "Traffic"
|
||||||
@@ -679,10 +679,10 @@ desktop/views/components/note.vue:
|
|||||||
reposted-by: "Partagé par {}"
|
reposted-by: "Partagé par {}"
|
||||||
reply: "Répondre"
|
reply: "Répondre"
|
||||||
renote: "Partager"
|
renote: "Partager"
|
||||||
add-reaction: "リアクション"
|
add-reaction: "Ajouter votre réaction"
|
||||||
detail: "詳細"
|
detail: "Détails"
|
||||||
private: "この投稿は非公開です"
|
private: "Cette publication est privée"
|
||||||
deleted: "この投稿は削除されました"
|
deleted: "Cette publication a été supprimée"
|
||||||
desktop/views/components/notes.vue:
|
desktop/views/components/notes.vue:
|
||||||
error: "Échec du chargement."
|
error: "Échec du chargement."
|
||||||
retry: "Réessayer"
|
retry: "Réessayer"
|
||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "Profil"
|
profile: "Profil"
|
||||||
notification: "Notification"
|
notification: "Notification"
|
||||||
apps: "Applications"
|
apps: "Applications"
|
||||||
mute: "Mettre en sourdine"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "Sécurité"
|
security: "Sécurité"
|
||||||
signin: "Historique de connexion"
|
signin: "Historique de connexion"
|
||||||
password: "Mot de Passe"
|
password: "Mot de Passe"
|
||||||
@@ -765,7 +766,7 @@ desktop/views/components/settings.vue:
|
|||||||
deck-default: "デッキをデフォルトのUIにする"
|
deck-default: "デッキをデフォルトのUIにする"
|
||||||
display: "Affichage et design"
|
display: "Affichage et design"
|
||||||
customize: "Personnaliser l'Accueil"
|
customize: "Personnaliser l'Accueil"
|
||||||
wallpaper: "壁紙"
|
wallpaper: "Arrière plan"
|
||||||
choose-wallpaper: "Sélectionner un fond d'écran"
|
choose-wallpaper: "Sélectionner un fond d'écran"
|
||||||
delete-wallpaper: "Supprimer le fond d'écran"
|
delete-wallpaper: "Supprimer le fond d'écran"
|
||||||
dark-mode: "Mode nuit"
|
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"
|
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-clock-on-header: "Afficher l'horloge à droite sur le coté supérieur"
|
||||||
show-reply-target: "Afficher les réponses"
|
show-reply-target: "Afficher les réponses"
|
||||||
timeline: "タイムライン"
|
timeline: "Chronologie"
|
||||||
show-my-renotes: "Afficher mes republications dans le fil"
|
show-my-renotes: "Afficher mes republications dans le fil"
|
||||||
show-renoted-my-notes: "Afficher mes republications dans les fils"
|
show-renoted-my-notes: "Afficher mes republications dans les fils"
|
||||||
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
|
show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
|
||||||
show-maps: "Afficher la carte"
|
show-maps: "Afficher la carte"
|
||||||
deck-column-align: "デッキのカラムの位置"
|
deck-column-align: "デッキのカラムの位置"
|
||||||
deck-column-align-center: "中央"
|
deck-column-align-center: "Centrer"
|
||||||
deck-column-align-left: "左"
|
deck-column-align-left: "À gauche"
|
||||||
sound: "Son"
|
sound: "Son"
|
||||||
enable-sounds: "Activer le 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."
|
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"
|
tools: "Outils"
|
||||||
task-manager: "Gestionnaire de tâches"
|
task-manager: "Gestionnaire de tâches"
|
||||||
third-parties: "Services tiers"
|
third-parties: "Services tiers"
|
||||||
navbar-position: "ナビゲーションバーの位置"
|
navbar-position: "Position de la barre de navigation"
|
||||||
navbar-position-top: "En haut"
|
navbar-position-top: "En haut"
|
||||||
navbar-position-left: "à gauche"
|
navbar-position-left: "à gauche"
|
||||||
navbar-position-right: "à droite"
|
navbar-position-right: "à droite"
|
||||||
@@ -850,25 +851,29 @@ desktop/views/components/settings.2fa.vue:
|
|||||||
common/views/components/api-settings.vue:
|
common/views/components/api-settings.vue:
|
||||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
||||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
||||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
regeneration-of-token: "Si votre jeton est compromis, vous pouvez le régénérer."
|
||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "Régénérer le jeton"
|
||||||
token: "Token:"
|
token: "Jeton :"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "Entrez le mot de passe"
|
||||||
console:
|
console:
|
||||||
title: 'APIコンソール'
|
title: 'Console API'
|
||||||
endpoint: 'エンドポイント'
|
endpoint: 'Point de terminaison'
|
||||||
parameter: 'パラメータ'
|
parameter: 'Paramètres'
|
||||||
send: '送信'
|
send: 'Envoyer'
|
||||||
sending: '応答待ち'
|
sending: 'Envoi en cours'
|
||||||
response: '結果'
|
response: 'Résultat'
|
||||||
desktop/views/components/settings.apps.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "Aucune application autorisée"
|
no-apps: "Aucune application autorisée"
|
||||||
common/views/components/drive-settings.vue:
|
common/views/components/drive-settings.vue:
|
||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "utilisé"
|
||||||
stats: "統計"
|
stats: "Statistiques"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "Aucun utilisateurs mis en sourdine"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "Changer votre mot de passe"
|
reset: "Changer votre mot de passe"
|
||||||
enter-current-password: "Entrez votre mot de passe actuel"
|
enter-current-password: "Entrez votre mot de passe actuel"
|
||||||
@@ -945,8 +950,8 @@ desktop/views/pages/admin/admin.vue:
|
|||||||
dashboard: "Tableau de bord"
|
dashboard: "Tableau de bord"
|
||||||
users: "Utilisateur·rice·s"
|
users: "Utilisateur·rice·s"
|
||||||
update: "Mises à jour"
|
update: "Mises à jour"
|
||||||
announcements: "お知らせ"
|
announcements: "Annonces"
|
||||||
hashtags: "ハッシュタグ"
|
hashtags: "Hashtags"
|
||||||
desktop/views/pages/admin/admin.dashboard.vue:
|
desktop/views/pages/admin/admin.dashboard.vue:
|
||||||
dashboard: "Tableau de bord"
|
dashboard: "Tableau de bord"
|
||||||
all-users: "Toutes les utilisateurrices"
|
all-users: "Toutes les utilisateurrices"
|
||||||
@@ -954,9 +959,9 @@ desktop/views/pages/admin/admin.dashboard.vue:
|
|||||||
all-notes: "Toutes les publications"
|
all-notes: "Toutes les publications"
|
||||||
original-notes: "Publications sur cette instance"
|
original-notes: "Publications sur cette instance"
|
||||||
invite: "Invitation"
|
invite: "Invitation"
|
||||||
banner-url: "Banner URL"
|
banner-url: "URL de la bannière"
|
||||||
disableRegistration: "Disable new user registration"
|
disableRegistration: "Désactiver l’enregistrement de nouveaux utilisateurs·rices"
|
||||||
disableLocalTimeline: "Disable the local timeline"
|
disableLocalTimeline: "Désactiver le fil local"
|
||||||
desktop/views/pages/admin/admin.suspend-user.vue:
|
desktop/views/pages/admin/admin.suspend-user.vue:
|
||||||
suspend-user: "Suspendre un·e utilisateur·rice"
|
suspend-user: "Suspendre un·e utilisateur·rice"
|
||||||
suspend: "Suspendre"
|
suspend: "Suspendre"
|
||||||
@@ -974,22 +979,22 @@ desktop/views/pages/admin/admin.unverify-user.vue:
|
|||||||
unverify: "Ôter la vérification du compte"
|
unverify: "Ôter la vérification du compte"
|
||||||
unverified: "Ce compte n'est pas vérifié"
|
unverified: "Ce compte n'est pas vérifié"
|
||||||
desktop/views/pages/admin/admin.announcements.vue:
|
desktop/views/pages/admin/admin.announcements.vue:
|
||||||
announcements: "お知らせ"
|
announcements: "Annonces"
|
||||||
desktop/views/pages/admin/admin.hashtags.vue:
|
desktop/views/pages/admin/admin.hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Tags cachés"
|
||||||
desktop/views/pages/deck/deck.tl-column.vue:
|
desktop/views/pages/deck/deck.tl-column.vue:
|
||||||
is-media-only: "Les publications médias uniquement"
|
is-media-only: "Les publications médias uniquement"
|
||||||
is-media-view: "Vue média"
|
is-media-view: "Vue média"
|
||||||
edit: "Options"
|
edit: "Options"
|
||||||
desktop/views/pages/deck/deck.user-column.vue:
|
desktop/views/pages/deck/deck.user-column.vue:
|
||||||
posts: "投稿"
|
posts: "Publications"
|
||||||
following: "フォロー"
|
following: "Suit"
|
||||||
followers: "フォロワー"
|
followers: "Abonné·e·s"
|
||||||
images: "画像"
|
images: "Images"
|
||||||
activity: "アクティビティ"
|
activity: "Activité"
|
||||||
timeline: "タイムライン"
|
timeline: "Chronologie"
|
||||||
pinned-notes: "ピン留めされた投稿"
|
pinned-notes: "Publications épinglées"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "Ajouter à la liste"
|
||||||
desktop/views/pages/stats/stats.vue:
|
desktop/views/pages/stats/stats.vue:
|
||||||
all-users: "Toutes les utilisateurrices"
|
all-users: "Toutes les utilisateurrices"
|
||||||
original-users: "Utilisateur·rice·s sur cette instance"
|
original-users: "Utilisateur·rice·s sur cette instance"
|
||||||
@@ -1042,7 +1047,7 @@ desktop/views/pages/user/user.friends.vue:
|
|||||||
no-users: "Pas d'utilisateurs"
|
no-users: "Pas d'utilisateurs"
|
||||||
desktop/views/pages/user/user.vue:
|
desktop/views/pages/user/user.vue:
|
||||||
is-suspended: "Ce compte a été suspendu."
|
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:
|
desktop/views/pages/user/user.photos.vue:
|
||||||
title: "Photos"
|
title: "Photos"
|
||||||
loading: "Chargement en cours"
|
loading: "Chargement en cours"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "Mettre en sourdine"
|
mute: "Mettre en sourdine"
|
||||||
muted: "Muting"
|
muted: "Muting"
|
||||||
unmute: "Enlever la sourdine"
|
unmute: "Enlever la sourdine"
|
||||||
|
block: "Bloquer"
|
||||||
|
unblock: "Débloquer"
|
||||||
|
block-confirm: "Bloquer cet utilisateur ?"
|
||||||
push-to-a-list: "Ajouter à la liste"
|
push-to-a-list: "Ajouter à la liste"
|
||||||
list-pushed: "Vous avez ajouté {user} à la liste {list}."
|
list-pushed: "Vous avez ajouté {user} à la liste {list}."
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1062,10 +1070,10 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "Suit"
|
following: "Suit"
|
||||||
followers: "Abonné·e·s"
|
followers: "Abonné·e·s"
|
||||||
is-bot: "Ce compte est un Bot"
|
is-bot: "Ce compte est un Bot"
|
||||||
years-old: "歳"
|
years-old: "ans d’âge"
|
||||||
year: "年"
|
year: "Année"
|
||||||
month: "月"
|
month: "Mois"
|
||||||
day: "日"
|
day: "Jour"
|
||||||
desktop/views/pages/user/user.timeline.vue:
|
desktop/views/pages/user/user.timeline.vue:
|
||||||
default: "Publications"
|
default: "Publications"
|
||||||
with-replies: "Publications et réponses"
|
with-replies: "Publications et réponses"
|
||||||
@@ -1124,8 +1132,8 @@ mobile/views/components/drive.file-detail.vue:
|
|||||||
hash: "Hash (md5)"
|
hash: "Hash (md5)"
|
||||||
exif: "EXIF"
|
exif: "EXIF"
|
||||||
nsfw: "CW"
|
nsfw: "CW"
|
||||||
mark-as-sensitive: "閲覧注意に設定"
|
mark-as-sensitive: "Marquer comme sensible"
|
||||||
unmark-as-sensitive: "閲覧注意を解除"
|
unmark-as-sensitive: "Ne pas marquer comme sensible"
|
||||||
mobile/views/components/media-image.vue:
|
mobile/views/components/media-image.vue:
|
||||||
sensitive: "Le contenu est NSFW"
|
sensitive: "Le contenu est NSFW"
|
||||||
click-to-show: "Cliquer pour afficher"
|
click-to-show: "Cliquer pour afficher"
|
||||||
@@ -1285,8 +1293,8 @@ mobile/views/pages/settings.vue:
|
|||||||
timeline: "Fil d'actualité"
|
timeline: "Fil d'actualité"
|
||||||
show-reply-target: "Afficher les réponses"
|
show-reply-target: "Afficher les réponses"
|
||||||
show-my-renotes: "Afficher mes republications"
|
show-my-renotes: "Afficher mes republications"
|
||||||
show-renoted-my-notes: "自分の投稿のRenoteを表示する"
|
show-renoted-my-notes: "Afficher mes publications partagées"
|
||||||
show-local-renotes: "ローカルの投稿のRenoteを表示する"
|
show-local-renotes: "Afficher les publications partagées localement"
|
||||||
post-style: "Style de la publication"
|
post-style: "Style de la publication"
|
||||||
post-style-standard: "Standard"
|
post-style-standard: "Standard"
|
||||||
post-style-smart: "Intelligent"
|
post-style-smart: "Intelligent"
|
||||||
@@ -1319,7 +1327,7 @@ mobile/views/pages/settings.vue:
|
|||||||
signout: "Déconnexion"
|
signout: "Déconnexion"
|
||||||
sound: "Sons"
|
sound: "Sons"
|
||||||
enable-sounds: "Activer les 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:
|
mobile/views/pages/user.vue:
|
||||||
follows-you: "Vous suit"
|
follows-you: "Vous suit"
|
||||||
following: "Abonnements"
|
following: "Abonnements"
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "Fil d'actualité"
|
timeline: "Fil d'actualité"
|
||||||
media: "Media"
|
media: "Media"
|
||||||
is-suspended: "This account has been suspended."
|
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:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "Notes récentes"
|
recent-notes: "Notes récentes"
|
||||||
images: "Images"
|
images: "Images"
|
||||||
@@ -1375,28 +1387,28 @@ docs:
|
|||||||
dev/views/index.vue:
|
dev/views/index.vue:
|
||||||
manage-apps: "Gestion des applications"
|
manage-apps: "Gestion des applications"
|
||||||
dev/views/apps.vue:
|
dev/views/apps.vue:
|
||||||
manage-apps: "アプリを管理"
|
manage-apps: "Gestion des applications"
|
||||||
create-app: "アプリ作成"
|
create-app: "Créer une app"
|
||||||
app-missing: "アプリなし"
|
app-missing: "Aucune application"
|
||||||
dev/views/new-app.vue:
|
dev/views/new-app.vue:
|
||||||
create-app: "アプリケーションの作成"
|
create-app: "Création d’une application"
|
||||||
app-name: "アプリケーション名"
|
app-name: "Nom de l’application"
|
||||||
app-name-desc: "あなたのアプリの名称。"
|
app-name-desc: "Le nom de votre application"
|
||||||
app-name-ex: "ex) Misskey for iOS"
|
app-name-ex: "p. ex. Misskey pour iOS"
|
||||||
app-overview: "アプリの概要"
|
app-overview: "Description courte de l’application"
|
||||||
app-desc: "あなたのアプリの簡単な説明や紹介。"
|
app-desc: "Brève description introductive à votre application."
|
||||||
app-desc-ex: "ex) Misskey iOSクライアント。"
|
app-desc-ex: "p. ex) Misskey pour iOS"
|
||||||
callback-url: "コールバックURL (オプション)"
|
callback-url: "コールバックURL (オプション)"
|
||||||
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
|
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
|
||||||
authority: "権限"
|
authority: "Autorisations "
|
||||||
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
|
authority-desc: "ここで要求した機能だけがAPIからアクセスできます。"
|
||||||
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"
|
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"
|
||||||
account-read: "アカウントの情報を見る。"
|
account-read: "Afficher les informations du compte"
|
||||||
account-write: "アカウントの情報を操作する。"
|
account-write: "Modifications des informations du compte"
|
||||||
note-write: "投稿する。"
|
note-write: "Publications."
|
||||||
reaction-write: "リアクションしたりリアクションをキャンセルする。"
|
reaction-write: "Ajout et suppression de réactions."
|
||||||
following-write: "フォローしたりフォロー解除する。"
|
following-write: "S’abonner et se désabonner."
|
||||||
drive-read: "ドライブを見る。"
|
drive-read: "Lecture du Drive."
|
||||||
drive-write: "ドライブを操作する。"
|
drive-write: "Téléversement/suppression des fichiers de votre Lecteur."
|
||||||
notification-read: "通知を見る。"
|
notification-read: "Lire vos notifications."
|
||||||
notification-write: "通知を操作する。"
|
notification-write: "Gestion de vos notifications."
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "現在のパスワードを入力してください"
|
enter-current-password: "現在のパスワードを入力してください"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
@@ -832,7 +832,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -973,8 +974,12 @@ common/views/components/drive-settings.vue:
|
|||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
|
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
|
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
@@ -1203,6 +1208,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
|
|
||||||
@@ -1531,6 +1539,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "守護神セキュリティ"
|
security: "守護神セキュリティ"
|
||||||
signin: "こんな感じでサインインしたらしいで"
|
signin: "こんな感じでサインインしたらしいで"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -848,27 +849,31 @@ desktop/views/components/settings.2fa.vue:
|
|||||||
failed: "なんか設定に失敗したで。トークンを間違えとらんか確認してや。"
|
failed: "なんか設定に失敗したで。トークンを間違えとらんか確認してや。"
|
||||||
info: "次のサインインからは、パスワードに加えてデバイスに出とるトークンを入力してな。"
|
info: "次のサインインからは、パスワードに加えてデバイスに出とるトークンを入力してな。"
|
||||||
common/views/components/api-settings.vue:
|
common/views/components/api-settings.vue:
|
||||||
intro: "APIを利用するには、上記のトークンを「i」というキーでパラメータに付加してリクエストします。"
|
intro: "API使うんやったらこのトークンを「i」っちゅうパラメータにくっつけてリクエストできるで。"
|
||||||
caution: "アカウントを不正利用される可能性があるため、このトークンは第三者に教えないでください(アプリなどにも入力しないでください)。"
|
caution: "アカウント勝手にいじられるかも知れんから、このトークンは教えたらあかんし、アプリにも書いたらあかんで(これはフリちゃうで)"
|
||||||
regeneration-of-token: "万が一このトークンが漏れたりその可能性がある場合はトークンを再生成できます。"
|
regeneration-of-token: "トークン漏れてもうたんやったらもっかい生成できるで。"
|
||||||
regenerate-token: "トークンを再生成"
|
regenerate-token: "トークンもっかい生成"
|
||||||
token: "Token:"
|
token: "Token:"
|
||||||
enter-password: "パスワードを入力してください"
|
enter-password: "パスワードを入れてや"
|
||||||
console:
|
console:
|
||||||
title: 'APIコンソール'
|
title: 'APIコンソール'
|
||||||
endpoint: 'エンドポイント'
|
endpoint: 'エンドポイント'
|
||||||
parameter: 'パラメータ'
|
parameter: 'パラメータ'
|
||||||
send: '送信'
|
send: '送る'
|
||||||
sending: '応答待ち'
|
sending: '応答待っとる'
|
||||||
response: '結果'
|
response: 'こんなん返ってきたわ'
|
||||||
desktop/views/components/settings.apps.vue:
|
desktop/views/components/settings.apps.vue:
|
||||||
no-apps: "連携しているアプリケーションはあらへんで"
|
no-apps: "連携しているアプリケーションはあらへんで"
|
||||||
common/views/components/drive-settings.vue:
|
common/views/components/drive-settings.vue:
|
||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使うとる"
|
in-use: "使うとる"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはおらんで"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "今のパスワードを入れてや"
|
enter-current-password: "今のパスワードを入れてや"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしとるで"
|
muted: "ミュートしとるで"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加したで。"
|
list-pushed: "{user}を{list}に追加したで。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーはあかんわ。凍結されとる。"
|
is-suspended: "このユーザーはあかんわ。凍結されとる。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近儲かりまっか?"
|
recent-notes: "最近儲かりまっか?"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "現在のパスワードを入力してください"
|
enter-current-password: "現在のパスワードを入力してください"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "Profiel"
|
profile: "Profiel"
|
||||||
notification: "Melding"
|
notification: "Melding"
|
||||||
apps: "Apps"
|
apps: "Apps"
|
||||||
mute: "Dempen"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "Beveiliging"
|
security: "Beveiliging"
|
||||||
signin: "Inloggeschiedenis"
|
signin: "Inloggeschiedenis"
|
||||||
password: "Wachtwoord"
|
password: "Wachtwoord"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "Geen gedempte gebruikers"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "Wachtwoord wijzigen"
|
reset: "Wachtwoord wijzigen"
|
||||||
enter-current-password: "Voer je huidige wachtwoord in"
|
enter-current-password: "Voer je huidige wachtwoord in"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "Dempen"
|
mute: "Dempen"
|
||||||
muted: "Dempend"
|
muted: "Dempend"
|
||||||
unmute: "Ontdempen"
|
unmute: "Ontdempen"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "Tijdlijn"
|
timeline: "Tijdlijn"
|
||||||
media: "Media"
|
media: "Media"
|
||||||
is-suspended: "Dit account is geschorst."
|
is-suspended: "Dit account is geschorst."
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "Recente notities"
|
recent-notes: "Recente notities"
|
||||||
images: "Afbeeldingen"
|
images: "Afbeeldingen"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "Notifikasjon"
|
notification: "Notifikasjon"
|
||||||
apps: "Apper"
|
apps: "Apper"
|
||||||
mute: "Demp"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "Passord"
|
password: "Passord"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "現在のパスワードを入力してください"
|
enter-current-password: "現在のパスワードを入力してください"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "Media"
|
media: "Media"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "Nylige innlegg"
|
recent-notes: "Nylige innlegg"
|
||||||
images: "Bilder"
|
images: "Bilder"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "Profil"
|
profile: "Profil"
|
||||||
notification: "Powiadomienia"
|
notification: "Powiadomienia"
|
||||||
apps: "Aplikacje"
|
apps: "Aplikacje"
|
||||||
mute: "Wyciszanie"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "Bezpieczeństwo"
|
security: "Bezpieczeństwo"
|
||||||
signin: "Historia logowań"
|
signin: "Historia logowań"
|
||||||
password: "Hasło"
|
password: "Hasło"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "Brak wyciszonych użytkowników"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "Zmień hasło"
|
reset: "Zmień hasło"
|
||||||
enter-current-password: "Wprowadź obecne hasło"
|
enter-current-password: "Wprowadź obecne hasło"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "Wycisz"
|
mute: "Wycisz"
|
||||||
muted: "Wyciszyłeś"
|
muted: "Wyciszyłeś"
|
||||||
unmute: "Cofnij wyciszenie"
|
unmute: "Cofnij wyciszenie"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "Dodaj do listy"
|
push-to-a-list: "Dodaj do listy"
|
||||||
list-pushed: "Dodałeś(-aś) {user} do {list}."
|
list-pushed: "Dodałeś(-aś) {user} do {list}."
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "Oś czasu"
|
timeline: "Oś czasu"
|
||||||
media: "Multimedia"
|
media: "Multimedia"
|
||||||
is-suspended: "To konto zostało zablokowane"
|
is-suspended: "To konto zostało zablokowane"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "Ostatnie wpisy"
|
recent-notes: "Ostatnie wpisy"
|
||||||
images: "Zdjęcia"
|
images: "Zdjęcia"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "現在のパスワードを入力してください"
|
enter-current-password: "現在のパスワードを入力してください"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "Linha do tempo"
|
timeline: "Linha do tempo"
|
||||||
media: "Mídia"
|
media: "Mídia"
|
||||||
is-suspended: "Esta conta foi suspensa"
|
is-suspended: "Esta conta foi suspensa"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "Notas recentes"
|
recent-notes: "Notas recentes"
|
||||||
images: "Imagens"
|
images: "Imagens"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "現在のパスワードを入力してください"
|
enter-current-password: "現在のパスワードを入力してください"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
@@ -741,7 +741,8 @@ desktop/views/components/settings.vue:
|
|||||||
profile: "プロフィール"
|
profile: "プロフィール"
|
||||||
notification: "通知"
|
notification: "通知"
|
||||||
apps: "アプリ"
|
apps: "アプリ"
|
||||||
mute: "ミュート"
|
mute-and-block: "ミュート/ブロック"
|
||||||
|
blocking: "ブロック"
|
||||||
security: "セキュリティ"
|
security: "セキュリティ"
|
||||||
signin: "サインイン履歴"
|
signin: "サインイン履歴"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -867,8 +868,12 @@ common/views/components/drive-settings.vue:
|
|||||||
max: "容量"
|
max: "容量"
|
||||||
in-use: "使用中"
|
in-use: "使用中"
|
||||||
stats: "統計"
|
stats: "統計"
|
||||||
desktop/views/components/settings.mute.vue:
|
common/views/components/mute-and-block.vue:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
mute-and-block: "ミュートとブロック"
|
||||||
|
mute: "ミュート"
|
||||||
|
block: "ブロック"
|
||||||
|
no-muted-users: "ミュートしているユーザーはいません"
|
||||||
|
no-blocked-users: "ブロックしているユーザーはいません"
|
||||||
desktop/views/components/settings.password.vue:
|
desktop/views/components/settings.password.vue:
|
||||||
reset: "パスワードを変更する"
|
reset: "パスワードを変更する"
|
||||||
enter-current-password: "現在のパスワードを入力してください"
|
enter-current-password: "現在のパスワードを入力してください"
|
||||||
@@ -1055,6 +1060,9 @@ desktop/views/pages/user/user.profile.vue:
|
|||||||
mute: "ミュートする"
|
mute: "ミュートする"
|
||||||
muted: "ミュートしています"
|
muted: "ミュートしています"
|
||||||
unmute: "ミュート解除"
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロックする"
|
||||||
|
unblock: "ブロック解除"
|
||||||
|
block-confirm: "このユーザーをブロックしますか?"
|
||||||
push-to-a-list: "リストに追加"
|
push-to-a-list: "リストに追加"
|
||||||
list-pushed: "{user}を{list}に追加しました。"
|
list-pushed: "{user}を{list}に追加しました。"
|
||||||
desktop/views/pages/user/user.header.vue:
|
desktop/views/pages/user/user.header.vue:
|
||||||
@@ -1329,6 +1337,10 @@ mobile/views/pages/user.vue:
|
|||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
is-suspended: "このユーザーは凍結されています。"
|
is-suspended: "このユーザーは凍結されています。"
|
||||||
|
mute: "ミュート"
|
||||||
|
unmute: "ミュート解除"
|
||||||
|
block: "ブロック"
|
||||||
|
unblock: "ブロック解除"
|
||||||
mobile/views/pages/user/home.vue:
|
mobile/views/pages/user/home.vue:
|
||||||
recent-notes: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
|
|||||||
75
package-lock.json
generated
75
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "10.32.0",
|
"version": "10.36.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -691,9 +691,12 @@
|
|||||||
"integrity": "sha512-XWwqRvaSsf4yq/4SYL5/7n5i2RWqf+jtIRlasj65cuZZDZ01wtkDfAIxrEpYcLvzT1HMqBmsnMEcZjK+OMeojQ=="
|
"integrity": "sha512-XWwqRvaSsf4yq/4SYL5/7n5i2RWqf+jtIRlasj65cuZZDZ01wtkDfAIxrEpYcLvzT1HMqBmsnMEcZjK+OMeojQ=="
|
||||||
},
|
},
|
||||||
"@types/speakeasy": {
|
"@types/speakeasy": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.3.tgz",
|
||||||
"integrity": "sha512-h8KW3wSd3/l4oKRGYPxExCaos5VmjcnwDG3RK25tfcoWQR9iLmM9UbwvF1Pd+UT5aY1Z3LdQGt4xU0u9Zk/C2Q=="
|
"integrity": "sha512-lDc49Aec4WnQPRhW3n90ct14CH0Zyrj48RGMK1SSQP6BOf8HamWg+PG9uj1DVnaa6t+lhQU1j1lhGA7d9baxWw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"@types/superagent": {
|
"@types/superagent": {
|
||||||
"version": "3.8.4",
|
"version": "3.8.4",
|
||||||
@@ -1259,9 +1262,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apexcharts": {
|
"apexcharts": {
|
||||||
"version": "2.1.6",
|
"version": "2.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-2.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-2.1.9.tgz",
|
||||||
"integrity": "sha512-kIb4Q07bWwTGuTWhyzhDAOz6nrltDgyP8VUUwqetxr0o11mNH6PA6YVnR/e9nyd9HU6q3bFZN8eVuSatnqdxAQ==",
|
"integrity": "sha512-Qs/jLUa03wqPR53yMk8QAwq+qrX/Odc3IIXH2WVVjdWyFXS1lYzGSDbVcVDnOKkxoLdAlzPI3icb2bMjskwfXQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
"core-js": "^2.5.7",
|
"core-js": "^2.5.7",
|
||||||
@@ -3332,12 +3335,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data-urls": {
|
"data-urls": {
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
|
||||||
"integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==",
|
"integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abab": "^2.0.0",
|
"abab": "^2.0.0",
|
||||||
"whatwg-mimetype": "^2.1.0",
|
"whatwg-mimetype": "^2.2.0",
|
||||||
"whatwg-url": "^7.0.0"
|
"whatwg-url": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4755,9 +4758,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint": {
|
"eslint": {
|
||||||
"version": "5.7.0",
|
"version": "5.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.8.0.tgz",
|
||||||
"integrity": "sha512-zYCeFQahsxffGl87U2aJ7DPyH8CbWgxBC213Y8+TCanhUTf2gEvfq3EKpHmEcozTLyPmGe9LZdMAwC/CpJBM5A==",
|
"integrity": "sha512-Zok6Bru3y2JprqTNm14mgQ15YQu/SMDkWdnmHfFg770DIUlmMFd/gqqzCHekxzjHZJxXv3tmTpH0C1icaYJsRQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/code-frame": "^7.0.0",
|
"@babel/code-frame": "^7.0.0",
|
||||||
"ajv": "^6.5.3",
|
"ajv": "^6.5.3",
|
||||||
@@ -5311,9 +5314,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"file-type": {
|
"file-type": {
|
||||||
"version": "10.1.0",
|
"version": "10.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.2.0.tgz",
|
||||||
"integrity": "sha512-fkjfXnqBRrdUFTS6opakWyMXb+uzDv8zOCqjSOWPbzMLnYnmnUEv/RNY9igkk4nc8TVL44Xd1OCC2fJXH3eb7Q=="
|
"integrity": "sha512-eqX81S1PWdLDPW39yyB214TVVOsUQjSmPcyUjeVH6ksH+94Y2YA/ItiIwa53rJiSofJZLK6lGsuCE3rwt0vp4w=="
|
||||||
},
|
},
|
||||||
"filename-regex": {
|
"filename-regex": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -8351,9 +8354,9 @@
|
|||||||
"integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ=="
|
"integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ=="
|
||||||
},
|
},
|
||||||
"jsdom": {
|
"jsdom": {
|
||||||
"version": "12.2.0",
|
"version": "13.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-12.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.0.0.tgz",
|
||||||
"integrity": "sha512-QPOggIJ8fquWPLaYYMoh+zqUmdphDtu1ju0QGTitZT1Yd8I5qenPpXM1etzUegu3MjVp8XPzgZxdn8Yj7e40ig==",
|
"integrity": "sha512-Kmq4ASMNkgpY+YufE322EnIKoiz0UWY2DRkKlU7d5YrIW4xiVRhWFrZV1fr6w/ZNxQ50wGAH5gGRzydgnmkkvw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abab": "^2.0.0",
|
"abab": "^2.0.0",
|
||||||
"acorn": "^6.0.2",
|
"acorn": "^6.0.2",
|
||||||
@@ -8374,6 +8377,7 @@
|
|||||||
"symbol-tree": "^3.2.2",
|
"symbol-tree": "^3.2.2",
|
||||||
"tough-cookie": "^2.4.3",
|
"tough-cookie": "^2.4.3",
|
||||||
"w3c-hr-time": "^1.0.1",
|
"w3c-hr-time": "^1.0.1",
|
||||||
|
"w3c-xmlserializer": "^1.0.0",
|
||||||
"webidl-conversions": "^4.0.2",
|
"webidl-conversions": "^4.0.2",
|
||||||
"whatwg-encoding": "^1.0.5",
|
"whatwg-encoding": "^1.0.5",
|
||||||
"whatwg-mimetype": "^2.2.0",
|
"whatwg-mimetype": "^2.2.0",
|
||||||
@@ -15569,9 +15573,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-loader": {
|
"ts-loader": {
|
||||||
"version": "5.2.2",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.3.0.tgz",
|
||||||
"integrity": "sha512-vM/TrEKXBqRYq5yLatsXyKFnYSpv53klmGtrILGlNqcMsxPVi8+e4yr1Agbu9oMZepx/4szDVn5QpFo83IQdQg==",
|
"integrity": "sha512-lGSNs7szRFj/rK9T1EQuayE3QNLg6izDUxt5jpmq0RG1rU2bapAt7E7uLckLCUPeO1jwxCiet2oRaWovc53UAg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"chalk": "^2.3.0",
|
"chalk": "^2.3.0",
|
||||||
"enhanced-resolve": "^4.0.0",
|
"enhanced-resolve": "^4.0.0",
|
||||||
@@ -15683,16 +15687,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.4.tgz",
|
||||||
"integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA=="
|
"integrity": "sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q=="
|
||||||
},
|
},
|
||||||
"typescript-eslint-parser": {
|
"typescript-eslint-parser": {
|
||||||
"version": "20.0.0",
|
"version": "20.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-20.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-20.1.1.tgz",
|
||||||
"integrity": "sha512-HZEoGA+LnS3etUlVAPX6I8sZ7872Yb0vPvQv6QDCIE44KD3bFmvPEQ4LbiD+qGkcxh6segjVK0v3rxpb2R6oSA==",
|
"integrity": "sha512-IJhpqHK60Pz2J5pe8rJUQ10DcMcGwhQnvRFcPV79coEV3bpNfSiHkgpS+sf6zx2ANDWgBhmtZbK9ICOy+v3FKA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"eslint": "4.19.1",
|
"eslint": "4.19.1",
|
||||||
|
"eslint-visitor-keys": "^1.0.0",
|
||||||
"typescript-estree": "2.1.0"
|
"typescript-estree": "2.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -15825,7 +15830,7 @@
|
|||||||
},
|
},
|
||||||
"fast-deep-equal": {
|
"fast-deep-equal": {
|
||||||
"version": "1.1.0",
|
"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="
|
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
|
||||||
},
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
@@ -16775,6 +16780,16 @@
|
|||||||
"browser-process-hrtime": "^0.1.2"
|
"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": {
|
"ware": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz",
|
||||||
|
|||||||
27
package.json
27
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.33.0",
|
"version": "10.36.1",
|
||||||
"clientVersion": "1.0.11172",
|
"clientVersion": "1.0.11311",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"@types/sharp": "0.21.0",
|
"@types/sharp": "0.21.0",
|
||||||
"@types/showdown": "1.7.5",
|
"@types/showdown": "1.7.5",
|
||||||
"@types/single-line-log": "1.1.0",
|
"@types/single-line-log": "1.1.0",
|
||||||
"@types/speakeasy": "2.0.2",
|
"@types/speakeasy": "2.0.3",
|
||||||
"@types/systeminformation": "3.23.0",
|
"@types/systeminformation": "3.23.0",
|
||||||
"@types/tinycolor2": "1.4.1",
|
"@types/tinycolor2": "1.4.1",
|
||||||
"@types/tmp": "0.0.33",
|
"@types/tmp": "0.0.33",
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
"@types/websocket": "0.0.40",
|
"@types/websocket": "0.0.40",
|
||||||
"@types/ws": "6.0.1",
|
"@types/ws": "6.0.1",
|
||||||
"animejs": "2.2.0",
|
"animejs": "2.2.0",
|
||||||
"apexcharts": "2.1.6",
|
"apexcharts": "2.1.9",
|
||||||
"autobind-decorator": "2.1.0",
|
"autobind-decorator": "2.1.0",
|
||||||
"autosize": "4.0.2",
|
"autosize": "4.0.2",
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
@@ -108,11 +108,11 @@
|
|||||||
"elasticsearch": "15.1.1",
|
"elasticsearch": "15.1.1",
|
||||||
"emojilib": "2.3.0",
|
"emojilib": "2.3.0",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"eslint": "5.7.0",
|
"eslint": "5.8.0",
|
||||||
"eslint-plugin-vue": "4.7.1",
|
"eslint-plugin-vue": "4.7.1",
|
||||||
"eventemitter3": "3.1.0",
|
"eventemitter3": "3.1.0",
|
||||||
"file-loader": "2.0.0",
|
"file-loader": "2.0.0",
|
||||||
"file-type": "10.1.0",
|
"file-type": "10.2.0",
|
||||||
"fuckadblock": "3.2.1",
|
"fuckadblock": "3.2.1",
|
||||||
"gulp": "3.9.1",
|
"gulp": "3.9.1",
|
||||||
"gulp-cssnano": "2.1.3",
|
"gulp-cssnano": "2.1.3",
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
"is-root": "2.0.0",
|
"is-root": "2.0.0",
|
||||||
"is-url": "1.2.4",
|
"is-url": "1.2.4",
|
||||||
"js-yaml": "3.12.0",
|
"js-yaml": "3.12.0",
|
||||||
"jsdom": "12.2.0",
|
"jsdom": "13.0.0",
|
||||||
"json5": "2.1.0",
|
"json5": "2.1.0",
|
||||||
"json5-loader": "1.0.1",
|
"json5-loader": "1.0.1",
|
||||||
"koa": "2.6.1",
|
"koa": "2.6.1",
|
||||||
@@ -201,11 +201,11 @@
|
|||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"tinycolor2": "1.4.1",
|
"tinycolor2": "1.4.1",
|
||||||
"tmp": "0.0.33",
|
"tmp": "0.0.33",
|
||||||
"ts-loader": "5.2.2",
|
"ts-loader": "5.3.0",
|
||||||
"ts-node": "7.0.1",
|
"ts-node": "7.0.1",
|
||||||
"tslint": "5.10.0",
|
"tslint": "5.10.0",
|
||||||
"typescript": "3.1.3",
|
"typescript": "3.1.4",
|
||||||
"typescript-eslint-parser": "20.0.0",
|
"typescript-eslint-parser": "20.1.1",
|
||||||
"uglify-es": "3.3.9",
|
"uglify-es": "3.3.9",
|
||||||
"url-loader": "1.1.2",
|
"url-loader": "1.1.2",
|
||||||
"uuid": "3.3.2",
|
"uuid": "3.3.2",
|
||||||
@@ -234,12 +234,5 @@
|
|||||||
"websocket": "1.0.28",
|
"websocket": "1.0.28",
|
||||||
"ws": "6.1.0",
|
"ws": "6.1.0",
|
||||||
"xev": "2.0.1"
|
"xev": "2.0.1"
|
||||||
},
|
|
||||||
"greenkeeper": {
|
|
||||||
"ignore": [
|
|
||||||
"deepcopy",
|
|
||||||
"cafy",
|
|
||||||
"@types/gulp"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
import muteAndBlock from './mute-and-block.vue';
|
||||||
|
import error from './error.vue';
|
||||||
import apiSettings from './api-settings.vue';
|
import apiSettings from './api-settings.vue';
|
||||||
import driveSettings from './drive-settings.vue';
|
import driveSettings from './drive-settings.vue';
|
||||||
import profileEditor from './profile-editor.vue';
|
import profileEditor from './profile-editor.vue';
|
||||||
@@ -49,6 +51,8 @@ import uiInfo from './ui/info.vue';
|
|||||||
import formButton from './ui/form/button.vue';
|
import formButton from './ui/form/button.vue';
|
||||||
import formRadio from './ui/form/radio.vue';
|
import formRadio from './ui/form/radio.vue';
|
||||||
|
|
||||||
|
Vue.component('mk-mute-and-block', muteAndBlock);
|
||||||
|
Vue.component('mk-error', error);
|
||||||
Vue.component('mk-api-settings', apiSettings);
|
Vue.component('mk-api-settings', apiSettings);
|
||||||
Vue.component('mk-drive-settings', driveSettings);
|
Vue.component('mk-drive-settings', driveSettings);
|
||||||
Vue.component('mk-profile-editor', profileEditor);
|
Vue.component('mk-profile-editor', profileEditor);
|
||||||
|
|||||||
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">
|
<header class="bvonvjxbwzaiskogyhbwgyxvcgserpmu">
|
||||||
<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle == 'smart'"/>
|
<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>
|
<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-admin" v-if="note.user.isAdmin">admin</span>
|
||||||
<span class="is-bot" v-if="note.user.isBot">bot</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="is-cat" v-if="note.user.isCat">cat</span>
|
||||||
<span class="username"><mk-acct :user="note.user"/></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">
|
<div class="info">
|
||||||
<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span>
|
<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>
|
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
|
||||||
@@ -68,10 +68,6 @@ export default Vue.extend({
|
|||||||
&:hover
|
&:hover
|
||||||
text-decoration underline
|
text-decoration underline
|
||||||
|
|
||||||
> .is-verified
|
|
||||||
margin-right 8px
|
|
||||||
color #4dabf7
|
|
||||||
|
|
||||||
> .is-admin
|
> .is-admin
|
||||||
> .is-bot
|
> .is-bot
|
||||||
> .is-cat
|
> .is-cat
|
||||||
@@ -95,6 +91,10 @@ export default Vue.extend({
|
|||||||
color var(--noteHeaderAcct)
|
color var(--noteHeaderAcct)
|
||||||
flex-shrink 2147483647
|
flex-shrink 2147483647
|
||||||
|
|
||||||
|
> .is-verified
|
||||||
|
margin 0 .5em 0 0
|
||||||
|
color #4dabf7
|
||||||
|
|
||||||
> .info
|
> .info
|
||||||
margin-left auto
|
margin-left auto
|
||||||
font-size 0.9em
|
font-size 0.9em
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ymxyweixqwsxauxldgpvecjepnwxbylu" :class="{ warn }">
|
<div class="ymxyweixqwsxauxldgpvecjepnwxbylu" :class="{ warn }">
|
||||||
<i v-if="warn">%fa:exclamation-triangle%</i>
|
<i v-if="warn">%fa:exclamation-triangle%</i>
|
||||||
|
<i v-else>%fa:info-circle%</i>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -23,11 +24,20 @@ export default Vue.extend({
|
|||||||
margin 16px 0
|
margin 16px 0
|
||||||
padding 16px
|
padding 16px
|
||||||
font-size 90%
|
font-size 90%
|
||||||
|
background var(--infoBg)
|
||||||
> i
|
color var(--infoFg)
|
||||||
margin-right 4px
|
|
||||||
|
|
||||||
&.warn
|
&.warn
|
||||||
background var(--infoWarnBg)
|
background var(--infoWarnBg)
|
||||||
color var(--infoWarnFg)
|
color var(--infoWarnFg)
|
||||||
|
|
||||||
|
&:first-child
|
||||||
|
margin-top 0
|
||||||
|
|
||||||
|
&:last-child
|
||||||
|
margin-bottom 0
|
||||||
|
|
||||||
|
> i
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ export default Vue.extend({
|
|||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||||
this.connection.on('follow', this.onFollow);
|
this.connection.on('follow', this.onFollowChange);
|
||||||
this.connection.on('unfollow', this.onUnfollow);
|
this.connection.on('unfollow', this.onFollowChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
@@ -49,17 +49,11 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onFollow(user) {
|
onFollowChange(user) {
|
||||||
if (user.id == this.u.id) {
|
|
||||||
this.u.isFollowing = user.isFollowing;
|
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onUnfollow(user) {
|
|
||||||
if (user.id == this.u.id) {
|
if (user.id == this.u.id) {
|
||||||
this.u.isFollowing = user.isFollowing;
|
this.u.isFollowing = user.isFollowing;
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||||
|
this.$forceUpdate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,7 @@
|
|||||||
|
|
||||||
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
|
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
|
||||||
|
|
||||||
<div v-if="!fetching && requestInitPromise != null" class="error">
|
<mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
|
||||||
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
|
|
||||||
<ui-button @click="resolveInitPromise">%i18n:common.error.retry%</ui-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="placeholder" v-if="fetching">
|
<div class="placeholder" v-if="fetching">
|
||||||
<template v-for="i in 10">
|
<template v-for="i in 10">
|
||||||
@@ -215,16 +212,6 @@ export default Vue.extend({
|
|||||||
> *
|
> *
|
||||||
transition transform .3s ease, opacity .3s ease
|
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
|
> .placeholder
|
||||||
padding 32px
|
padding 32px
|
||||||
opacity 0.3
|
opacity 0.3
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<div class="none ui info" v-if="!fetching && apps.length == 0">
|
<ui-info v-if="!fetching && apps.length == 0">%i18n:@no-apps%</ui-info>
|
||||||
<p>%fa:info-circle%%i18n:@no-apps%</p>
|
|
||||||
</div>
|
|
||||||
<div class="apps" v-if="apps.length != 0">
|
<div class="apps" v-if="apps.length != 0">
|
||||||
<div v-for="app in apps">
|
<div v-for="app in apps">
|
||||||
<p><b>{{ app.name }}</b></p>
|
<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 == '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 == '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 == '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 == '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 == 'security' }" @mousedown="page = 'security'">%fa:unlock-alt .fw%%i18n:@security%</p>
|
||||||
<p :class="{ active: page == 'api' }" @mousedown="page = 'api'">%fa:key .fw%API</p>
|
<p :class="{ active: page == 'api' }" @mousedown="page = 'api'">%fa:key .fw%API</p>
|
||||||
@@ -200,12 +200,9 @@
|
|||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
|
||||||
<ui-card class="mute" v-show="page == 'mute'">
|
<div class="muteAndBlock" v-show="page == 'muteAndBlock'">
|
||||||
<div slot="title">%fa:ban% %i18n:@mute%</div>
|
<mk-mute-and-block/>
|
||||||
<section>
|
</div>
|
||||||
<x-mute/>
|
|
||||||
</section>
|
|
||||||
</ui-card>
|
|
||||||
|
|
||||||
<ui-card class="apps" v-show="page == 'apps'">
|
<ui-card class="apps" v-show="page == 'apps'">
|
||||||
<div slot="title">%fa:puzzle-piece% %i18n:@apps%</div>
|
<div slot="title">%fa:puzzle-piece% %i18n:@apps%</div>
|
||||||
@@ -289,7 +286,6 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import XMute from './settings.mute.vue';
|
|
||||||
import XPassword from './settings.password.vue';
|
import XPassword from './settings.password.vue';
|
||||||
import X2fa from './settings.2fa.vue';
|
import X2fa from './settings.2fa.vue';
|
||||||
import XApps from './settings.apps.vue';
|
import XApps from './settings.apps.vue';
|
||||||
@@ -300,7 +296,6 @@ import checkForUpdate from '../../../common/scripts/check-for-update';
|
|||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XMute,
|
|
||||||
XPassword,
|
XPassword,
|
||||||
X2fa,
|
X2fa,
|
||||||
XApps,
|
XApps,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow, active, isStacked, draghover, dragging, dropready }"
|
<div class="dnpfarvgbnfmyzbdquhhzyxcmstpdqzs" :class="{ naked, narrow, active, isStacked, draghover, dragging, dropready }"
|
||||||
@dragover.prevent.stop="onDragover"
|
@dragenter.prevent.stop="onDragenter"
|
||||||
@dragleave="onDragleave"
|
@dragleave="onDragleave"
|
||||||
@drop.prevent.stop="onDrop"
|
@drop.prevent.stop="onDrop"
|
||||||
v-hotkey="keymap">
|
v-hotkey="keymap">
|
||||||
@@ -269,7 +269,7 @@ export default Vue.extend({
|
|||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
onDragover(e) {
|
onDragenter(e) {
|
||||||
// テンポラリカラムにはドロップさせない
|
// テンポラリカラムにはドロップさせない
|
||||||
if (this.isTemporaryColumn) {
|
if (this.isTemporaryColumn) {
|
||||||
e.dataTransfer.dropEffect = 'none';
|
e.dataTransfer.dropEffect = 'none';
|
||||||
@@ -287,7 +287,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
|
e.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
|
||||||
|
|
||||||
if (!this.dragging) this.draghover = true;
|
if (!this.dragging && isDeckColumn) this.draghover = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
onDragleave() {
|
onDragleave() {
|
||||||
|
|||||||
@@ -8,10 +8,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!fetching && requestInitPromise != null" class="error">
|
<mk-error v-if="!fetching && requestInitPromise != null" @retry="resolveInitPromise"/>
|
||||||
<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
|
|
||||||
<ui-button @click="resolveInitPromise">%i18n:common.error.retry%</ui-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
<!-- トランジションを有効にするとなぜかメモリリークする -->
|
||||||
<!--<transition-group name="mk-notes" class="transition" ref="notes">-->
|
<!--<transition-group name="mk-notes" class="transition" ref="notes">-->
|
||||||
@@ -221,13 +218,6 @@ export default Vue.extend({
|
|||||||
> *
|
> *
|
||||||
transition transform .3s ease, opacity .3s ease
|
transition transform .3s ease, opacity .3s ease
|
||||||
|
|
||||||
> .error
|
|
||||||
max-width 300px
|
|
||||||
margin 0 auto
|
|
||||||
padding 16px
|
|
||||||
text-align center
|
|
||||||
color var(--text)
|
|
||||||
|
|
||||||
> .placeholder
|
> .placeholder
|
||||||
padding 16px
|
padding 16px
|
||||||
opacity 0.3
|
opacity 0.3
|
||||||
|
|||||||
@@ -11,7 +11,11 @@
|
|||||||
<div class="action-form">
|
<div class="action-form">
|
||||||
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
|
<ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
|
||||||
<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
|
<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>
|
||||||
<ui-button @click="list">%fa:list% %i18n:@push-to-a-list%</ui-button>
|
<ui-button @click="list">%fa:list% %i18n:@push-to-a-list%</ui-button>
|
||||||
</div>
|
</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() {
|
list() {
|
||||||
const w = (this as any).os.new(MkUserListsWindow);
|
const w = (this as any).os.new(MkUserListsWindow);
|
||||||
w.$once('choosen', async list => {
|
w.$once('choosen', async list => {
|
||||||
@@ -114,7 +139,6 @@ export default Vue.extend({
|
|||||||
> .action-form
|
> .action-form
|
||||||
padding 16px
|
padding 16px
|
||||||
text-align center
|
text-align center
|
||||||
border-bottom solid 1px var(--faceDivider)
|
|
||||||
|
|
||||||
> *
|
> *
|
||||||
width 100%
|
width 100%
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
user: {
|
user: {
|
||||||
@@ -24,6 +25,7 @@ export default Vue.extend({
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
u: this.user,
|
u: this.user,
|
||||||
@@ -31,28 +33,24 @@ export default Vue.extend({
|
|||||||
connection: null
|
connection: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = (this as any).os.stream.useSharedConnection('main');
|
this.connection = (this as any).os.stream.useSharedConnection('main');
|
||||||
|
|
||||||
this.connection.on('follow', this.onFollow);
|
this.connection.on('follow', this.onFollowChange);
|
||||||
this.connection.on('unfollow', this.onUnfollow);
|
this.connection.on('unfollow', this.onFollowChange);
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.dispose();
|
this.connection.dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
onFollowChange(user) {
|
||||||
onFollow(user) {
|
|
||||||
if (user.id == this.u.id) {
|
|
||||||
this.u.isFollowing = user.isFollowing;
|
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onUnfollow(user) {
|
|
||||||
if (user.id == this.u.id) {
|
if (user.id == this.u.id) {
|
||||||
this.u.isFollowing = user.isFollowing;
|
this.u.isFollowing = user.isFollowing;
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||||
|
this.$forceUpdate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -90,8 +88,6 @@ export default Vue.extend({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
|
||||||
|
|
||||||
.mk-follow-button
|
.mk-follow-button
|
||||||
display block
|
display block
|
||||||
user-select none
|
user-select none
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import noteCard from './note-card.vue';
|
|||||||
import userCard from './user-card.vue';
|
import userCard from './user-card.vue';
|
||||||
import noteDetail from './note-detail.vue';
|
import noteDetail from './note-detail.vue';
|
||||||
import followButton from './follow-button.vue';
|
import followButton from './follow-button.vue';
|
||||||
import muteButton from './mute-button.vue';
|
|
||||||
import friendsMaker from './friends-maker.vue';
|
import friendsMaker from './friends-maker.vue';
|
||||||
import notification from './notification.vue';
|
import notification from './notification.vue';
|
||||||
import notifications from './notifications.vue';
|
import notifications from './notifications.vue';
|
||||||
@@ -37,7 +36,6 @@ Vue.component('mk-note-card', noteCard);
|
|||||||
Vue.component('mk-user-card', userCard);
|
Vue.component('mk-user-card', userCard);
|
||||||
Vue.component('mk-note-detail', noteDetail);
|
Vue.component('mk-note-detail', noteDetail);
|
||||||
Vue.component('mk-follow-button', followButton);
|
Vue.component('mk-follow-button', followButton);
|
||||||
Vue.component('mk-mute-button', muteButton);
|
|
||||||
Vue.component('mk-friends-maker', friendsMaker);
|
Vue.component('mk-friends-maker', friendsMaker);
|
||||||
Vue.component('mk-notification', notification);
|
Vue.component('mk-notification', notification);
|
||||||
Vue.component('mk-notifications', notifications);
|
Vue.component('mk-notifications', notifications);
|
||||||
|
|||||||
@@ -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>
|
|
||||||
@@ -85,6 +85,8 @@
|
|||||||
|
|
||||||
<mk-drive-settings/>
|
<mk-drive-settings/>
|
||||||
|
|
||||||
|
<mk-mute-and-block/>
|
||||||
|
|
||||||
<ui-card>
|
<ui-card>
|
||||||
<div slot="title">%fa:volume-up% %i18n:@sound%</div>
|
<div slot="title">%fa:volume-up% %i18n:@sound%</div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<a class="avatar">
|
<a class="avatar">
|
||||||
<img :src="user.avatarUrl" alt="avatar"/>
|
<img :src="user.avatarUrl" alt="avatar"/>
|
||||||
</a>
|
</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"/>
|
<mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -67,6 +67,7 @@ import Vue from 'vue';
|
|||||||
import * as age from 's-age';
|
import * as age from 's-age';
|
||||||
import parseAcct from '../../../../../misc/acct/parse';
|
import parseAcct from '../../../../../misc/acct/parse';
|
||||||
import Progress from '../../../common/scripts/loading';
|
import Progress from '../../../common/scripts/loading';
|
||||||
|
import Menu from '../../../common/views/components/menu.vue';
|
||||||
import XHome from './user/home.vue';
|
import XHome from './user/home.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
@@ -109,7 +110,61 @@ export default Vue.extend({
|
|||||||
Progress.done();
|
Progress.done();
|
||||||
document.title = `${Vue.filter('userName')(this.user)} | ${(this as any).os.instanceName}`;
|
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>
|
</script>
|
||||||
@@ -156,14 +211,10 @@ main
|
|||||||
max-width 600px
|
max-width 600px
|
||||||
|
|
||||||
> .top
|
> .top
|
||||||
&:after
|
display flex
|
||||||
content ''
|
|
||||||
display block
|
|
||||||
clear both
|
|
||||||
|
|
||||||
> .avatar
|
> .avatar
|
||||||
display block
|
display block
|
||||||
float left
|
|
||||||
width 25%
|
width 25%
|
||||||
height 40px
|
height 40px
|
||||||
|
|
||||||
@@ -183,11 +234,15 @@ main
|
|||||||
border 4px solid $bg
|
border 4px solid $bg
|
||||||
border-radius 12px
|
border-radius 12px
|
||||||
|
|
||||||
> .mk-mute-button
|
> .menu
|
||||||
float right
|
margin 0 0 0 auto
|
||||||
|
padding 8px
|
||||||
|
margin-right 8px
|
||||||
|
font-size 18px
|
||||||
|
color var(--text)
|
||||||
|
|
||||||
> .mk-follow-button
|
> .mk-follow-button
|
||||||
float right
|
margin 0
|
||||||
|
|
||||||
> .title
|
> .title
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|||||||
@@ -131,6 +131,8 @@
|
|||||||
remoteInfoBg: '#42321c',
|
remoteInfoBg: '#42321c',
|
||||||
remoteInfoFg: '#ffbd3e',
|
remoteInfoFg: '#ffbd3e',
|
||||||
|
|
||||||
|
infoBg: '#253142',
|
||||||
|
infoFg: '#fff',
|
||||||
infoWarnBg: '#42321c',
|
infoWarnBg: '#42321c',
|
||||||
infoWarnFg: '#ffbd3e',
|
infoWarnFg: '#ffbd3e',
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,8 @@
|
|||||||
remoteInfoBg: '#fff0db',
|
remoteInfoBg: '#fff0db',
|
||||||
remoteInfoFg: '#573c08',
|
remoteInfoFg: '#573c08',
|
||||||
|
|
||||||
|
infoBg: '#e5f5ff',
|
||||||
|
infoFg: '#72818a',
|
||||||
infoWarnBg: '#fff0db',
|
infoWarnBg: '#fff0db',
|
||||||
infoWarnFg: '#573c08',
|
infoWarnFg: '#573c08',
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ export type Source = {
|
|||||||
* メンテナの連絡先(URLかmailto形式のURL)
|
* メンテナの連絡先(URLかmailto形式のURL)
|
||||||
*/
|
*/
|
||||||
url: string;
|
url: string;
|
||||||
|
email?: string;
|
||||||
repository_url?: string;
|
repository_url?: string;
|
||||||
feedback_url?: string;
|
feedback_url?: string;
|
||||||
};
|
};
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
languages?: string[];
|
||||||
welcome_bg_url?: string;
|
welcome_bg_url?: string;
|
||||||
url: string;
|
url: string;
|
||||||
port: number;
|
port: number;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
|
以下のURLに、`i`というパラメータ名で認証情報を含めて、websocket接続してください。例:
|
||||||
```
|
```
|
||||||
%URL%/streaming?i=xxxxxxxxxxxxxxx
|
%WS_URL%/streaming?i=xxxxxxxxxxxxxxx
|
||||||
```
|
```
|
||||||
|
|
||||||
認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
|
認証情報は、自分のAPIキーや、アプリケーションからストリームに接続する際はユーザーのアクセストークンのことを指します。
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
|
認証情報は省略することもできますが、その場合非ログインでの利用ということになり、受信できる情報や可能な操作は限られます。例:
|
||||||
|
|
||||||
```
|
```
|
||||||
%URL%/streaming
|
%WS_URL%/streaming
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ export type TextElementHashtag = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function(text: string, i: number) {
|
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 isHead = text.startsWith('#');
|
||||||
const hashtag = text.match(/^\s?#[^\s\.,!\?]+/)[0];
|
const hashtag = text.match(/^\s?#[^\s\.,!\?#]+/)[0];
|
||||||
const res: any[] = !isHead ? [{
|
const res: any[] = !isHead ? [{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
content: text[0]
|
content: text[0]
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export default function(text: string, index: number) {
|
|||||||
const quote = match[1]
|
const quote = match[1]
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map(line => line.replace(/^>+/g, '').trim())
|
.map(line => line.replace(/^>+/g, '').trim())
|
||||||
.join('\n');
|
.join('\n')
|
||||||
|
.trim();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'quote',
|
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 * as mongo from 'mongodb';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
|
||||||
|
|
||||||
const AccessToken = db.get<IAccessToken>('accessTokens');
|
const AccessToken = db.get<IAccessToken>('accessTokens');
|
||||||
AccessToken.createIndex('token');
|
AccessToken.createIndex('token');
|
||||||
@@ -15,30 +14,3 @@ export type IAccessToken = {
|
|||||||
token: string;
|
token: string;
|
||||||
hash: 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
|
* Pack an app for API response
|
||||||
*
|
|
||||||
* @param {any} app
|
|
||||||
* @param {any} me?
|
|
||||||
* @param {any} options?
|
|
||||||
* @return {Promise<any>}
|
|
||||||
*/
|
*/
|
||||||
export const pack = (
|
export const pack = (
|
||||||
app: any,
|
app: any,
|
||||||
me?: any,
|
me?: any,
|
||||||
options?: {
|
options?: {
|
||||||
|
detail?: boolean,
|
||||||
includeSecret?: boolean,
|
includeSecret?: boolean,
|
||||||
includeProfileImageIds?: boolean
|
includeProfileImageIds?: boolean
|
||||||
}
|
}
|
||||||
) => new Promise<any>(async (resolve, reject) => {
|
) => new Promise<any>(async (resolve, reject) => {
|
||||||
const opts = options || {
|
const opts = Object.assign({
|
||||||
|
detail: false,
|
||||||
includeSecret: false,
|
includeSecret: false,
|
||||||
includeProfileImageIds: false
|
includeProfileImageIds: false
|
||||||
};
|
}, options);
|
||||||
|
|
||||||
let _app: any;
|
let _app: any;
|
||||||
|
|
||||||
|
const fields = opts.detail ? {} : {
|
||||||
|
name: true
|
||||||
|
};
|
||||||
|
|
||||||
// Populate the app if 'app' is ID
|
// Populate the app if 'app' is ID
|
||||||
if (isObjectId(app)) {
|
if (isObjectId(app)) {
|
||||||
_app = await App.findOne({
|
_app = await App.findOne({
|
||||||
@@ -51,7 +52,7 @@ export const pack = (
|
|||||||
} else if (typeof app === 'string') {
|
} else if (typeof app === 'string') {
|
||||||
_app = await App.findOne({
|
_app = await App.findOne({
|
||||||
_id: new mongo.ObjectID(app)
|
_id: new mongo.ObjectID(app)
|
||||||
});
|
}, { fields });
|
||||||
} else {
|
} else {
|
||||||
_app = deepcopy(app);
|
_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 * as mongo from 'mongodb';
|
||||||
import monkDb, { nativeDbConn } from '../db/mongodb';
|
import monkDb, { nativeDbConn } from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
|
||||||
|
|
||||||
const DriveFileThumbnail = monkDb.get<IDriveFileThumbnail>('driveFileThumbnails.files');
|
const DriveFileThumbnail = monkDb.get<IDriveFileThumbnail>('driveFileThumbnails.files');
|
||||||
DriveFileThumbnail.createIndex('metadata.originalId', { sparse: true, unique: true });
|
DriveFileThumbnail.createIndex('metadata.originalId', { sparse: true, unique: true });
|
||||||
@@ -28,35 +27,3 @@ export type IDriveFileThumbnail = {
|
|||||||
contentType: string;
|
contentType: string;
|
||||||
metadata: IMetadata;
|
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';
|
import * as mongo from 'mongodb';
|
||||||
const deepcopy = require('deepcopy');
|
const deepcopy = require('deepcopy');
|
||||||
import { pack as packFolder } from './drive-folder';
|
import { pack as packFolder } from './drive-folder';
|
||||||
import config from '../config';
|
|
||||||
import monkDb, { nativeDbConn } from '../db/mongodb';
|
import monkDb, { nativeDbConn } from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
import isObjectId from '../misc/is-objectid';
|
||||||
import Note, { deleteNote } from './note';
|
import getDriveFileUrl from '../misc/get-drive-file-url';
|
||||||
import MessagingMessage, { deleteMessagingMessage } from './messaging-message';
|
|
||||||
import User from './user';
|
|
||||||
import DriveFileThumbnail, { deleteDriveFileThumbnail } from './drive-file-thumbnail';
|
|
||||||
|
|
||||||
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
|
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
|
||||||
DriveFile.createIndex('md5');
|
DriveFile.createIndex('md5');
|
||||||
@@ -37,7 +33,14 @@ export type IMetadata = {
|
|||||||
thumbnailUrl?: string;
|
thumbnailUrl?: string;
|
||||||
src?: string;
|
src?: string;
|
||||||
deletedAt?: Date;
|
deletedAt?: Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このファイルの中身データがMongoDB内に保存されているのか否か
|
||||||
|
* オブジェクトストレージを利用している or リモートサーバーへの直リンクである
|
||||||
|
* な場合は false になります
|
||||||
|
*/
|
||||||
withoutChunks?: boolean;
|
withoutChunks?: boolean;
|
||||||
|
|
||||||
storage?: string;
|
storage?: string;
|
||||||
storageProps?: any;
|
storageProps?: any;
|
||||||
isSensitive?: boolean;
|
isSensitive?: boolean;
|
||||||
@@ -77,71 +80,13 @@ export function validateFileName(name: string): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export const packMany = (
|
||||||
* 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 (
|
|
||||||
files: any[],
|
files: any[],
|
||||||
options?: {
|
options?: {
|
||||||
detail: boolean
|
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 = Object.assign(_target, _file.metadata);
|
||||||
|
|
||||||
_target.url = _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
|
_target.url = getDriveFileUrl(_file);
|
||||||
_target.thumbnailUrl = _file.metadata.thumbnailUrl ? _file.metadata.thumbnailUrl : _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}?thumbnail`;
|
_target.thumbnailUrl = getDriveFileUrl(_file, true);
|
||||||
_target.isRemote = _file.metadata.isRemote;
|
_target.isRemote = _file.metadata.isRemote;
|
||||||
|
|
||||||
if (_target.properties == null) _target.properties = {};
|
if (_target.properties == null) _target.properties = {};
|
||||||
@@ -218,6 +163,7 @@ export const pack = (
|
|||||||
delete _target.storage;
|
delete _target.storage;
|
||||||
delete _target.storageProps;
|
delete _target.storageProps;
|
||||||
delete _target.isRemote;
|
delete _target.isRemote;
|
||||||
|
delete _target._user;
|
||||||
|
|
||||||
resolve(_target);
|
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
|
* Pack a drive folder for API response
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import isObjectId from '../misc/is-objectid';
|
|||||||
import { pack as packNote } from './note';
|
import { pack as packNote } from './note';
|
||||||
|
|
||||||
const Favorite = db.get<IFavorite>('favorites');
|
const Favorite = db.get<IFavorite>('favorites');
|
||||||
|
Favorite.createIndex('userId');
|
||||||
Favorite.createIndex(['userId', 'noteId'], { unique: true });
|
Favorite.createIndex(['userId', 'noteId'], { unique: true });
|
||||||
export default Favorite;
|
export default Favorite;
|
||||||
|
|
||||||
@@ -15,38 +16,11 @@ export type IFavorite = {
|
|||||||
noteId: mongo.ObjectID;
|
noteId: mongo.ObjectID;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export const packMany = (
|
||||||
* 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 (
|
|
||||||
favorites: any[],
|
favorites: any[],
|
||||||
me: 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';
|
import { pack as packUser } from './user';
|
||||||
|
|
||||||
const FollowRequest = db.get<IFollowRequest>('followRequests');
|
const FollowRequest = db.get<IFollowRequest>('followRequests');
|
||||||
|
FollowRequest.createIndex('followerId');
|
||||||
|
FollowRequest.createIndex('followeeId');
|
||||||
FollowRequest.createIndex(['followerId', 'followeeId'], { unique: true });
|
FollowRequest.createIndex(['followerId', 'followeeId'], { unique: true });
|
||||||
export default FollowRequest;
|
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
|
* Pack a request for API response
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
|
||||||
|
|
||||||
const Following = db.get<IFollowing>('following');
|
const Following = db.get<IFollowing>('following');
|
||||||
|
Following.createIndex('followerId');
|
||||||
|
Following.createIndex('followeeId');
|
||||||
Following.createIndex(['followerId', 'followeeId'], { unique: true });
|
Following.createIndex(['followerId', 'followeeId'], { unique: true });
|
||||||
export default Following;
|
export default Following;
|
||||||
|
|
||||||
@@ -25,30 +26,3 @@ export type IFollowing = {
|
|||||||
sharedInbox?: string;
|
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 * as mongo from 'mongodb';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
|
||||||
|
|
||||||
const MessagingHistory = db.get<IMessagingHistory>('messagingHistories');
|
const MessagingHistory = db.get<IMessagingHistory>('messagingHistories');
|
||||||
export default MessagingHistory;
|
export default MessagingHistory;
|
||||||
@@ -12,30 +11,3 @@ export type IMessagingHistory = {
|
|||||||
partnerId: mongo.ObjectID;
|
partnerId: mongo.ObjectID;
|
||||||
messageId: 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 { pack as packFile } from './drive-file';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
import isObjectId from '../misc/is-objectid';
|
||||||
import MessagingHistory, { deleteMessagingHistory } from './messaging-history';
|
|
||||||
import { length } from 'stringz';
|
import { length } from 'stringz';
|
||||||
|
|
||||||
const MessagingMessage = db.get<IMessagingMessage>('messagingMessages');
|
const MessagingMessage = db.get<IMessagingMessage>('messagingMessages');
|
||||||
@@ -24,38 +23,6 @@ export function isValidText(text: string): boolean {
|
|||||||
return length(text.trim()) <= 1000 && text.trim() != '';
|
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
|
* Pack a messaging message for API response
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
import isObjectId from '../misc/is-objectid';
|
||||||
|
const deepcopy = require('deepcopy');
|
||||||
|
import { pack as packUser, IUser } from './user';
|
||||||
|
|
||||||
const Mute = db.get<IMute>('mute');
|
const Mute = db.get<IMute>('mute');
|
||||||
|
Mute.createIndex('muterId');
|
||||||
|
Mute.createIndex('muteeId');
|
||||||
Mute.createIndex(['muterId', 'muteeId'], { unique: true });
|
Mute.createIndex(['muterId', 'muteeId'], { unique: true });
|
||||||
export default Mute;
|
export default Mute;
|
||||||
|
|
||||||
@@ -13,29 +17,40 @@ export interface IMute {
|
|||||||
muteeId: mongo.ObjectID;
|
muteeId: mongo.ObjectID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export const packMany = (
|
||||||
* Muteを物理削除します
|
mutes: (string | mongo.ObjectID | IMute)[],
|
||||||
*/
|
me?: string | mongo.ObjectID | IUser
|
||||||
export async function deleteMute(mute: string | mongo.ObjectID | IMute) {
|
) => {
|
||||||
let m: IMute;
|
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)) {
|
if (isObjectId(mute)) {
|
||||||
m = await Mute.findOne({
|
_mute = await Mute.findOne({
|
||||||
_id: mute
|
_id: mute
|
||||||
});
|
});
|
||||||
} else if (typeof mute === 'string') {
|
} else if (typeof mute === 'string') {
|
||||||
m = await Mute.findOne({
|
_mute = await Mute.findOne({
|
||||||
_id: new mongo.ObjectID(mute)
|
_id: new mongo.ObjectID(mute)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
m = mute as IMute;
|
_mute = deepcopy(mute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m == null) return;
|
// Rename _id to id
|
||||||
|
_mute.id = _mute._id;
|
||||||
|
delete _mute._id;
|
||||||
|
|
||||||
// このMuteを削除
|
// Populate mutee
|
||||||
await Mute.remove({
|
_mute.mutee = await packUser(_mute.muteeId, me, {
|
||||||
_id: m._id
|
detail: true
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
resolve(_mute);
|
||||||
|
});
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import Reaction from './note-reaction';
|
|||||||
import { pack as packUser } from './user';
|
import { pack as packUser } from './user';
|
||||||
|
|
||||||
const NoteReaction = db.get<INoteReaction>('noteReactions');
|
const NoteReaction = db.get<INoteReaction>('noteReactions');
|
||||||
|
NoteReaction.createIndex('noteId');
|
||||||
|
NoteReaction.createIndex('userId');
|
||||||
NoteReaction.createIndex(['userId', 'noteId'], { unique: true });
|
NoteReaction.createIndex(['userId', 'noteId'], { unique: true });
|
||||||
export default NoteReaction;
|
export default NoteReaction;
|
||||||
|
|
||||||
@@ -31,33 +33,6 @@ export const validateReaction = $.str.or([
|
|||||||
'pudding'
|
'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
|
* Pack a reaction for API response
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import * as mongo from 'mongodb';
|
|||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
|
|
||||||
const NoteUnread = db.get<INoteUnread>('noteUnreads');
|
const NoteUnread = db.get<INoteUnread>('noteUnreads');
|
||||||
|
NoteUnread.createIndex('userId');
|
||||||
|
NoteUnread.createIndex('noteId');
|
||||||
NoteUnread.createIndex(['userId', 'noteId'], { unique: true });
|
NoteUnread.createIndex(['userId', 'noteId'], { unique: true });
|
||||||
export default NoteUnread;
|
export default NoteUnread;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
|
||||||
|
|
||||||
const NoteWatching = db.get<INoteWatching>('noteWatching');
|
const NoteWatching = db.get<INoteWatching>('noteWatching');
|
||||||
|
NoteWatching.createIndex('userId');
|
||||||
|
NoteWatching.createIndex('noteId');
|
||||||
NoteWatching.createIndex(['userId', 'noteId'], { unique: true });
|
NoteWatching.createIndex(['userId', 'noteId'], { unique: true });
|
||||||
export default NoteWatching;
|
export default NoteWatching;
|
||||||
|
|
||||||
@@ -12,30 +13,3 @@ export interface INoteWatching {
|
|||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
noteId: 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 { length } from 'stringz';
|
||||||
import { IUser, pack as packUser } from './user';
|
import { IUser, pack as packUser } from './user';
|
||||||
import { pack as packApp } from './app';
|
import { pack as packApp } from './app';
|
||||||
import PollVote, { deletePollVote } from './poll-vote';
|
import PollVote from './poll-vote';
|
||||||
import Reaction, { deleteNoteReaction } from './note-reaction';
|
import Reaction from './note-reaction';
|
||||||
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
||||||
import NoteWatching, { deleteNoteWatching } from './note-watching';
|
import Favorite from './favorite';
|
||||||
import NoteReaction from './note-reaction';
|
|
||||||
import Favorite, { deleteFavorite } from './favorite';
|
|
||||||
import Notification, { deleteNotification } from './notification';
|
|
||||||
import Following from './following';
|
import Following from './following';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
|
|
||||||
@@ -108,72 +105,6 @@ export type INote = {
|
|||||||
_files?: IDriveFile[];
|
_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) => {
|
export const hideNote = async (packedNote: any, meId: mongo.ObjectID) => {
|
||||||
let hide = false;
|
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)[],
|
notes: (string | mongo.ObjectID | INote)[],
|
||||||
me?: string | mongo.ObjectID | IUser,
|
me?: string | mongo.ObjectID | IUser,
|
||||||
options?: {
|
options?: {
|
||||||
@@ -241,7 +172,7 @@ export const packMany = async (
|
|||||||
skipHide?: boolean;
|
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
|
// When requested a detailed note data
|
||||||
if (opts.detail) {
|
if (opts.detail) {
|
||||||
//#region 重いので廃止
|
|
||||||
_note.prev = null;
|
|
||||||
_note.next = null;
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
if (_note.replyId) {
|
if (_note.replyId) {
|
||||||
// Populate reply to note
|
// Populate reply to note
|
||||||
_note.reply = pack(_note.replyId, meId, {
|
_note.reply = pack(_note.replyId, meId, {
|
||||||
|
|||||||
@@ -51,37 +51,10 @@ export interface INotification {
|
|||||||
isRead: Boolean;
|
isRead: Boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export const packMany = (
|
||||||
* 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 (
|
|
||||||
notifications: any[]
|
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 * as mongo from 'mongodb';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
|
||||||
|
|
||||||
const PollVote = db.get<IPollVote>('pollVotes');
|
const PollVote = db.get<IPollVote>('pollVotes');
|
||||||
export default PollVote;
|
export default PollVote;
|
||||||
@@ -12,30 +11,3 @@ export interface IPollVote {
|
|||||||
noteId: mongo.ObjectID;
|
noteId: mongo.ObjectID;
|
||||||
choice: number;
|
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 * as mongo from 'mongodb';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
|
||||||
|
|
||||||
const SwSubscription = db.get<ISwSubscription>('swSubscriptions');
|
const SwSubscription = db.get<ISwSubscription>('swSubscriptions');
|
||||||
export default SwSubscription;
|
export default SwSubscription;
|
||||||
@@ -12,30 +11,3 @@ export interface ISwSubscription {
|
|||||||
auth: string;
|
auth: string;
|
||||||
publickey: 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[];
|
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 = (
|
export const pack = (
|
||||||
userList: string | mongo.ObjectID | IUserList
|
userList: string | mongo.ObjectID | IUserList
|
||||||
) => new Promise<any>(async (resolve, reject) => {
|
) => new Promise<any>(async (resolve, reject) => {
|
||||||
|
|||||||
@@ -1,27 +1,15 @@
|
|||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
const deepcopy = require('deepcopy');
|
const deepcopy = require('deepcopy');
|
||||||
const sequential = require('promise-sequential');
|
|
||||||
import rap from '@prezzemolo/rap';
|
import rap from '@prezzemolo/rap';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import isObjectId from '../misc/is-objectid';
|
import isObjectId from '../misc/is-objectid';
|
||||||
import Note, { packMany as packNoteMany, deleteNote } from './note';
|
import { packMany as packNoteMany } from './note';
|
||||||
import Following, { deleteFollowing } from './following';
|
import Following from './following';
|
||||||
import Mute, { deleteMute } from './mute';
|
import Blocking from './blocking';
|
||||||
|
import Mute from './mute';
|
||||||
import { getFriendIds } from '../server/api/common/get-friends';
|
import { getFriendIds } from '../server/api/common/get-friends';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import AccessToken, { deleteAccessToken } from './access-token';
|
import FollowRequest from './follow-request';
|
||||||
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';
|
|
||||||
|
|
||||||
const User = db.get<IUser>('users');
|
const User = db.get<IUser>('users');
|
||||||
|
|
||||||
@@ -167,149 +155,48 @@ export function isValidBirthday(birthday: string): boolean {
|
|||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
/**
|
export async function getRelation(me: mongo.ObjectId, target: mongo.ObjectId) {
|
||||||
* Userを物理削除します
|
const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
|
||||||
*/
|
Following.findOne({
|
||||||
export async function deleteUser(user: string | mongo.ObjectID | IUser) {
|
followerId: me,
|
||||||
let u: IUser;
|
followeeId: target
|
||||||
|
}),
|
||||||
// Populate
|
Following.findOne({
|
||||||
if (isObjectId(user)) {
|
followerId: target,
|
||||||
u = await User.findOne({
|
followeeId: me
|
||||||
_id: user
|
}),
|
||||||
});
|
FollowRequest.findOne({
|
||||||
} else if (typeof user === 'string') {
|
followerId: me,
|
||||||
u = await User.findOne({
|
followeeId: target
|
||||||
_id: new mongo.ObjectID(user)
|
}),
|
||||||
});
|
FollowRequest.findOne({
|
||||||
} else {
|
followerId: target,
|
||||||
u = user as IUser;
|
followeeId: me
|
||||||
}
|
}),
|
||||||
|
Blocking.findOne({
|
||||||
console.log(u == null ? `User: delete skipped ${user}` : `User: deleting ${u._id}`);
|
blockerId: me,
|
||||||
|
blockeeId: target
|
||||||
if (u == null) return;
|
}),
|
||||||
|
Blocking.findOne({
|
||||||
// このユーザーのAccessTokenをすべて削除
|
blockerId: target,
|
||||||
await Promise.all((
|
blockeeId: me
|
||||||
await AccessToken.find({ userId: u._id })
|
}),
|
||||||
).map(x => deleteAccessToken(x)));
|
Mute.findOne({
|
||||||
|
muterId: me,
|
||||||
// このユーザーのNoteをすべて削除
|
muteeId: target
|
||||||
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 }
|
|
||||||
})
|
})
|
||||||
));
|
]);
|
||||||
|
|
||||||
// このユーザーを削除
|
return {
|
||||||
await User.remove({
|
isFollowing: following1 !== null,
|
||||||
_id: u._id
|
isStalking: following1 && following1.stalk,
|
||||||
});
|
hasPendingFollowRequestFromYou: followReq1 !== null,
|
||||||
|
hasPendingFollowRequestToYou: followReq2 !== null,
|
||||||
console.log(`User: deleted ${u._id}`);
|
isFollowed: following2 !== null,
|
||||||
|
isBlocking: toBlocking !== null,
|
||||||
|
isBlocked: fromBlocked !== null,
|
||||||
|
isMuted: mute !== null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -336,13 +223,16 @@ export const pack = (
|
|||||||
|
|
||||||
let _user: any;
|
let _user: any;
|
||||||
|
|
||||||
const fields = opts.detail ? {
|
const fields = opts.detail ? {} : {
|
||||||
} : {
|
name: true,
|
||||||
settings: false,
|
username: true,
|
||||||
clientSettings: false,
|
host: true,
|
||||||
profile: false,
|
avatarColor: true,
|
||||||
keywords: false,
|
avatarUrl: true,
|
||||||
domains: false
|
isCat: true,
|
||||||
|
isBot: true,
|
||||||
|
isAdmin: true,
|
||||||
|
isVerified: true
|
||||||
};
|
};
|
||||||
|
|
||||||
// Populate the user if 'user' is ID
|
// Populate the user if 'user' is ID
|
||||||
@@ -377,6 +267,8 @@ export const pack = (
|
|||||||
_user.id = _user._id;
|
_user.id = _user._id;
|
||||||
delete _user._id;
|
delete _user._id;
|
||||||
|
|
||||||
|
delete _user.usernameLower;
|
||||||
|
|
||||||
if (_user.host == null) {
|
if (_user.host == null) {
|
||||||
// Remove private properties
|
// Remove private properties
|
||||||
delete _user.keypair;
|
delete _user.keypair;
|
||||||
@@ -384,7 +276,6 @@ export const pack = (
|
|||||||
delete _user.token;
|
delete _user.token;
|
||||||
delete _user.twoFactorTempSecret;
|
delete _user.twoFactorTempSecret;
|
||||||
delete _user.twoFactorSecret;
|
delete _user.twoFactorSecret;
|
||||||
delete _user.usernameLower;
|
|
||||||
if (_user.twitter) {
|
if (_user.twitter) {
|
||||||
delete _user.twitter.accessToken;
|
delete _user.twitter.accessToken;
|
||||||
delete _user.twitter.accessTokenSecret;
|
delete _user.twitter.accessTokenSecret;
|
||||||
@@ -407,16 +298,6 @@ export const pack = (
|
|||||||
|
|
||||||
if (_user.avatarUrl == null) {
|
if (_user.avatarUrl == null) {
|
||||||
_user.avatarUrl = `${config.drive_url}/default-avatar.jpg`;
|
_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) {
|
if (!meId || !meId.equals(_user.id) || !opts.detail) {
|
||||||
@@ -426,42 +307,17 @@ export const pack = (
|
|||||||
delete _user.hasUnreadNotification;
|
delete _user.hasUnreadNotification;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meId && !meId.equals(_user.id)) {
|
if (meId && !meId.equals(_user.id) && opts.detail) {
|
||||||
const [following1, following2, followReq1, followReq2, mute] = await Promise.all([
|
const relation = await getRelation(meId, _user.id);
|
||||||
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
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Whether the user is following
|
_user.isFollowing = relation.isFollowing;
|
||||||
_user.isFollowing = following1 !== null;
|
_user.isFollowed = relation.isFollowed;
|
||||||
_user.isStalking = following1 && following1.stalk;
|
_user.isStalking = relation.isStalking;
|
||||||
|
_user.hasPendingFollowRequestFromYou = relation.hasPendingFollowRequestFromYou;
|
||||||
_user.hasPendingFollowRequestFromYou = followReq1 !== null;
|
_user.hasPendingFollowRequestToYou = relation.hasPendingFollowRequestToYou;
|
||||||
_user.hasPendingFollowRequestToYou = followReq2 !== null;
|
_user.isBlocking = relation.isBlocking;
|
||||||
|
_user.isBlocked = relation.isBlocked;
|
||||||
// Whether the user is followed
|
_user.isMuted = relation.isMuted;
|
||||||
_user.isFollowed = following2 !== null;
|
|
||||||
|
|
||||||
// Whether the user is muted
|
|
||||||
_user.isMuted = mute !== null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.detail) {
|
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 reject from './reject';
|
||||||
import add from './add';
|
import add from './add';
|
||||||
import remove from './remove';
|
import remove from './remove';
|
||||||
|
import block from './block';
|
||||||
|
|
||||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
||||||
switch (activity.type) {
|
switch (activity.type) {
|
||||||
@@ -53,6 +54,10 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
|||||||
await undo(actor, activity);
|
await undo(actor, activity);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'Block':
|
||||||
|
await block(actor, activity);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'Collection':
|
case 'Collection':
|
||||||
case 'OrderedCollection':
|
case 'OrderedCollection':
|
||||||
// TODO
|
// 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 * as debug from 'debug';
|
||||||
|
|
||||||
import { IRemoteUser } from '../../../../models/user';
|
import { IRemoteUser } from '../../../../models/user';
|
||||||
import { IUndo, IFollow } from '../../type';
|
import { IUndo, IFollow, IBlock } from '../../type';
|
||||||
import unfollow from './follow';
|
import unfollow from './follow';
|
||||||
|
import unblock from './block';
|
||||||
import Resolver from '../../resolver';
|
import Resolver from '../../resolver';
|
||||||
|
|
||||||
const log = debug('misskey:activitypub');
|
const log = debug('misskey:activitypub');
|
||||||
@@ -31,6 +32,9 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
|
|||||||
case 'Follow':
|
case 'Follow':
|
||||||
unfollow(actor, object as IFollow);
|
unfollow(actor, object as IFollow);
|
||||||
break;
|
break;
|
||||||
|
case 'Block':
|
||||||
|
unblock(actor, object as IBlock);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { URL } from 'url';
|
|||||||
import { resolveNote } from './note';
|
import { resolveNote } from './note';
|
||||||
import registerInstance from '../../../services/register-instance';
|
import registerInstance from '../../../services/register-instance';
|
||||||
import Instance from '../../../models/instance';
|
import Instance from '../../../models/instance';
|
||||||
|
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
||||||
|
|
||||||
const log = debug('misskey:activitypub');
|
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 avatarId = avatar ? avatar._id : null;
|
||||||
const bannerId = banner ? banner._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 avatarUrl = getDriveFileUrl(avatar, true);
|
||||||
const bannerUrl = (banner && banner.metadata.url) ? banner.metadata.url : null;
|
const bannerUrl = getDriveFileUrl(banner, false);
|
||||||
|
|
||||||
await User.update({ _id: user._id }, {
|
await User.update({ _id: user._id }, {
|
||||||
$set: {
|
$set: {
|
||||||
@@ -303,8 +304,8 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
|
|||||||
featured: person.featured,
|
featured: person.featured,
|
||||||
avatarId: avatar ? avatar._id : null,
|
avatarId: avatar ? avatar._id : null,
|
||||||
bannerId: banner ? banner._id : null,
|
bannerId: banner ? banner._id : null,
|
||||||
avatarUrl: (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null,
|
avatarUrl: getDriveFileUrl(avatar, true),
|
||||||
bannerUrl: banner && banner.metadata.url ? banner.metadata.url : null,
|
bannerUrl: getDriveFileUrl(banner, false),
|
||||||
description: htmlToMFM(person.summary),
|
description: htmlToMFM(person.summary),
|
||||||
followersCount,
|
followersCount,
|
||||||
followingCount,
|
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 { IDriveFile } from '../../../models/drive-file';
|
||||||
|
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
||||||
|
|
||||||
export default (file: IDriveFile) => ({
|
export default (file: IDriveFile) => ({
|
||||||
type: 'Document',
|
type: 'Document',
|
||||||
mediaType: file.contentType,
|
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 { IDriveFile } from '../../../models/drive-file';
|
||||||
|
import getDriveFileUrl from '../../../misc/get-drive-file-url';
|
||||||
|
|
||||||
export default (file: IDriveFile) => ({
|
export default (file: IDriveFile) => ({
|
||||||
type: 'Image',
|
type: 'Image',
|
||||||
url: file.metadata.url || `${config.drive_url}/${file._id}`,
|
url: getDriveFileUrl(file),
|
||||||
sensitive: file.metadata.isSensitive
|
sensitive: file.metadata.isSensitive
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ export interface IAnnounce extends IActivity {
|
|||||||
type: 'Announce';
|
type: 'Announce';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IBlock extends IActivity {
|
||||||
|
type: 'Block';
|
||||||
|
}
|
||||||
|
|
||||||
export type Object =
|
export type Object =
|
||||||
ICollection |
|
ICollection |
|
||||||
IOrderedCollection |
|
IOrderedCollection |
|
||||||
@@ -120,4 +124,5 @@ export type Object =
|
|||||||
IAdd |
|
IAdd |
|
||||||
IRemove |
|
IRemove |
|
||||||
ILike |
|
ILike |
|
||||||
IAnnounce;
|
IAnnounce |
|
||||||
|
IBlock;
|
||||||
|
|||||||
@@ -1,36 +1,83 @@
|
|||||||
import { toUnicode, toASCII } from 'punycode';
|
import { toUnicode, toASCII } from 'punycode';
|
||||||
import User, { IUser } from '../models/user';
|
import User, { IUser, IRemoteUser } from '../models/user';
|
||||||
import webFinger from './webfinger';
|
import webFinger from './webfinger';
|
||||||
import config from '../config';
|
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();
|
const usernameLower = username.toLowerCase();
|
||||||
|
|
||||||
if (_host == null) {
|
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 hostAscii = toASCII(_host).toLowerCase();
|
||||||
const host = toUnicode(hostAscii);
|
const host = toUnicode(hostAscii);
|
||||||
|
|
||||||
if (config.host == host) {
|
if (config.host == host) {
|
||||||
|
log(`return local user: ${usernameLower}`);
|
||||||
return await User.findOne({ usernameLower, host: null });
|
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) {
|
if (user === null) {
|
||||||
const acctLower = `${usernameLower}@${hostAscii}`;
|
const self = await resolveSelf(acctLower);
|
||||||
|
|
||||||
const finger = await webFinger(acctLower);
|
log(`return new remote user: ${acctLower}`);
|
||||||
const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self');
|
return await createPerson(self.href);
|
||||||
if (!self) {
|
|
||||||
throw new Error('self link not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
user = 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;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ async function fetchAny(uri: string) {
|
|||||||
// URIがこのサーバーを指しているなら、ローカルユーザーIDとしてDBからフェッチ
|
// URIがこのサーバーを指しているなら、ローカルユーザーIDとしてDBからフェッチ
|
||||||
if (uri.startsWith(config.url + '/')) {
|
if (uri.startsWith(config.url + '/')) {
|
||||||
const id = new mongo.ObjectID(uri.split('/').pop());
|
const id = new mongo.ObjectID(uri.split('/').pop());
|
||||||
const [ user, note ] = await Promise.all([
|
const [user, note] = await Promise.all([
|
||||||
User.findOne({ _id: id }),
|
User.findOne({ _id: id }),
|
||||||
Note.findOne({ _id: id })
|
Note.findOne({ _id: id })
|
||||||
]);
|
]);
|
||||||
@@ -52,7 +52,7 @@ async function fetchAny(uri: string) {
|
|||||||
|
|
||||||
// URI(AP Object id)としてDB検索
|
// URI(AP Object id)としてDB検索
|
||||||
{
|
{
|
||||||
const [ user, note ] = await Promise.all([
|
const [user, note] = await Promise.all([
|
||||||
User.findOne({ uri: uri }),
|
User.findOne({ uri: uri }),
|
||||||
Note.findOne({ uri: uri })
|
Note.findOne({ uri: uri })
|
||||||
]);
|
]);
|
||||||
@@ -68,7 +68,7 @@ async function fetchAny(uri: string) {
|
|||||||
// /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する
|
// /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する
|
||||||
// これはDBに存在する可能性があるため再度DB検索
|
// これはDBに存在する可能性があるため再度DB検索
|
||||||
if (uri !== object.id) {
|
if (uri !== object.id) {
|
||||||
const [ user, note ] = await Promise.all([
|
const [user, note] = await Promise.all([
|
||||||
User.findOne({ uri: object.id }),
|
User.findOne({ uri: object.id }),
|
||||||
Note.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
|
// Response
|
||||||
res(await pack(app, null, {
|
res(await pack(app, null, {
|
||||||
|
detail: true,
|
||||||
includeSecret: true
|
includeSecret: true
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export default (params: any, user: ILocalUser, app: IApp) => new Promise(async (
|
|||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
res(await pack(ap, user, {
|
res(await pack(ap, user, {
|
||||||
|
detail: true,
|
||||||
includeSecret: isSecure && ap.userId.equals(user._id)
|
includeSecret: isSecure && ap.userId.equals(user._id)
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|||||||
77
src/server/api/endpoints/blocking/create.ts
Normal file
77
src/server/api/endpoints/blocking/create.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
|
||||||
|
const ms = require('ms');
|
||||||
|
import User, { pack, ILocalUser } from '../../../../models/user';
|
||||||
|
import Blocking from '../../../../models/blocking';
|
||||||
|
import create from '../../../../services/blocking/create';
|
||||||
|
import getParams from '../../get-params';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
stability: 'stable',
|
||||||
|
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '指定したユーザーをブロックします。',
|
||||||
|
'en-US': 'Block a user.'
|
||||||
|
},
|
||||||
|
|
||||||
|
limit: {
|
||||||
|
duration: ms('1hour'),
|
||||||
|
max: 100
|
||||||
|
},
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'following-write',
|
||||||
|
|
||||||
|
params: {
|
||||||
|
userId: $.type(ID).note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '対象のユーザーのID',
|
||||||
|
'en-US': 'Target user ID'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
|
||||||
|
const [ps, psErr] = getParams(meta, params);
|
||||||
|
if (psErr) return rej(psErr);
|
||||||
|
|
||||||
|
const blocker = user;
|
||||||
|
|
||||||
|
// 自分自身
|
||||||
|
if (user._id.equals(ps.userId)) {
|
||||||
|
return rej('blockee is yourself');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get blockee
|
||||||
|
const blockee = await User.findOne({
|
||||||
|
_id: ps.userId
|
||||||
|
}, {
|
||||||
|
fields: {
|
||||||
|
data: false,
|
||||||
|
profile: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blockee === null) {
|
||||||
|
return rej('user not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if already blocking
|
||||||
|
const exist = await Blocking.findOne({
|
||||||
|
blockerId: blocker._id,
|
||||||
|
blockeeId: blockee._id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exist !== null) {
|
||||||
|
return rej('already blocking');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create blocking
|
||||||
|
await create(blocker, blockee);
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res(await pack(blockee._id, user, {
|
||||||
|
detail: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
77
src/server/api/endpoints/blocking/delete.ts
Normal file
77
src/server/api/endpoints/blocking/delete.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
|
||||||
|
const ms = require('ms');
|
||||||
|
import User, { pack, ILocalUser } from '../../../../models/user';
|
||||||
|
import Blocking from '../../../../models/blocking';
|
||||||
|
import deleteBlocking from '../../../../services/blocking/delete';
|
||||||
|
import getParams from '../../get-params';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
stability: 'stable',
|
||||||
|
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '指定したユーザーのブロックを解除します。',
|
||||||
|
'en-US': 'Unblock a user.'
|
||||||
|
},
|
||||||
|
|
||||||
|
limit: {
|
||||||
|
duration: ms('1hour'),
|
||||||
|
max: 100
|
||||||
|
},
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'following-write',
|
||||||
|
|
||||||
|
params: {
|
||||||
|
userId: $.type(ID).note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '対象のユーザーのID',
|
||||||
|
'en-US': 'Target user ID'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
|
||||||
|
const [ps, psErr] = getParams(meta, params);
|
||||||
|
if (psErr) return rej(psErr);
|
||||||
|
|
||||||
|
const blocker = user;
|
||||||
|
|
||||||
|
// Check if the blockee is yourself
|
||||||
|
if (user._id.equals(ps.userId)) {
|
||||||
|
return rej('blockee is yourself');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get blockee
|
||||||
|
const blockee = await User.findOne({
|
||||||
|
_id: ps.userId
|
||||||
|
}, {
|
||||||
|
fields: {
|
||||||
|
data: false,
|
||||||
|
'profile': false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blockee === null) {
|
||||||
|
return rej('user not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check not blocking
|
||||||
|
const exist = await Blocking.findOne({
|
||||||
|
blockerId: blocker._id,
|
||||||
|
blockeeId: blockee._id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exist === null) {
|
||||||
|
return rej('already not blocking');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete blocking
|
||||||
|
await deleteBlocking(blocker, blockee);
|
||||||
|
|
||||||
|
// Send response
|
||||||
|
res(await pack(blockee._id, user, {
|
||||||
|
detail: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
64
src/server/api/endpoints/blocking/list.ts
Normal file
64
src/server/api/endpoints/blocking/list.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
|
||||||
|
import Blocking, { packMany } from '../../../../models/blocking';
|
||||||
|
import { ILocalUser } from '../../../../models/user';
|
||||||
|
import getParams from '../../get-params';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'ブロックしているユーザー一覧を取得します。',
|
||||||
|
'en-US': 'Get blocking users.'
|
||||||
|
},
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'following-read',
|
||||||
|
|
||||||
|
params: {
|
||||||
|
limit: $.num.optional.range(1, 100).note({
|
||||||
|
default: 30
|
||||||
|
}),
|
||||||
|
|
||||||
|
sinceId: $.type(ID).optional.note({
|
||||||
|
}),
|
||||||
|
|
||||||
|
untilId: $.type(ID).optional.note({
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
|
||||||
|
const [ps, psErr] = getParams(meta, params);
|
||||||
|
if (psErr) return rej(psErr);
|
||||||
|
|
||||||
|
// Check if both of sinceId and untilId is specified
|
||||||
|
if (ps.sinceId && ps.untilId) {
|
||||||
|
return rej('cannot set sinceId and untilId');
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
blockerId: me._id
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const sort = {
|
||||||
|
_id: -1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ps.sinceId) {
|
||||||
|
sort._id = 1;
|
||||||
|
query._id = {
|
||||||
|
$gt: ps.sinceId
|
||||||
|
};
|
||||||
|
} else if (ps.untilId) {
|
||||||
|
query._id = {
|
||||||
|
$lt: ps.untilId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockings = await Blocking
|
||||||
|
.find(query, {
|
||||||
|
limit: ps.limit,
|
||||||
|
sort: sort
|
||||||
|
});
|
||||||
|
|
||||||
|
res(await packMany(blockings, me));
|
||||||
|
});
|
||||||
@@ -16,7 +16,7 @@ export const meta = {
|
|||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
duration: ms('1hour'),
|
duration: ms('1hour'),
|
||||||
max: 100
|
max: 120
|
||||||
},
|
},
|
||||||
|
|
||||||
requireFile: true,
|
requireFile: true,
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create following
|
// Create following
|
||||||
await create(follower, followee);
|
try {
|
||||||
|
await create(follower, followee);
|
||||||
|
} catch (e) {
|
||||||
|
return rej(e && e.message ? e.message : e);
|
||||||
|
}
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
res(await pack(followee._id, user));
|
res(await pack(followee._id, user));
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Serialize
|
// Serialize
|
||||||
res(await Promise.all(tokens.map(async token =>
|
res(await Promise.all(tokens.map(token => pack(token.appId, user, {
|
||||||
await pack(token.appId))));
|
detail: true
|
||||||
|
}))));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { publishMainStream } from '../../../../stream';
|
|||||||
import DriveFile from '../../../../models/drive-file';
|
import DriveFile from '../../../../models/drive-file';
|
||||||
import acceptAllFollowRequests from '../../../../services/following/requests/accept-all';
|
import acceptAllFollowRequests from '../../../../services/following/requests/accept-all';
|
||||||
import { IApp } from '../../../../models/app';
|
import { IApp } from '../../../../models/app';
|
||||||
import config from '../../../../config';
|
|
||||||
import { publishToFollowers } from '../../../../services/i/update';
|
import { publishToFollowers } from '../../../../services/i/update';
|
||||||
import getParams from '../../get-params';
|
import getParams from '../../get-params';
|
||||||
|
import getDriveFileUrl from '../../../../misc/get-drive-file-url';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
@@ -129,7 +129,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a
|
|||||||
if (avatar == null) return rej('avatar not found');
|
if (avatar == null) return rej('avatar not found');
|
||||||
if (!avatar.contentType.startsWith('image/')) return rej('avatar not an image');
|
if (!avatar.contentType.startsWith('image/')) return rej('avatar not an image');
|
||||||
|
|
||||||
updates.avatarUrl = avatar.metadata.thumbnailUrl || avatar.metadata.url || `${config.drive_url}/${avatar._id}`;
|
updates.avatarUrl = getDriveFileUrl(avatar, true);
|
||||||
|
|
||||||
if (avatar.metadata.properties.avgColor) {
|
if (avatar.metadata.properties.avgColor) {
|
||||||
updates.avatarColor = avatar.metadata.properties.avgColor;
|
updates.avatarColor = avatar.metadata.properties.avgColor;
|
||||||
@@ -144,7 +144,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a
|
|||||||
if (banner == null) return rej('banner not found');
|
if (banner == null) return rej('banner not found');
|
||||||
if (!banner.contentType.startsWith('image/')) return rej('banner not an image');
|
if (!banner.contentType.startsWith('image/')) return rej('banner not an image');
|
||||||
|
|
||||||
updates.bannerUrl = banner.metadata.url || `${config.drive_url}/${banner._id}`;
|
updates.bannerUrl = getDriveFileUrl(banner, false);
|
||||||
|
|
||||||
if (banner.metadata.properties.avgColor) {
|
if (banner.metadata.properties.avgColor) {
|
||||||
updates.bannerColor = banner.metadata.properties.avgColor;
|
updates.bannerColor = banner.metadata.properties.avgColor;
|
||||||
@@ -162,7 +162,7 @@ export default async (params: any, user: ILocalUser, app: IApp) => new Promise(a
|
|||||||
|
|
||||||
if (wallpaper == null) return rej('wallpaper not found');
|
if (wallpaper == null) return rej('wallpaper not found');
|
||||||
|
|
||||||
updates.wallpaperUrl = wallpaper.metadata.url || `${config.drive_url}/${wallpaper._id}`;
|
updates.wallpaperUrl = getDriveFileUrl(wallpaper);
|
||||||
|
|
||||||
if (wallpaper.metadata.properties.avgColor) {
|
if (wallpaper.metadata.properties.avgColor) {
|
||||||
updates.wallpaperColor = wallpaper.metadata.properties.avgColor;
|
updates.wallpaperColor = wallpaper.metadata.properties.avgColor;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
|
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
|
||||||
import Mute from '../../../../models/mute';
|
import Mute, { packMany } from '../../../../models/mute';
|
||||||
import { pack, ILocalUser } from '../../../../models/user';
|
import { ILocalUser } from '../../../../models/user';
|
||||||
import { getFriendIds } from '../../common/get-friends';
|
import getParams from '../../get-params';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
@@ -11,64 +11,54 @@ export const meta = {
|
|||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'account/read'
|
kind: 'account/read',
|
||||||
|
|
||||||
|
params: {
|
||||||
|
limit: $.num.optional.range(1, 100).note({
|
||||||
|
default: 30
|
||||||
|
}),
|
||||||
|
|
||||||
|
sinceId: $.type(ID).optional.note({
|
||||||
|
}),
|
||||||
|
|
||||||
|
untilId: $.type(ID).optional.note({
|
||||||
|
}),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
|
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
|
||||||
// Get 'iknow' parameter
|
const [ps, psErr] = getParams(meta, params);
|
||||||
const [iknow = false, iknowErr] = $.bool.optional.get(params.iknow);
|
if (psErr) return rej(psErr);
|
||||||
if (iknowErr) return rej('invalid iknow param');
|
|
||||||
|
|
||||||
// Get 'limit' parameter
|
// Check if both of sinceId and untilId is specified
|
||||||
const [limit = 30, limitErr] = $.num.optional.range(1, 100).get(params.limit);
|
if (ps.sinceId && ps.untilId) {
|
||||||
if (limitErr) return rej('invalid limit param');
|
return rej('cannot set sinceId and untilId');
|
||||||
|
}
|
||||||
|
|
||||||
// Get 'cursor' parameter
|
|
||||||
const [cursor = null, cursorErr] = $.type(ID).optional.get(params.cursor);
|
|
||||||
if (cursorErr) return rej('invalid cursor param');
|
|
||||||
|
|
||||||
// Construct query
|
|
||||||
const query = {
|
const query = {
|
||||||
muterId: me._id,
|
muterId: me._id
|
||||||
deletedAt: { $exists: false }
|
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
if (iknow) {
|
const sort = {
|
||||||
// Get my friends
|
_id: -1
|
||||||
const myFriends = await getFriendIds(me._id);
|
};
|
||||||
|
|
||||||
query.muteeId = {
|
if (ps.sinceId) {
|
||||||
$in: myFriends
|
sort._id = 1;
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// カーソルが指定されている場合
|
|
||||||
if (cursor) {
|
|
||||||
query._id = {
|
query._id = {
|
||||||
$lt: cursor
|
$gt: ps.sinceId
|
||||||
|
};
|
||||||
|
} else if (ps.untilId) {
|
||||||
|
query._id = {
|
||||||
|
$lt: ps.untilId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mutes
|
|
||||||
const mutes = await Mute
|
const mutes = await Mute
|
||||||
.find(query, {
|
.find(query, {
|
||||||
limit: limit + 1,
|
limit: ps.limit,
|
||||||
sort: { _id: -1 }
|
sort: sort
|
||||||
});
|
});
|
||||||
|
|
||||||
// 「次のページ」があるかどうか
|
res(await packMany(mutes, me));
|
||||||
const inStock = mutes.length === limit + 1;
|
|
||||||
if (inStock) {
|
|
||||||
mutes.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize
|
|
||||||
const users = await Promise.all(mutes.map(async m =>
|
|
||||||
await pack(m.muteeId, me, { detail: true })));
|
|
||||||
|
|
||||||
// Response
|
|
||||||
res({
|
|
||||||
users: users,
|
|
||||||
next: inStock ? mutes[mutes.length - 1]._id : null,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Reply
|
// Reply
|
||||||
res(await Promise.all(apps.map(async app =>
|
res(await Promise.all(apps.map(app => pack(app, user, {
|
||||||
await pack(app))));
|
detail: true
|
||||||
|
}))));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
|
|||||||
return rej('cannot set sinceId and untilId');
|
return rej('cannot set sinceId and untilId');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct query
|
|
||||||
const query = {
|
const query = {
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
|
|
||||||
@@ -81,15 +80,13 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue query
|
|
||||||
const mentions = await Note
|
const mentions = await Note
|
||||||
.find(query, {
|
.find(query, {
|
||||||
limit: ps.limit,
|
limit: ps.limit,
|
||||||
sort: sort
|
sort: sort
|
||||||
});
|
});
|
||||||
|
|
||||||
mentions.forEach(note => read(user._id, note._id));
|
|
||||||
|
|
||||||
// Serialize
|
|
||||||
res(await packMany(mentions, user));
|
res(await packMany(mentions, user));
|
||||||
|
|
||||||
|
mentions.forEach(note => read(user._id, note._id));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
|
|||||||
import Note from '../../../../models/note';
|
import Note from '../../../../models/note';
|
||||||
import Reaction, { pack } from '../../../../models/note-reaction';
|
import Reaction, { pack } from '../../../../models/note-reaction';
|
||||||
import { ILocalUser } from '../../../../models/user';
|
import { ILocalUser } from '../../../../models/user';
|
||||||
|
import getParams from '../../get-params';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
@@ -9,46 +10,74 @@ export const meta = {
|
|||||||
'en-US': 'Show reactions of a note.'
|
'en-US': 'Show reactions of a note.'
|
||||||
},
|
},
|
||||||
|
|
||||||
requireCredential: true
|
requireCredential: false,
|
||||||
|
|
||||||
|
params: {
|
||||||
|
noteId: $.type(ID).note({
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '対象の投稿のID',
|
||||||
|
'en-US': 'The ID of the target note'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
limit: $.num.optional.range(1, 100).note({
|
||||||
|
default: 10
|
||||||
|
}),
|
||||||
|
|
||||||
|
offset: $.num.optional.note({
|
||||||
|
default: 0
|
||||||
|
}),
|
||||||
|
|
||||||
|
sinceId: $.type(ID).optional.note({
|
||||||
|
}),
|
||||||
|
|
||||||
|
untilId: $.type(ID).optional.note({
|
||||||
|
}),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
|
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
|
||||||
// Get 'noteId' parameter
|
const [ps, psErr] = getParams(meta, params);
|
||||||
const [noteId, noteIdErr] = $.type(ID).get(params.noteId);
|
if (psErr) return rej(psErr);
|
||||||
if (noteIdErr) return rej('invalid noteId param');
|
|
||||||
|
|
||||||
// Get 'limit' parameter
|
// Check if both of sinceId and untilId is specified
|
||||||
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
|
if (ps.sinceId && ps.untilId) {
|
||||||
if (limitErr) return rej('invalid limit param');
|
return rej('cannot set sinceId and untilId');
|
||||||
|
}
|
||||||
// Get 'offset' parameter
|
|
||||||
const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
|
|
||||||
if (offsetErr) return rej('invalid offset param');
|
|
||||||
|
|
||||||
// Get 'sort' parameter
|
|
||||||
const [sort = 'desc', sortError] = $.str.optional.or('desc asc').get(params.sort);
|
|
||||||
if (sortError) return rej('invalid sort param');
|
|
||||||
|
|
||||||
// Lookup note
|
// Lookup note
|
||||||
const note = await Note.findOne({
|
const note = await Note.findOne({
|
||||||
_id: noteId
|
_id: ps.noteId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (note === null) {
|
if (note === null) {
|
||||||
return rej('note not found');
|
return rej('note not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue query
|
const query = {
|
||||||
|
noteId: note._id
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const sort = {
|
||||||
|
_id: -1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ps.sinceId) {
|
||||||
|
sort._id = 1;
|
||||||
|
query._id = {
|
||||||
|
$gt: ps.sinceId
|
||||||
|
};
|
||||||
|
} else if (ps.untilId) {
|
||||||
|
query._id = {
|
||||||
|
$lt: ps.untilId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const reactions = await Reaction
|
const reactions = await Reaction
|
||||||
.find({
|
.find(query, {
|
||||||
noteId: note._id,
|
limit: ps.limit,
|
||||||
deletedAt: { $exists: false }
|
skip: ps.offset,
|
||||||
}, {
|
sort: sort
|
||||||
limit: limit,
|
|
||||||
skip: offset,
|
|
||||||
sort: {
|
|
||||||
_id: sort == 'asc' ? 1 : -1
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Serialize
|
// Serialize
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import ID from '../../../../../misc/cafy-id';
|
import ID from '../../../../../misc/cafy-id';
|
||||||
import UserList, { deleteUserList } from '../../../../../models/user-list';
|
import UserList from '../../../../../models/user-list';
|
||||||
import { ILocalUser } from '../../../../../models/user';
|
import { ILocalUser } from '../../../../../models/user';
|
||||||
import getParams from '../../../get-params';
|
import getParams from '../../../get-params';
|
||||||
|
|
||||||
@@ -37,7 +37,9 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
|
|||||||
return rej('list not found');
|
return rej('list not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteUserList(userList);
|
await UserList.remove({
|
||||||
|
_id: userList._id
|
||||||
|
});
|
||||||
|
|
||||||
res();
|
res();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -143,8 +143,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
|
|||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
userId: user._id,
|
userId: user._id
|
||||||
visibility: { $in: ['public', 'home'] }
|
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
if (ps.sinceId) {
|
if (ps.sinceId) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user