Merge branch 'develop' into sw-notification-action
This commit is contained in:
commit
ffa1dc6219
14 changed files with 88 additions and 39 deletions
15
migration/1613503367223-use-bigint-for-driveUsage.ts
Normal file
15
migration/1613503367223-use-bigint-for-driveUsage.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class useBigintForDriveUsage1613503367223 implements MigrationInterface {
|
||||||
|
name = 'useBigintForDriveUsage1613503367223'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "driveUsage" TYPE bigint`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveUsage"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "instance" ADD "driveUsage" integer NOT NULL DEFAULT 0`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ import { reactive } from 'vue';
|
||||||
import { apiUrl } from '@/config';
|
import { apiUrl } from '@/config';
|
||||||
import { waiting } from '@/os';
|
import { waiting } from '@/os';
|
||||||
import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
|
import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
||||||
type Account = {
|
type Account = {
|
||||||
|
|
|
@ -99,7 +99,8 @@ import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
|
||||||
import MkModal from '@/components/ui/modal.vue';
|
import MkModal from '@/components/ui/modal.vue';
|
||||||
import Particle from '@/components/particle.vue';
|
import Particle from '@/components/particle.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { isDeviceTouch } from '../scripts/is-device-touch';
|
import { isDeviceTouch } from '@/scripts/is-device-touch';
|
||||||
|
import { isMobile } from '@/scripts/is-mobile';
|
||||||
import { emojiCategories } from '@/instance';
|
import { emojiCategories } from '@/instance';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -322,7 +323,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
if (!os.isMobile) {
|
if (!isMobile && !isDeviceTouch) {
|
||||||
this.$refs.search.focus({
|
this.$refs.search.focus({
|
||||||
preventScroll: true
|
preventScroll: true
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,6 +69,7 @@ import { noteVisibilities } from '../../types';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { selectFile } from '@/scripts/select-file';
|
import { selectFile } from '@/scripts/select-file';
|
||||||
import { notePostInterruptors, postFormActions } from '@/store';
|
import { notePostInterruptors, postFormActions } from '@/store';
|
||||||
|
import { isMobile } from '@/scripts/is-mobile';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -580,7 +581,7 @@ export default defineComponent({
|
||||||
localOnly: this.localOnly,
|
localOnly: this.localOnly,
|
||||||
visibility: this.visibility,
|
visibility: this.visibility,
|
||||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||||
viaMobile: os.isMobile
|
viaMobile: isMobile
|
||||||
};
|
};
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
|
|
|
@ -138,13 +138,19 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
async openAccountMenu(ev) {
|
async openAccountMenu(ev) {
|
||||||
const storedAccounts = await getAccounts();
|
const storedAccounts = (await getAccounts()).filter(x => x.id !== this.$i.id);
|
||||||
const accounts = (await os.api('users/show', { userIds: storedAccounts.map(x => x.id) })).filter(x => x.id !== this.$i.id);
|
const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
||||||
|
|
||||||
const accountItems = accounts.map(account => ({
|
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
|
||||||
type: 'user',
|
accountsPromise.then(accounts => {
|
||||||
user: account,
|
const account = accounts.find(x => x.id === a.id);
|
||||||
action: () => { this.switchAccount(account); }
|
if (account == null) return res(null);
|
||||||
|
res({
|
||||||
|
type: 'user',
|
||||||
|
user: account,
|
||||||
|
action: () => { this.switchAccount(account); }
|
||||||
|
});
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
os.modalMenu([...[{
|
os.modalMenu([...[{
|
||||||
|
@ -152,7 +158,7 @@ export default defineComponent({
|
||||||
text: this.$ts.profile,
|
text: this.$ts.profile,
|
||||||
to: `/@${ this.$i.username }`,
|
to: `/@${ this.$i.username }`,
|
||||||
avatar: this.$i,
|
avatar: this.$i,
|
||||||
}, null, ...accountItems, {
|
}, null, ...accountItemPromises, {
|
||||||
icon: faPlus,
|
icon: faPlus,
|
||||||
text: this.$ts.addAcount,
|
text: this.$ts.addAcount,
|
||||||
action: () => {
|
action: () => {
|
||||||
|
|
|
@ -56,13 +56,14 @@ import { router } from '@/router';
|
||||||
import { applyTheme } from '@/scripts/theme';
|
import { applyTheme } from '@/scripts/theme';
|
||||||
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
|
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { stream, isMobile, dialog, post } from '@/os';
|
import { stream, dialog, post } from '@/os';
|
||||||
import * as sound from '@/scripts/sound';
|
import * as sound from '@/scripts/sound';
|
||||||
import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
|
import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
|
||||||
import { defaultStore, ColdDeviceStorage } from '@/store';
|
import { defaultStore, ColdDeviceStorage } from '@/store';
|
||||||
import { fetchInstance, instance } from '@/instance';
|
import { fetchInstance, instance } from '@/instance';
|
||||||
import { makeHotkey } from '@/scripts/hotkey';
|
import { makeHotkey } from '@/scripts/hotkey';
|
||||||
import { search } from '@/scripts/search';
|
import { search } from '@/scripts/search';
|
||||||
|
import { isMobile } from '@/scripts/is-mobile';
|
||||||
import { getThemes } from '@/theme-store';
|
import { getThemes } from '@/theme-store';
|
||||||
import { initializeSw } from '@/scripts/initialize-sw';
|
import { initializeSw } from '@/scripts/initialize-sw';
|
||||||
import { reloadChannel } from '@/scripts/unison-reload';
|
import { reloadChannel } from '@/scripts/unison-reload';
|
||||||
|
|
|
@ -9,9 +9,6 @@ import { resolve } from '@/router';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
|
|
||||||
const ua = navigator.userAgent.toLowerCase();
|
|
||||||
export const isMobile = /mobile|iphone|ipad|android/.test(ua);
|
|
||||||
|
|
||||||
export const stream = markRaw(new Stream());
|
export const stream = markRaw(new Stream());
|
||||||
|
|
||||||
export const pendingApiRequestsCount = ref(0);
|
export const pendingApiRequestsCount = ref(0);
|
||||||
|
|
2
src/client/scripts/is-mobile.ts
Normal file
2
src/client/scripts/is-mobile.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
const ua = navigator.userAgent.toLowerCase();
|
||||||
|
export const isMobile = /mobile|iphone|ipad|android/.test(ua);
|
|
@ -16,6 +16,8 @@
|
||||||
bg: '#000',
|
bg: '#000',
|
||||||
acrylicBg: ':alpha<0.5<@bg',
|
acrylicBg: ':alpha<0.5<@bg',
|
||||||
fg: '#dadada',
|
fg: '#dadada',
|
||||||
|
fgTransparentWeak: ':alpha<0.75<@fg',
|
||||||
|
fgTransparent: ':alpha<0.5<@fg',
|
||||||
fgHighlighted: ':lighten<3<@fg',
|
fgHighlighted: ':lighten<3<@fg',
|
||||||
divider: 'rgba(255, 255, 255, 0.1)',
|
divider: 'rgba(255, 255, 255, 0.1)',
|
||||||
indicator: '@accent',
|
indicator: '@accent',
|
||||||
|
@ -77,5 +79,6 @@
|
||||||
X14: ':alpha<0.5<@navBg',
|
X14: ':alpha<0.5<@navBg',
|
||||||
X15: ':alpha<0<@panel',
|
X15: ':alpha<0<@panel',
|
||||||
X16: ':alpha<0.7<@panel',
|
X16: ':alpha<0.7<@panel',
|
||||||
|
X17: ':alpha<0.8<@bg',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
bg: '#fff',
|
bg: '#fff',
|
||||||
acrylicBg: ':alpha<0.5<@bg',
|
acrylicBg: ':alpha<0.5<@bg',
|
||||||
fg: '#5f5f5f',
|
fg: '#5f5f5f',
|
||||||
|
fgTransparentWeak: ':alpha<0.75<@fg',
|
||||||
|
fgTransparent: ':alpha<0.5<@fg',
|
||||||
fgHighlighted: ':darken<3<@fg',
|
fgHighlighted: ':darken<3<@fg',
|
||||||
divider: 'rgba(0, 0, 0, 0.1)',
|
divider: 'rgba(0, 0, 0, 0.1)',
|
||||||
indicator: '@accent',
|
indicator: '@accent',
|
||||||
|
@ -77,5 +79,6 @@
|
||||||
X14: ':alpha<0.5<@navBg',
|
X14: ':alpha<0.5<@navBg',
|
||||||
X15: ':alpha<0<@panel',
|
X15: ':alpha<0<@panel',
|
||||||
X16: ':alpha<0.7<@panel',
|
X16: ':alpha<0.7<@panel',
|
||||||
|
X17: ':alpha<0.8<@bg',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
if (window.innerWidth < 1024) {
|
||||||
|
localStorage.setItem('ui', 'default');
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
router.beforeEach((to, from) => {
|
router.beforeEach((to, from) => {
|
||||||
this.$refs.side.navigate(to.fullPath);
|
this.$refs.side.navigate(to.fullPath);
|
||||||
// search?q=foo のようなクエリを受け取れるようにするため、return falseはできない
|
// search?q=foo のようなクエリを受け取れるようにするため、return falseはできない
|
||||||
|
@ -414,10 +419,12 @@ export default defineComponent({
|
||||||
> .body {
|
> .body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 8px 0;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
> .container {
|
> .container {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
& + .container {
|
& + .container {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
@ -426,10 +433,21 @@ export default defineComponent({
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
opacity: 0.7;
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: var(--X17);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
z-index: 1;
|
||||||
|
color: var(--fgTransparentWeak);
|
||||||
|
|
||||||
> .add {
|
> .add {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
color: var(--fgTransparentWeak);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--fg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,11 +466,11 @@ export default defineComponent({
|
||||||
|
|
||||||
&.active, &.active:hover {
|
&.active, &.active:hover {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: #fff;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.read {
|
&.read {
|
||||||
opacity: 0.5;
|
color: var(--fgTransparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
> .icon {
|
> .icon {
|
||||||
|
@ -527,6 +545,7 @@ export default defineComponent({
|
||||||
|
|
||||||
> .instance {
|
> .instance {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .clock {
|
> .clock {
|
||||||
|
|
|
@ -65,21 +65,21 @@
|
||||||
</div>
|
</div>
|
||||||
<XReactionsViewer :note="appearNote" ref="reactionsViewer"/>
|
<XReactionsViewer :note="appearNote" ref="reactionsViewer"/>
|
||||||
<footer class="footer _panel">
|
<footer class="footer _panel">
|
||||||
<button @click="reply()" class="button _button">
|
<button @click="reply()" class="button _button" v-tooltip="$ts.reply">
|
||||||
<template v-if="appearNote.reply"><Fa :icon="faReplyAll"/></template>
|
<template v-if="appearNote.reply"><Fa :icon="faReplyAll"/></template>
|
||||||
<template v-else><Fa :icon="faReply"/></template>
|
<template v-else><Fa :icon="faReply"/></template>
|
||||||
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
|
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="canRenote" @click="renote()" class="button _button" ref="renoteButton">
|
<button v-if="canRenote" @click="renote()" class="button _button" ref="renoteButton" v-tooltip="$ts.renote">
|
||||||
<Fa :icon="faRetweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
|
<Fa :icon="faRetweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button v-else class="button _button">
|
<button v-else class="button _button">
|
||||||
<Fa :icon="faBan"/>
|
<Fa :icon="faBan"/>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="appearNote.myReaction == null" class="button _button" @click="react()" ref="reactButton">
|
<button v-if="appearNote.myReaction == null" class="button _button" @click="react()" ref="reactButton" v-tooltip="$ts.reaction">
|
||||||
<Fa :icon="faPlus"/>
|
<Fa :icon="faPlus"/>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="appearNote.myReaction != null" class="button _button reacted" @click="undoReact(appearNote)" ref="reactButton">
|
<button v-if="appearNote.myReaction != null" class="button _button reacted" @click="undoReact(appearNote)" ref="reactButton" v-tooltip="$ts.reaction">
|
||||||
<Fa :icon="faMinus"/>
|
<Fa :icon="faMinus"/>
|
||||||
</button>
|
</button>
|
||||||
<button class="button _button" @click="menu()" ref="menuButton">
|
<button class="button _button" @click="menu()" ref="menuButton">
|
||||||
|
@ -911,7 +911,7 @@ export default defineComponent({
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px 16px 4px 16px;
|
padding: 12px 16px 4px 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
font-size: 90%;
|
font-size: 85%;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
color: #d28a3f;
|
color: #d28a3f;
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ import { noteVisibilities } from '../../../types';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { selectFile } from '@/scripts/select-file';
|
import { selectFile } from '@/scripts/select-file';
|
||||||
import { notePostInterruptors, postFormActions } from '@/store';
|
import { notePostInterruptors, postFormActions } from '@/store';
|
||||||
|
import { isMobile } from '@/scripts/is-mobile';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -541,7 +542,7 @@ export default defineComponent({
|
||||||
localOnly: this.localOnly,
|
localOnly: this.localOnly,
|
||||||
visibility: this.visibility,
|
visibility: this.visibility,
|
||||||
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
|
||||||
viaMobile: os.isMobile
|
viaMobile: isMobile
|
||||||
};
|
};
|
||||||
|
|
||||||
// plugin
|
// plugin
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { extractApHashtags } from './tag';
|
||||||
import { apLogger } from '../logger';
|
import { apLogger } from '../logger';
|
||||||
import { Note } from '../../../models/entities/note';
|
import { Note } from '../../../models/entities/note';
|
||||||
import { updateUsertags } from '../../../services/update-hashtag';
|
import { updateUsertags } from '../../../services/update-hashtag';
|
||||||
import { Users, UserNotePinings, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '../../../models';
|
import { Users, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '../../../models';
|
||||||
import { User, IRemoteUser } from '../../../models/entities/user';
|
import { User, IRemoteUser } from '../../../models/entities/user';
|
||||||
import { Emoji } from '../../../models/entities/emoji';
|
import { Emoji } from '../../../models/entities/emoji';
|
||||||
import { UserNotePining } from '../../../models/entities/user-note-pining';
|
import { UserNotePining } from '../../../models/entities/user-note-pining';
|
||||||
|
@ -479,18 +479,19 @@ export async function updateFeatured(userId: User['id']) {
|
||||||
.slice(0, 5)
|
.slice(0, 5)
|
||||||
.map(item => limit(() => resolveNote(item, resolver))));
|
.map(item => limit(() => resolveNote(item, resolver))));
|
||||||
|
|
||||||
// delete
|
await getConnection().transaction(async transactionalEntityManager => {
|
||||||
await UserNotePinings.delete({ userId: user.id });
|
await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
|
||||||
|
|
||||||
// とりあえずidを別の時間で生成して順番を維持
|
// とりあえずidを別の時間で生成して順番を維持
|
||||||
let td = 0;
|
let td = 0;
|
||||||
for (const note of featuredNotes.filter(note => note != null)) {
|
for (const note of featuredNotes.filter(note => note != null)) {
|
||||||
td -= 1000;
|
td -= 1000;
|
||||||
UserNotePinings.save({
|
transactionalEntityManager.insert(UserNotePining, {
|
||||||
id: genId(new Date(Date.now() + td)),
|
id: genId(new Date(Date.now() + td)),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
noteId: note!.id
|
noteId: note!.id
|
||||||
} as UserNotePining);
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue