From e85e1a2aba364274bc8f2fff19e543ad001188a3 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 20 Nov 2023 20:04:03 +0900 Subject: [PATCH 01/69] add button for joining public channel --- resources/js/chat/chat-state-store.ts | 12 ++++++--- resources/js/chat/conversation-list.tsx | 2 ++ resources/js/chat/join-channel-button.tsx | 32 +++++++++++++++++++++++ resources/lang/en/chat.php | 1 + 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 resources/js/chat/join-channel-button.tsx diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 92299029eb5..03e9fd480f9 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -24,7 +24,7 @@ import { createAnnouncement, getUpdates } from './chat-api'; import MainView from './main-view'; import PingService from './ping-service'; -type ChannelId = number | 'create' | null; +type ChannelId = number | 'create' | 'join' | null; @dispatchListener export default class ChatStateStore implements DispatchListener { @@ -53,13 +53,17 @@ export default class ChatStateStore implements DispatchListener { @computed get selectedChannel() { - return this.selected == null || this.selected === 'create' ? null : this.channelStore.get(this.selected); + return typeof this.selected === 'number' ? this.channelStore.get(this.selected) : null; } get showingCreateAnnouncement() { return this.selected === 'create'; } + get showingJoinChannel() { + return this.selected === 'join'; + } + @computed private get channelList(): Channel[] { return supportedChannelTypes.flatMap((type) => this.channelStore.groupedChannels[type]); @@ -152,12 +156,12 @@ export default class ChatStateStore implements DispatchListener { this.selected = channelId; - if (channelId === 'create') { + if (typeof channelId === 'string') { if (mode != null) { Turbolinks.controller[mode](updateQueryString(null, { channel_id: null, sendto: null, - }, 'create')); + }, channelId)); } return; diff --git a/resources/js/chat/conversation-list.tsx b/resources/js/chat/conversation-list.tsx index 758eb5369b1..e1853f007ce 100644 --- a/resources/js/chat/conversation-list.tsx +++ b/resources/js/chat/conversation-list.tsx @@ -9,6 +9,7 @@ import * as React from 'react'; import { trans } from 'utils/lang'; import ConversationListItem from './conversation-list-item'; import CreateAnnouncementButton from './create-announcement-button'; +import JoinChannelButton from './join-channel-button'; const icons: Record = { ANNOUNCE: 'fas fa-bullhorn', @@ -40,6 +41,7 @@ function renderChannels(type: SupportedChannelType) { ))} {type === 'ANNOUNCE' && } + {type === 'PUBLIC' && }
diff --git a/resources/js/chat/join-channel-button.tsx b/resources/js/chat/join-channel-button.tsx new file mode 100644 index 00000000000..d5cb4be9433 --- /dev/null +++ b/resources/js/chat/join-channel-button.tsx @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +import { observer } from 'mobx-react'; +import core from 'osu-core-singleton'; +import * as React from 'react'; +import { classWithModifiers } from 'utils/css'; +import { trans } from 'utils/lang'; + +@observer +export default class JoinChannelButton extends React.Component { + render() { + const modifiers = { selected: core.dataStore.chatState.showingJoinChannel }; + + return ( +
+ +
+ ); + } + + private readonly handleClick = () => { + core.dataStore.chatState.selectChannel('join'); + }; +} diff --git a/resources/lang/en/chat.php b/resources/lang/en/chat.php index bd30127f266..d7bdbfeab22 100644 --- a/resources/lang/en/chat.php +++ b/resources/lang/en/chat.php @@ -18,6 +18,7 @@ 'channels' => [ 'confirm_part' => 'Do you want to hide this channel? You will still receive messages from this channel.', 'create' => 'create announcement', + 'join' => 'join channel', 'list' => [ 'title' => [ From 6aeb0acc50003f7fc814e178aa1d995915c4d8b9 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 21 Nov 2023 15:27:17 +0900 Subject: [PATCH 02/69] adding channel list mark joined channels --- resources/css/bem-index.less | 3 +- resources/css/bem/chat-join-channel.less | 19 ++++++++ resources/js/chat/chat-api.ts | 4 ++ resources/js/chat/conversation-panel.tsx | 3 ++ resources/js/chat/join-channel.tsx | 57 ++++++++++++++++++++++++ resources/js/entrypoints/chat.tsx | 2 + 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 resources/css/bem/chat-join-channel.less create mode 100644 resources/js/chat/join-channel.tsx diff --git a/resources/css/bem-index.less b/resources/css/bem-index.less index 9e444ce6183..a95cf4c9dc3 100644 --- a/resources/css/bem-index.less +++ b/resources/css/bem-index.less @@ -106,8 +106,9 @@ @import "bem/chat-conversation-list-item"; @import "bem/chat-conversation-list-separator"; @import "bem/chat-conversation-panel"; -@import "bem/chat-input"; @import "bem/chat-form"; +@import "bem/chat-input"; +@import "bem/chat-join-channel"; @import "bem/chat-message-group"; @import "bem/chat-message-item"; @import "bem/circular-progress"; diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less new file mode 100644 index 00000000000..07151fc08ae --- /dev/null +++ b/resources/css/bem/chat-join-channel.less @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +.chat-join-channel { + --input-size: @font-size--title-small-3; + + display: flex; + flex-direction: column; + flex: 1; + background: hsl(var(--hsl-b4)); + + &__channels { + display: grid; + gap: 10px; + grid-template-columns: max-content 200px auto; + overflow-wrap: anywhere; + padding: 10px; + } +} diff --git a/resources/js/chat/chat-api.ts b/resources/js/chat/chat-api.ts index 0150aa76d33..35abdd5c51a 100644 --- a/resources/js/chat/chat-api.ts +++ b/resources/js/chat/chat-api.ts @@ -66,6 +66,10 @@ export function getMessages(channelId: number, params?: { until?: number }) { })); } +export function getPublicChannels() { + return $.get(route('chat.channels.index')) as JQuery.jqXHR; +} + export function getUpdates(since: number, lastHistoryId?: number | null) { return $.get( route('chat.updates'), diff --git a/resources/js/chat/conversation-panel.tsx b/resources/js/chat/conversation-panel.tsx index 1ede5cfcafb..2e88c0b63b1 100644 --- a/resources/js/chat/conversation-panel.tsx +++ b/resources/js/chat/conversation-panel.tsx @@ -8,6 +8,7 @@ import * as React from 'react'; import { trans } from 'utils/lang'; import ConversationView from './conversation-view'; import CreateAnnouncement from './create-announcement'; +import JoinChannel from './join-channel'; const lazerLink = 'https://github.com/ppy/osu/releases'; @@ -20,6 +21,8 @@ export default class ConversationPanel extends React.Component ) : core.dataStore.chatState.showingCreateAnnouncement ? ( + ) : core.dataStore.chatState.showingJoinChannel ? ( + ) : (
diff --git a/resources/js/chat/join-channel.tsx b/resources/js/chat/join-channel.tsx new file mode 100644 index 00000000000..184d0e23c56 --- /dev/null +++ b/resources/js/chat/join-channel.tsx @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +import { Spinner } from 'components/spinner'; +import ChannelJson from 'interfaces/chat/channel-json'; +import { computed, makeObservable, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import core from 'osu-core-singleton'; +import * as React from 'react'; +import { getPublicChannels } from './chat-api'; + +type Props = Record; + +@observer +export default class JoinChannel extends React.Component { + @observable private channels?: ChannelJson[]; + + + @computed + get joinedChannelIds() { + return new Set(core.dataStore.channelStore.groupedChannels.PUBLIC.map((channel) => channel.channelId)); + } + + constructor(props: Props) { + super(props); + + makeObservable(this); + + this.loadChannelList(); + } + + render() { + if (this.channels == null) { + return ; + } + + return ( +
+
+ {this.channels.map((channel) => ( + +
{this.joinedChannelIds.has(channel.channel_id) && }
+
{channel.name}
+
{channel.description}
+
+ ))} +
+
+ ); + } + + private async loadChannelList() { + if (this.channels != null) return; + + this.channels = await getPublicChannels(); + } +} diff --git a/resources/js/entrypoints/chat.tsx b/resources/js/entrypoints/chat.tsx index 9b8f6a831db..ab96a3953e0 100644 --- a/resources/js/entrypoints/chat.tsx +++ b/resources/js/entrypoints/chat.tsx @@ -79,6 +79,8 @@ core.reactTurbolinks.register('chat', action(() => { if (currentUrl().hash === '#create') { core.dataStore.chatState.selectChannel('create', 'replaceHistory'); + } else if (currentUrl().hash === '#join') { + core.dataStore.chatState.selectChannel('join', 'replaceHistory'); } else { const channel = getInitialChannel(initial?.send_to); From 7b82e0f9e16e8e8a2dee5c56d0ebe8721836e5dd Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 21 Nov 2023 21:05:10 +0900 Subject: [PATCH 03/69] join public channel --- resources/css/bem/chat-join-channel.less | 18 ++++++++++---- resources/js/chat/chat-api.ts | 7 ++++++ resources/js/chat/chat-state-store.ts | 30 ++++++++++++++---------- resources/js/chat/conversation-list.tsx | 2 +- resources/js/chat/join-channel.tsx | 21 +++++++++++++---- 5 files changed, 55 insertions(+), 23 deletions(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 07151fc08ae..8fa37a1efb0 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -2,13 +2,23 @@ // See the LICENCE file in the repository root for full licence text. .chat-join-channel { - --input-size: @font-size--title-small-3; - - display: flex; - flex-direction: column; flex: 1; background: hsl(var(--hsl-b4)); + &__channel { + display: contents; + color: hsl(var(--hsl-l3)); + text-align: left; + + .link-hover({ + color: hsl(var(--hsl-c1)); + }); + + &--joined { + color: hsl(var(--hsl-c1)); + } + } + &__channels { display: grid; gap: 10px; diff --git a/resources/js/chat/chat-api.ts b/resources/js/chat/chat-api.ts index 35abdd5c51a..98f1004ac22 100644 --- a/resources/js/chat/chat-api.ts +++ b/resources/js/chat/chat-api.ts @@ -81,6 +81,13 @@ export function getUpdates(since: number, lastHistoryId?: number | null) { ) as JQuery.jqXHR; } +export function joinChannel(channelId: number, userId: number) { + return $.ajax({ + type: 'PUT', + url: route('chat.channels.join', { channel: channelId, user: userId }), + }) as JQuery.jqXHR; +} + export function markAsRead(channelId: number, messageId: number) { return $.ajax({ type: 'PUT', diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 03e9fd480f9..c2d92e8d277 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -20,7 +20,7 @@ import { trans } from 'utils/lang'; import { updateQueryString } from 'utils/url'; import ChannelJoinEvent from './channel-join-event'; import ChannelPartEvent from './channel-part-event'; -import { createAnnouncement, getUpdates } from './chat-api'; +import { createAnnouncement, getUpdates, joinChannel } from './chat-api'; import MainView from './main-view'; import PingService from './ping-service'; @@ -129,18 +129,22 @@ export default class ChatStateStore implements DispatchListener { // Only up to one join/create channel operation should be allowed to be running at any time. // For consistency, the operation is considered complete when the websocket message arrives, not when the request completes. @action - joinChannel() { - if (!this.createAnnouncement.isValid || this.isJoiningChannel) return; - - const json = this.createAnnouncement.toJson(); - this.waitJoinChannelUuid = json.uuid; - // TODO: when adding more channel types to join, remember to add separate busy spinner states for them. - - createAnnouncement(json) - .fail(action((xhr: JQueryXHR) => { - onError(xhr); - this.waitJoinChannelUuid = null; - })); + joinChannel(channelId?: number) { + if (channelId != null) { + joinChannel(channelId, core.currentUserOrFail.id); + } else { + if (!this.createAnnouncement.isValid || this.isJoiningChannel) return; + + const json = this.createAnnouncement.toJson(); + this.waitJoinChannelUuid = json.uuid; + // TODO: when adding more channel types to join, remember to add separate busy spinner states for them. + + createAnnouncement(json) + .fail(action((xhr: JQueryXHR) => { + onError(xhr); + this.waitJoinChannelUuid = null; + })); + } } @action diff --git a/resources/js/chat/conversation-list.tsx b/resources/js/chat/conversation-list.tsx index e1853f007ce..1bef79eac99 100644 --- a/resources/js/chat/conversation-list.tsx +++ b/resources/js/chat/conversation-list.tsx @@ -20,7 +20,7 @@ const icons: Record = { function renderChannels(type: SupportedChannelType) { const channels = core.dataStore.channelStore.groupedChannels[type]; - if (channels.length > 0 || type === 'ANNOUNCE' && core.dataStore.chatState.canChatAnnounce) { + if (channels.length > 0 || type ==='PUBLIC' ||type === 'ANNOUNCE' && core.dataStore.chatState.canChatAnnounce) { const title = trans(`chat.channels.list.title.${type}`); // Optimization so that the channel list can be rendered as several smaller layers. diff --git a/resources/js/chat/join-channel.tsx b/resources/js/chat/join-channel.tsx index 184d0e23c56..a16521621cd 100644 --- a/resources/js/chat/join-channel.tsx +++ b/resources/js/chat/join-channel.tsx @@ -7,10 +7,21 @@ import { computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import core from 'osu-core-singleton'; import * as React from 'react'; +import { classWithModifiers } from 'utils/css'; import { getPublicChannels } from './chat-api'; type Props = Record; +function Channel({ channel, joined, onClick }: { channel: ChannelJson; joined: boolean; onClick: (id: number) => void }) { + return ( + + ); +} + @observer export default class JoinChannel extends React.Component { @observable private channels?: ChannelJson[]; @@ -38,17 +49,17 @@ export default class JoinChannel extends React.Component {
{this.channels.map((channel) => ( - -
{this.joinedChannelIds.has(channel.channel_id) && }
-
{channel.name}
-
{channel.description}
-
+ ))}
); } + private readonly handleClick = (channelId: number) => { + core.dataStore.chatState.joinChannel(channelId); + }; + private async loadChannelList() { if (this.channels != null) return; From 6ae117b873b914f6e2f92dd28d5ce15245868a28 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 21 Nov 2023 21:07:40 +0900 Subject: [PATCH 04/69] rename component to JoinChannels --- resources/js/chat/conversation-panel.tsx | 4 ++-- resources/js/chat/{join-channel.tsx => join-channels.tsx} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename resources/js/chat/{join-channel.tsx => join-channels.tsx} (96%) diff --git a/resources/js/chat/conversation-panel.tsx b/resources/js/chat/conversation-panel.tsx index 2e88c0b63b1..21f6d490353 100644 --- a/resources/js/chat/conversation-panel.tsx +++ b/resources/js/chat/conversation-panel.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { trans } from 'utils/lang'; import ConversationView from './conversation-view'; import CreateAnnouncement from './create-announcement'; -import JoinChannel from './join-channel'; +import JoinChannels from './join-channels'; const lazerLink = 'https://github.com/ppy/osu/releases'; @@ -22,7 +22,7 @@ export default class ConversationPanel extends React.Component ) : core.dataStore.chatState.showingJoinChannel ? ( - + ) : (
diff --git a/resources/js/chat/join-channel.tsx b/resources/js/chat/join-channels.tsx similarity index 96% rename from resources/js/chat/join-channel.tsx rename to resources/js/chat/join-channels.tsx index a16521621cd..2e63dace679 100644 --- a/resources/js/chat/join-channel.tsx +++ b/resources/js/chat/join-channels.tsx @@ -23,7 +23,7 @@ function Channel({ channel, joined, onClick }: { channel: ChannelJson; joined: b } @observer -export default class JoinChannel extends React.Component { +export default class JoinChannels extends React.Component { @observable private channels?: ChannelJson[]; From 19a5bdc0e8472da1579ca1bb6b00770c79d90498 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 29 Nov 2023 17:45:25 +0900 Subject: [PATCH 05/69] useCallback the callback --- resources/js/chat/join-channels.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index 2e63dace679..e3e95dbc7be 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -12,9 +12,20 @@ import { getPublicChannels } from './chat-api'; type Props = Record; -function Channel({ channel, joined, onClick }: { channel: ChannelJson; joined: boolean; onClick: (id: number) => void }) { +interface ChannelProps { + channel: ChannelJson; + joined: boolean; + onClick: (channelId: number) => void; +} + +function Channel({ channel, joined, onClick }: ChannelProps) { + const handleClick = React.useCallback( + () => onClick(channel.channel_id), + [channel.channel_id, onClick], + ); + return ( -
+ ); + } + + renderContent() { + const chatState = core.dataStore.chatState; + + if (chatState.selectedChannelId === 'create') { + return ; + } + + if (chatState.selectedChannelId === 'join') { + return ; + } + + if (chatState.selectedChannel != null) { + return ; + } + + return ( +
+ + {core.dataStore.channelStore.channels.size > 0 ? ( + <> +
{trans('chat.not_found.title')}
+
{trans('chat.not_found.message')}
+ ) : ( -
- - {core.dataStore.channelStore.channels.size > 0 ? ( - <> -
{trans('chat.not_found.title')}
-
{trans('chat.not_found.message')}
- - ) : ( - <> -
{trans('chat.no-conversations.title')}
-
{trans('chat.no-conversations.howto')}
-
- - )} -
+ <> +
{trans('chat.no-conversations.title')}
+
{trans('chat.no-conversations.howto')}
+
+ )}
); diff --git a/resources/js/chat/create-announcement-button.tsx b/resources/js/chat/create-announcement-button.tsx index 30711d7a641..1738b8a5493 100644 --- a/resources/js/chat/create-announcement-button.tsx +++ b/resources/js/chat/create-announcement-button.tsx @@ -12,7 +12,7 @@ export default class CreateAnnouncementButton extends React.Component { render() { if (!core.dataStore.chatState.canChatAnnounce) return null; - const modifiers = { selected: core.dataStore.chatState.showingCreateAnnouncement }; + const modifiers = { selected: core.dataStore.chatState.selectedChannelId === 'create' }; return (
diff --git a/resources/js/chat/join-channel-button.tsx b/resources/js/chat/join-channel-button.tsx index d5cb4be9433..dca94036fc8 100644 --- a/resources/js/chat/join-channel-button.tsx +++ b/resources/js/chat/join-channel-button.tsx @@ -10,7 +10,7 @@ import { trans } from 'utils/lang'; @observer export default class JoinChannelButton extends React.Component { render() { - const modifiers = { selected: core.dataStore.chatState.showingJoinChannel }; + const modifiers = { selected: core.dataStore.chatState.selectedChannelId === 'join' }; return (
From 5e05f24fe7b7e6bd50234d52316608f47e697dcd Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 29 Nov 2023 19:41:03 +0900 Subject: [PATCH 07/69] combine buttons --- ...ment-button.tsx => add-channel-button.tsx} | 14 +++++--- resources/js/chat/conversation-list.tsx | 7 ++-- resources/js/chat/join-channel-button.tsx | 32 ------------------- 3 files changed, 12 insertions(+), 41 deletions(-) rename resources/js/chat/{create-announcement-button.tsx => add-channel-button.tsx} (73%) delete mode 100644 resources/js/chat/join-channel-button.tsx diff --git a/resources/js/chat/create-announcement-button.tsx b/resources/js/chat/add-channel-button.tsx similarity index 73% rename from resources/js/chat/create-announcement-button.tsx rename to resources/js/chat/add-channel-button.tsx index 1738b8a5493..e51c6ffec80 100644 --- a/resources/js/chat/create-announcement-button.tsx +++ b/resources/js/chat/add-channel-button.tsx @@ -7,12 +7,16 @@ import * as React from 'react'; import { classWithModifiers } from 'utils/css'; import { trans } from 'utils/lang'; +interface Props { + type: 'create' | 'join'; +} + @observer -export default class CreateAnnouncementButton extends React.Component { +export default class AddChannelButton extends React.Component { render() { - if (!core.dataStore.chatState.canChatAnnounce) return null; + if (this.props.type === 'create' && !core.dataStore.chatState.canChatAnnounce) return null; - const modifiers = { selected: core.dataStore.chatState.selectedChannelId === 'create' }; + const modifiers = { selected: core.dataStore.chatState.selectedChannelId === this.props.type }; return (
@@ -22,13 +26,13 @@ export default class CreateAnnouncementButton extends React.Component {
-
{trans('chat.channels.create')}
+
{trans(`chat.channels.${this.props.type}`)}
); } private readonly handleClick = () => { - core.dataStore.chatState.selectChannel('create'); + core.dataStore.chatState.selectChannel(this.props.type); }; } diff --git a/resources/js/chat/conversation-list.tsx b/resources/js/chat/conversation-list.tsx index 1bef79eac99..0b759887784 100644 --- a/resources/js/chat/conversation-list.tsx +++ b/resources/js/chat/conversation-list.tsx @@ -7,9 +7,8 @@ import { observer } from 'mobx-react'; import core from 'osu-core-singleton'; import * as React from 'react'; import { trans } from 'utils/lang'; +import AddChannelButton from './add-channel-button'; import ConversationListItem from './conversation-list-item'; -import CreateAnnouncementButton from './create-announcement-button'; -import JoinChannelButton from './join-channel-button'; const icons: Record = { ANNOUNCE: 'fas fa-bullhorn', @@ -40,8 +39,8 @@ function renderChannels(type: SupportedChannelType) { {c.map((channel) => )}
))} - {type === 'ANNOUNCE' && } - {type === 'PUBLIC' && } + {type === 'ANNOUNCE' && } + {type === 'PUBLIC' && }
diff --git a/resources/js/chat/join-channel-button.tsx b/resources/js/chat/join-channel-button.tsx deleted file mode 100644 index dca94036fc8..00000000000 --- a/resources/js/chat/join-channel-button.tsx +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. -// See the LICENCE file in the repository root for full licence text. - -import { observer } from 'mobx-react'; -import core from 'osu-core-singleton'; -import * as React from 'react'; -import { classWithModifiers } from 'utils/css'; -import { trans } from 'utils/lang'; - -@observer -export default class JoinChannelButton extends React.Component { - render() { - const modifiers = { selected: core.dataStore.chatState.selectedChannelId === 'join' }; - - return ( -
- -
- ); - } - - private readonly handleClick = () => { - core.dataStore.chatState.selectChannel('join'); - }; -} From 3f76dfdacfe013b10758190b2bb8463a2db1a700 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 29 Nov 2023 20:03:37 +0900 Subject: [PATCH 08/69] assign in action --- resources/js/chat/join-channels.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index e3e95dbc7be..5c12ab5df90 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -3,7 +3,7 @@ import { Spinner } from 'components/spinner'; import ChannelJson from 'interfaces/chat/channel-json'; -import { computed, makeObservable, observable } from 'mobx'; +import { computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import core from 'osu-core-singleton'; import * as React from 'react'; @@ -74,6 +74,13 @@ export default class JoinChannels extends React.Component { private async loadChannelList() { if (this.channels != null) return; - this.channels = await getPublicChannels(); + try { + const channels = await getPublicChannels(); + runInAction(() => { + this.channels = channels; + }); + } catch { + // TODO: show error + } } } From 30f04b74363a7f80875f9bebb90d9204644a877b Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 30 Nov 2023 00:03:12 +0900 Subject: [PATCH 09/69] switch to channel after joining --- resources/js/chat/chat-state-store.ts | 42 +++++++++++++------ .../js/models/chat/create-announcement.ts | 1 - 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 03978b50c33..f7434970d47 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -15,7 +15,7 @@ import Channel from 'models/chat/channel'; import CreateAnnouncement from 'models/chat/create-announcement'; import core from 'osu-core-singleton'; import ChannelStore from 'stores/channel-store'; -import { onError } from 'utils/ajax'; +import { isJqXHR, onError } from 'utils/ajax'; import { trans } from 'utils/lang'; import { updateQueryString } from 'utils/url'; import ChannelJoinEvent from './channel-join-event'; @@ -38,7 +38,7 @@ export default class ChatStateStore implements DispatchListener { private readonly pingService: PingService; @observable private selected: ChannelId = null; private selectedIndex = 0; - @observable private waitJoinChannelUuid: string | null = null; + @observable private waitJoinChannelUuid: string | number | null = null; @computed get isChatMounted() { @@ -51,6 +51,13 @@ export default class ChatStateStore implements DispatchListener { return this.waitJoinChannelUuid != null; } + get waitJoinChannelType() { + if (this.waitJoinChannelUuid == null) return null; + return typeof this.waitJoinChannelUuid === 'string' + ? 'announce' + : 'existing'; + } + @computed get selectedChannel() { return typeof this.selected === 'number' ? this.channelStore.get(this.selected) : null; @@ -130,20 +137,21 @@ export default class ChatStateStore implements DispatchListener { // For consistency, the operation is considered complete when the websocket message arrives, not when the request completes. @action joinChannel(channelId?: number) { + if (this.isJoiningChannel) return; + if (channelId != null) { - joinChannel(channelId, core.currentUserOrFail.id); + this.waitJoinChannelUuid = channelId; + joinChannel(channelId, core.currentUserOrFail.id) + .fail(this.handleJoinChannelFail); } else { - if (!this.createAnnouncement.isValid || this.isJoiningChannel) return; + if (!this.createAnnouncement.isValid) return; const json = this.createAnnouncement.toJson(); this.waitJoinChannelUuid = json.uuid; // TODO: when adding more channel types to join, remember to add separate busy spinner states for them. createAnnouncement(json) - .fail(action((xhr: JQueryXHR) => { - onError(xhr); - this.waitJoinChannelUuid = null; - })); + .fail(this.handleJoinChannelFail); } } @@ -221,12 +229,15 @@ export default class ChatStateStore implements DispatchListener { @action private handleChatChannelJoinEvent(event: ChannelJoinEvent) { - this.channelStore.update(event.json); + const json = event.json; + this.channelStore.update(json); - if (this.waitJoinChannelUuid != null && this.waitJoinChannelUuid === event.json.uuid) { - this.selectChannel(event.json.channel_id); + if (this.waitJoinChannelType === 'announce' && this.waitJoinChannelUuid === json.uuid + || this.waitJoinChannelType === 'existing' && this.waitJoinChannelUuid === json.channel_id + ) { + this.selectChannel(json.channel_id); this.waitJoinChannelUuid = null; - if (event.json.type === 'ANNOUNCE') { + if (json.type === 'ANNOUNCE') { this.createAnnouncement.clear(); } } @@ -253,6 +264,13 @@ export default class ChatStateStore implements DispatchListener { channel?.loadMetadata(); } + @action + private readonly handleJoinChannelFail = (error: unknown) => { + if (!isJqXHR(error)) throw error; + onError(error); + this.waitJoinChannelUuid = null; + }; + @action private handleSocketStateChanged(event: SocketStateChangedAction) { this.isConnected = event.connected; diff --git a/resources/js/models/chat/create-announcement.ts b/resources/js/models/chat/create-announcement.ts index 454c685a1a3..60c712fdede 100644 --- a/resources/js/models/chat/create-announcement.ts +++ b/resources/js/models/chat/create-announcement.ts @@ -200,7 +200,6 @@ export default class CreateAnnouncement implements FormWithErrors { try { this.xhrLookupUsers = $.getJSON(route('chat.users.index'), { ids: userIds }); const response = await this.xhrLookupUsers; - this.extractValidUsers(response.users); } catch (error) { onError(error); From 1e777c1566fc19124e77e3a02dd5d9cebb6188b4 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 27 Dec 2023 15:54:57 +0900 Subject: [PATCH 10/69] set correct url for pm if channel already exists --- resources/js/chat/chat-state-store.ts | 9 ++------- resources/js/turbolinks-overrides.coffee | 8 +++++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index f7434970d47..504c0fd1766 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -157,17 +157,14 @@ export default class ChatStateStore implements DispatchListener { @action selectChannel(channelId: ChannelId, mode: 'advanceHistory' | 'replaceHistory' | null = 'advanceHistory') { - // TODO: enforce location url even if channel doesn't change; - // noticeable when navigating via ?sendto= on existing channel. - if (this.selected === channelId) return; - // Mark the channel being switched away from as read. // Marking as read is done here to avoid constantly sending mark-as-read requests // while receiving messages when autoScroll is enabled on the channel. this.selectedChannel?.throttledSendMarkAsRead(); - this.selected = channelId; + if (channelId == null) return; + if (typeof channelId === 'string') { if (mode != null) { Turbolinks.controller[mode](updateQueryString(null, { @@ -179,8 +176,6 @@ export default class ChatStateStore implements DispatchListener { return; } - if (channelId == null) return; - const channel = this.channelStore.get(channelId); if (channel == null) { diff --git a/resources/js/turbolinks-overrides.coffee b/resources/js/turbolinks-overrides.coffee index 379dc38a5d3..ef74133b149 100644 --- a/resources/js/turbolinks-overrides.coffee +++ b/resources/js/turbolinks-overrides.coffee @@ -52,9 +52,15 @@ Turbolinks.Controller::advanceHistory = (url) -> Turbolinks.Controller::replaceHistory = (url) -> return if url == currentUrl().href - history.replaceState history.state, '', url @lastRenderedLocation = Turbolinks.Location.wrap(url) + if window.newUrl? + # replaceState needs to run after turbolinks:load to override the correct url + Timeout.set 0, -> + history.replaceState history.state, '', url + else + history.replaceState history.state, '', url + # Ignore anchor check on loading snapshot to prevent repeating requesting page # when the target doesn't exist. From 49ed23e2faa0aa466dcaefaccd1533b909e2bdf8 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 27 Dec 2023 18:03:08 +0900 Subject: [PATCH 11/69] mimimum size for joined column --- resources/css/bem/chat-join-channel.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 8fa37a1efb0..3503ce43581 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -22,7 +22,8 @@ &__channels { display: grid; gap: 10px; - grid-template-columns: max-content 200px auto; + // checkmark is 14px wide + grid-template-columns: minmax(14px, max-content) 200px auto; overflow-wrap: anywhere; padding: 10px; } From ff6f1d4a86d7101bf94aead04ae7367895cdea0a Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 27 Dec 2023 19:13:51 +0900 Subject: [PATCH 12/69] show loading overlay --- resources/js/chat/chat-state-store.ts | 77 ++++++++++++++++----------- resources/js/chat/join-channels.tsx | 41 ++++++++++---- 2 files changed, 79 insertions(+), 39 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 504c0fd1766..173f5d4d53d 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -17,6 +17,7 @@ import core from 'osu-core-singleton'; import ChannelStore from 'stores/channel-store'; import { isJqXHR, onError } from 'utils/ajax'; import { trans } from 'utils/lang'; +import { hideLoadingOverlay, showImmediateLoadingOverlay } from 'utils/loading-overlay'; import { updateQueryString } from 'utils/url'; import ChannelJoinEvent from './channel-join-event'; import ChannelPartEvent from './channel-part-event'; @@ -38,7 +39,7 @@ export default class ChatStateStore implements DispatchListener { private readonly pingService: PingService; @observable private selected: ChannelId = null; private selectedIndex = 0; - @observable private waitJoinChannelUuid: string | number | null = null; + @observable private waitJoinChannelId: string | number | null = null; @computed get isChatMounted() { @@ -48,14 +49,19 @@ export default class ChatStateStore implements DispatchListener { // Should not join/create another channel if still waiting for a pending request. @computed get isJoiningChannel() { - return this.waitJoinChannelUuid != null; + return this.waitJoinChannelId != null; } - get waitJoinChannelType() { - if (this.waitJoinChannelUuid == null) return null; - return typeof this.waitJoinChannelUuid === 'string' - ? 'announce' - : 'existing'; + @computed + get joiningChannelId() { + return typeof this.waitJoinChannelId === 'number' + ? this.waitJoinChannelId + : null; + } + + @computed + get joinedPublicChannelIds() { + return new Set(this.channelStore.groupedChannels.PUBLIC.map((channel) => channel.channelId)); } @computed @@ -76,6 +82,13 @@ export default class ChatStateStore implements DispatchListener { return supportedChannelTypes.flatMap((type) => this.channelStore.groupedChannels[type]); } + private get waitJoinChannelType() { + if (this.waitJoinChannelId == null) return null; + return typeof this.waitJoinChannelId === 'string' + ? 'announce' + : 'existing'; + } + constructor(protected channelStore: ChannelStore) { this.pingService = new PingService(channelStore); @@ -132,27 +145,36 @@ export default class ChatStateStore implements DispatchListener { } } - // TODO: This will support the other types of joining channels in the future // Only up to one join/create channel operation should be allowed to be running at any time. // For consistency, the operation is considered complete when the websocket message arrives, not when the request completes. + // TODO: allow user cancelling operation from UI? @action - joinChannel(channelId?: number) { + async joinChannel(channelId?: number) { if (this.isJoiningChannel) return; - if (channelId != null) { - this.waitJoinChannelUuid = channelId; - joinChannel(channelId, core.currentUserOrFail.id) - .fail(this.handleJoinChannelFail); - } else { - if (!this.createAnnouncement.isValid) return; + showImmediateLoadingOverlay(); + + try { + if (channelId != null) { + this.waitJoinChannelId = channelId; + await joinChannel(channelId, core.currentUserOrFail.id); + } else { + if (!this.createAnnouncement.isValid) return; - const json = this.createAnnouncement.toJson(); - this.waitJoinChannelUuid = json.uuid; - // TODO: when adding more channel types to join, remember to add separate busy spinner states for them. + const json = this.createAnnouncement.toJson(); + this.waitJoinChannelId = json.uuid; + await createAnnouncement(json); + } + } catch (error) { + if (!isJqXHR(error)) throw error; - createAnnouncement(json) - .fail(this.handleJoinChannelFail); + onError(error); + runInAction(() => { + this.waitJoinChannelId = null; + }); } + + hideLoadingOverlay(); } @action @@ -227,11 +249,13 @@ export default class ChatStateStore implements DispatchListener { const json = event.json; this.channelStore.update(json); - if (this.waitJoinChannelType === 'announce' && this.waitJoinChannelUuid === json.uuid - || this.waitJoinChannelType === 'existing' && this.waitJoinChannelUuid === json.channel_id + if (this.waitJoinChannelType === 'announce' && this.waitJoinChannelId === json.uuid + || this.waitJoinChannelType === 'existing' && this.waitJoinChannelId === json.channel_id ) { + // hide overlay before changing channel if we're waiting for a change to remove it from history navigation. + hideLoadingOverlay(); this.selectChannel(json.channel_id); - this.waitJoinChannelUuid = null; + this.waitJoinChannelId = null; if (json.type === 'ANNOUNCE') { this.createAnnouncement.clear(); } @@ -259,13 +283,6 @@ export default class ChatStateStore implements DispatchListener { channel?.loadMetadata(); } - @action - private readonly handleJoinChannelFail = (error: unknown) => { - if (!isJqXHR(error)) throw error; - onError(error); - this.waitJoinChannelUuid = null; - }; - @action private handleSocketStateChanged(event: SocketStateChangedAction) { this.isConnected = event.connected; diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index 5c12ab5df90..876a3e13046 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -10,23 +10,31 @@ import * as React from 'react'; import { classWithModifiers } from 'utils/css'; import { getPublicChannels } from './chat-api'; +type JoinedStatus = 'joined' | 'joining' | null; type Props = Record; interface ChannelProps { channel: ChannelJson; - joined: boolean; onClick: (channelId: number) => void; + status: JoinedStatus; } -function Channel({ channel, joined, onClick }: ChannelProps) { +function Channel({ channel, onClick, status }: ChannelProps) { const handleClick = React.useCallback( () => onClick(channel.channel_id), [channel.channel_id, onClick], ); + let statusElement: React.ReactNode | undefined; + if (status === 'joined') { + statusElement = ; + } else if (status === 'joining') { + statusElement = ; + } + return ( - @@ -37,9 +45,8 @@ function Channel({ channel, joined, onClick }: ChannelProps) { export default class JoinChannels extends React.Component { @observable private channels?: ChannelJson[]; - @computed - get joinedChannelIds() { + get joinedPublicChannelIds() { return new Set(core.dataStore.channelStore.groupedChannels.PUBLIC.map((channel) => channel.channelId)); } @@ -59,9 +66,7 @@ export default class JoinChannels extends React.Component { return (
- {this.channels.map((channel) => ( - - ))} + {this.channels.map(this.renderChannel)}
); @@ -83,4 +88,22 @@ export default class JoinChannels extends React.Component { // TODO: show error } } + + private readonly renderChannel = (channel: ChannelJson) => { + let status: JoinedStatus = null; + if (this.joinedPublicChannelIds.has(channel.channel_id)) { + status = 'joined'; + } else if (core.dataStore.chatState.joiningChannelId === channel.channel_id) { + status = 'joining'; + } + + return ( + + ); + }; } From 459f8df97c61d57ac9cf3ac8418bf6ddf152139b Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 27 Dec 2023 19:27:12 +0900 Subject: [PATCH 13/69] fix NaN maxHeight; can happen when navigating forward to a channel from a join-channels --- resources/js/components/textarea-autosize.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/components/textarea-autosize.tsx b/resources/js/components/textarea-autosize.tsx index 10deca89f37..ed2a4ffd6f7 100644 --- a/resources/js/components/textarea-autosize.tsx +++ b/resources/js/components/textarea-autosize.tsx @@ -24,7 +24,7 @@ export default class TextareaAutosize extends React.PureComponent private shouldUpdate = true; private get maxHeight() { - return this.props.maxRows != null && this.state.lineHeight != null + return this.props.maxRows != null && this.state.lineHeight != null && Number.isFinite(this.state.lineHeight) ? this.state.lineHeight * (this.props.maxRows + 1) // additional row to fit maxRows + slight leeway for scrollbar : null; } From 40a03ff8c36c5f8c723469390bed0f03bd91558c Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 27 Dec 2023 19:40:02 +0900 Subject: [PATCH 14/69] show spinner when loading channel list --- resources/css/bem/chat-join-channel.less | 7 +++++++ resources/js/chat/join-channels.tsx | 18 +++++++++++------- resources/lang/en/chat.php | 4 ++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 3503ce43581..625afc5424f 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -27,4 +27,11 @@ overflow-wrap: anywhere; padding: 10px; } + + &__loading { + .center-content(); + display: flex; + flex-direction: column; + height: 100%; + } } diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index 876a3e13046..b6fe68ad23a 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -8,6 +8,7 @@ import { observer } from 'mobx-react'; import core from 'osu-core-singleton'; import * as React from 'react'; import { classWithModifiers } from 'utils/css'; +import { trans } from 'utils/lang'; import { getPublicChannels } from './chat-api'; type JoinedStatus = 'joined' | 'joining' | null; @@ -59,15 +60,18 @@ export default class JoinChannels extends React.Component { } render() { - if (this.channels == null) { - return ; - } - return (
-
- {this.channels.map(this.renderChannel)} -
+ {this.channels == null ? ( +
+ + {trans('chat.join_channels.loading')} +
+ ) : ( +
+ {this.channels.map(this.renderChannel)} +
+ )}
); } diff --git a/resources/lang/en/chat.php b/resources/lang/en/chat.php index d7bdbfeab22..f146161bbd2 100644 --- a/resources/lang/en/chat.php +++ b/resources/lang/en/chat.php @@ -61,4 +61,8 @@ 'lazer' => 'Public channels you join via osu!lazer will also be visible here.', 'title' => 'no conversations yet', ], + + 'join_channels' => [ + 'loading' => 'Loading channel list...', + ], ]; From d896d5773989d65a7b8c7afe82cbce297ab8c6e5 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 27 Dec 2023 20:00:59 +0900 Subject: [PATCH 15/69] handle loading channel list error --- resources/js/chat/join-channels.tsx | 53 +++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index b6fe68ad23a..3fa860ac003 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -3,10 +3,11 @@ import { Spinner } from 'components/spinner'; import ChannelJson from 'interfaces/chat/channel-json'; -import { computed, makeObservable, observable, runInAction } from 'mobx'; +import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import core from 'osu-core-singleton'; import * as React from 'react'; +import { isJqXHR } from 'utils/ajax'; import { classWithModifiers } from 'utils/css'; import { trans } from 'utils/lang'; import { getPublicChannels } from './chat-api'; @@ -45,6 +46,8 @@ function Channel({ channel, onClick, status }: ChannelProps) { @observer export default class JoinChannels extends React.Component { @observable private channels?: ChannelJson[]; + @observable private error = false; + private xhr: ReturnType | null= null; @computed get joinedPublicChannelIds() { @@ -59,13 +62,16 @@ export default class JoinChannels extends React.Component { this.loadChannelList(); } + componentWillUnmount() { + this.xhr?.abort(); + } + render() { return (
{this.channels == null ? (
- - {trans('chat.join_channels.loading')} + {this.renderLoading()}
) : (
@@ -80,16 +86,29 @@ export default class JoinChannels extends React.Component { core.dataStore.chatState.joinChannel(channelId); }; + private readonly handleRetryClick = () => { + this.loadChannelList(); + }; + + @action private async loadChannelList() { - if (this.channels != null) return; + if (this.channels != null || this.xhr != null) return; + + this.error = false; try { - const channels = await getPublicChannels(); + this.xhr = getPublicChannels(); + const channels = await this.xhr; runInAction(() => { this.channels = channels; }); - } catch { - // TODO: show error + } catch (error) { + if (!isJqXHR(error)) throw error; + runInAction(() => { + this.error = true; + }); + } finally { + this.xhr = null; } } @@ -110,4 +129,24 @@ export default class JoinChannels extends React.Component { /> ); }; + + private renderLoading() { + if (this.error) { + return ( + <> +

{trans('errors.load_failed')}

+ + + ); + } + + return ( + <> + +

{trans('chat.join_channels.loading')}

+ + ); + } } From 2e65cb56f678ada9292cc559d03836b3f40e9d77 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 27 Dec 2023 20:13:41 +0900 Subject: [PATCH 16/69] parse hash for selectChannel --- resources/js/entrypoints/chat.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/js/entrypoints/chat.tsx b/resources/js/entrypoints/chat.tsx index ab96a3953e0..545eaf1b446 100644 --- a/resources/js/entrypoints/chat.tsx +++ b/resources/js/entrypoints/chat.tsx @@ -26,6 +26,10 @@ interface SendToJson { target: UserJson; } +function getChannelHash(hash: string) { + return hash.startsWith('#') ? hash.slice(1) : null; +} + function getParamValue(urlParams: URLSearchParams, key: string) { const value = Number(urlParams.get(key)); return Number.isInteger(value) && value > 0 ? value : null; @@ -77,10 +81,10 @@ core.reactTurbolinks.register('chat', action(() => { core.dataStore.channelStore.lastReceivedMessageId = initial.last_message_id ?? 0; } - if (currentUrl().hash === '#create') { - core.dataStore.chatState.selectChannel('create', 'replaceHistory'); - } else if (currentUrl().hash === '#join') { - core.dataStore.chatState.selectChannel('join', 'replaceHistory'); + const hash = getChannelHash(currentUrl().hash); + + if (hash === 'create' || hash === 'join') { + core.dataStore.chatState.selectChannel(hash, 'replaceHistory'); } else { const channel = getInitialChannel(initial?.send_to); From d44068ea3b34a8326b2b4d44bd6b41251dc1c499 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 28 Dec 2023 15:21:13 +0900 Subject: [PATCH 17/69] rollup possible options into single property --- resources/js/chat/add-channel-button.tsx | 2 +- resources/js/chat/chat-state-store.ts | 18 +++++++++--------- resources/js/chat/conversation-panel.tsx | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/resources/js/chat/add-channel-button.tsx b/resources/js/chat/add-channel-button.tsx index e51c6ffec80..b5b916f62a1 100644 --- a/resources/js/chat/add-channel-button.tsx +++ b/resources/js/chat/add-channel-button.tsx @@ -16,7 +16,7 @@ export default class AddChannelButton extends React.Component { render() { if (this.props.type === 'create' && !core.dataStore.chatState.canChatAnnounce) return null; - const modifiers = { selected: core.dataStore.chatState.selectedChannelId === this.props.type }; + const modifiers = { selected: core.dataStore.chatState.selectedView === this.props.type }; return (
diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 173f5d4d53d..0d4a8e603de 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -69,12 +69,12 @@ export default class ChatStateStore implements DispatchListener { return typeof this.selected === 'number' ? this.channelStore.get(this.selected) : null; } - get selectedChannelId() { - return this.selected; - } - - get showingNonChannel() { - return typeof this.selected !== 'number'; + // TODO: better name + @computed + get selectedView() { + return typeof this.selected === 'number' + ? this.channelStore.get(this.selected) + : this.selected; } @computed @@ -297,10 +297,10 @@ export default class ChatStateStore implements DispatchListener { * Keeps the current channel in focus, unless deleted, then focus on next channel. */ private refocusSelectedChannel() { - if (this.showingNonChannel) return; + if (typeof this.selectedView === 'string') return; - if (this.selectedChannel != null) { - this.selectChannel(this.selectedChannel.channelId); + if (this.selectedView != null) { + this.selectChannel(this.selectedView.channelId); } else { this.focusChannelAtIndex(this.selectedIndex); } diff --git a/resources/js/chat/conversation-panel.tsx b/resources/js/chat/conversation-panel.tsx index 56392622c1c..5ddd7382966 100644 --- a/resources/js/chat/conversation-panel.tsx +++ b/resources/js/chat/conversation-panel.tsx @@ -23,17 +23,17 @@ export default class ConversationPanel extends React.Component; } - if (chatState.selectedChannelId === 'join') { + if (selected === 'join') { return ; } - if (chatState.selectedChannel != null) { + if (selected != null) { return ; } From 711803f9afc2a4036419cdad8c0b4bf3f1b86804 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Thu, 28 Dec 2023 16:35:24 +0900 Subject: [PATCH 18/69] export valid ChannelId type --- resources/js/chat/add-channel-button.tsx | 3 ++- resources/js/chat/channel-id.ts | 7 +++++++ resources/js/chat/chat-state-store.ts | 3 +-- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 resources/js/chat/channel-id.ts diff --git a/resources/js/chat/add-channel-button.tsx b/resources/js/chat/add-channel-button.tsx index b5b916f62a1..729b027cff8 100644 --- a/resources/js/chat/add-channel-button.tsx +++ b/resources/js/chat/add-channel-button.tsx @@ -6,9 +6,10 @@ import core from 'osu-core-singleton'; import * as React from 'react'; import { classWithModifiers } from 'utils/css'; import { trans } from 'utils/lang'; +import { AddChannelType } from './channel-id'; interface Props { - type: 'create' | 'join'; + type: AddChannelType; } @observer diff --git a/resources/js/chat/channel-id.ts b/resources/js/chat/channel-id.ts new file mode 100644 index 00000000000..aba142af46d --- /dev/null +++ b/resources/js/chat/channel-id.ts @@ -0,0 +1,7 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +export type AddChannelType = 'create' | 'join'; +type ChannelId = number | AddChannelType | null; + +export default ChannelId; diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 0d4a8e603de..fb15bf913de 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -19,14 +19,13 @@ import { isJqXHR, onError } from 'utils/ajax'; import { trans } from 'utils/lang'; import { hideLoadingOverlay, showImmediateLoadingOverlay } from 'utils/loading-overlay'; import { updateQueryString } from 'utils/url'; +import ChannelId from './channel-id'; import ChannelJoinEvent from './channel-join-event'; import ChannelPartEvent from './channel-part-event'; import { createAnnouncement, getUpdates, joinChannel } from './chat-api'; import MainView from './main-view'; import PingService from './ping-service'; -type ChannelId = number | 'create' | 'join' | null; - @dispatchListener export default class ChatStateStore implements DispatchListener { @observable canChatAnnounce = false; From 5e2e82fc28018285c2a50e01b4679e0009191c1f Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 22 Jan 2024 15:57:58 +0900 Subject: [PATCH 19/69] overflow scroll channel list --- resources/css/bem/chat-join-channel.less | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 625afc5424f..5d689ecf5d3 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -4,6 +4,7 @@ .chat-join-channel { flex: 1; background: hsl(var(--hsl-b4)); + overflow-y: auto; &__channel { display: contents; From 8fc14377eb08d2f4dd906de3fdb7f784fe17a846 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 22 Jan 2024 16:30:24 +0900 Subject: [PATCH 20/69] add note for -webkit-overflow-scrolling --- resources/css/bem/chat-conversation-list.less | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/css/bem/chat-conversation-list.less b/resources/css/bem/chat-conversation-list.less index 0abfb599bcd..73c3c31700f 100644 --- a/resources/css/bem/chat-conversation-list.less +++ b/resources/css/bem/chat-conversation-list.less @@ -4,6 +4,7 @@ .chat-conversation-list { width: 270px; overflow-y: auto; + // not needed for inertial scrolling but draws the scrollbar in front of the icons when scrolling on mobile safari. -webkit-overflow-scrolling: touch; @media @desktop { From 90f9dc83661b00caa4fc63424a1026c03ba914e5 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 20 Feb 2024 21:02:56 +0900 Subject: [PATCH 21/69] no block element in button --- resources/js/chat/join-channels.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index 3fa860ac003..f381369ae68 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -36,9 +36,9 @@ function Channel({ channel, onClick, status }: ChannelProps) { return ( ); } From df989cc92a57115d670c8c89cba86a9d21a2e3f1 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 20 Feb 2024 21:04:40 +0900 Subject: [PATCH 22/69] also remove block level elements from this button --- resources/js/chat/add-channel-button.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/js/chat/add-channel-button.tsx b/resources/js/chat/add-channel-button.tsx index 729b027cff8..f7e871a384a 100644 --- a/resources/js/chat/add-channel-button.tsx +++ b/resources/js/chat/add-channel-button.tsx @@ -22,12 +22,12 @@ export default class AddChannelButton extends React.Component { return (
); From 6f4d423cc83553e0f00f7ebe0e0081a6fe291008 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 20 Feb 2024 21:05:30 +0900 Subject: [PATCH 23/69] key got moved to parent --- resources/js/chat/join-channels.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index f381369ae68..40e245803f4 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -35,7 +35,7 @@ function Channel({ channel, onClick, status }: ChannelProps) { } return ( - +
+ {this.isLoading ? (
{this.renderLoading()}
) : (
- {this.channels.map(this.renderChannel)} + {this.channels?.map(this.renderChannel)}
)}
@@ -82,7 +91,7 @@ export default class JoinChannels extends React.Component { core.dataStore.chatState.addChannel(channelId); }; - private readonly handleRetryClick = () => { + private readonly handleRefreshClick = () => { core.dataStore.chatState.loadPublicChannelList(); }; @@ -109,8 +118,8 @@ export default class JoinChannels extends React.Component { return ( <>

{trans('errors.load_failed')}

- ); diff --git a/resources/lang/en/common.php b/resources/lang/en/common.php index 95d0f86e30a..5bb26369a5a 100644 --- a/resources/lang/en/common.php +++ b/resources/lang/en/common.php @@ -39,6 +39,7 @@ 'pin' => 'pin', 'post' => 'Post', 'read_more' => 'read more', + 'refresh' => 'Refresh', 'reply' => 'Reply', 'reply_reopen' => 'Reply and Reopen', 'reply_resolve' => 'Reply and Resolve', From ce3775e6468829f4862ebd1a159b59976ff442d9 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 26 Feb 2024 22:41:34 +0900 Subject: [PATCH 28/69] separate public channel handling into class --- resources/js/chat/chat-state-store.ts | 35 ++++------------------- resources/js/chat/join-channels.tsx | 16 +++++++---- resources/js/chat/public-channels.ts | 41 +++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 35 deletions(-) create mode 100644 resources/js/chat/public-channels.ts diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 779cd502c7e..51e7f97ec0b 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -8,7 +8,7 @@ import SocketMessageSendAction from 'actions/socket-message-send-action'; import SocketStateChangedAction from 'actions/socket-state-changed-action'; import { dispatch, dispatchListener } from 'app-dispatcher'; import DispatchListener from 'dispatch-listener'; -import ChannelJson, { supportedChannelTypes } from 'interfaces/chat/channel-json'; +import { supportedChannelTypes } from 'interfaces/chat/channel-json'; import { clamp, maxBy } from 'lodash'; import { action, autorun, computed, makeObservable, observable, observe, runInAction } from 'mobx'; import Channel from 'models/chat/channel'; @@ -22,18 +22,17 @@ import { updateQueryString } from 'utils/url'; import ChannelId from './channel-id'; import ChannelJoinEvent from './channel-join-event'; import ChannelPartEvent from './channel-part-event'; -import { createAnnouncement, getPublicChannels, getUpdates, joinChannel } from './chat-api'; +import { createAnnouncement, getUpdates, joinChannel } from './chat-api'; import MainView from './main-view'; import PingService from './ping-service'; +import PublicChannels from './public-channels'; @dispatchListener export default class ChatStateStore implements DispatchListener { @observable canChatAnnounce = false; @observable createAnnouncement = new CreateAnnouncement(); @observable isReady = false; - @observable publicChannels?: ChannelJson[]; - @observable publicChannelsXhr: ReturnType | null= null; - @observable publicChannelsXhrError = false; + readonly publicChannels = new PublicChannels(); skipRefresh = false; @observable viewsMounted = new Set(); @observable private isConnected = false; @@ -134,8 +133,8 @@ export default class ChatStateStore implements DispatchListener { }); autorun(() => { - if (this.selected === 'join' && this.publicChannels == null && !this.publicChannelsXhrError) { - this.loadPublicChannelList(); + if (this.selected === 'join' && this.publicChannels.channels == null && !this.publicChannels.error) { + this.publicChannels.load(); } }); } @@ -186,28 +185,6 @@ export default class ChatStateStore implements DispatchListener { } } - @action - async loadPublicChannelList() { - if (this.publicChannelsXhr != null) return; - - try { - this.publicChannelsXhr = getPublicChannels(); - const channels = await this.publicChannelsXhr; - runInAction(() => { - this.publicChannels = channels; - }); - } catch (error) { - if (!isJqXHR(error)) throw error; - runInAction(() => { - this.publicChannelsXhrError = true; - }); - } finally { - runInAction(() => { - this.publicChannelsXhr = null; - }); - } - } - @action selectChannel(channelId: ChannelId, mode: 'advanceHistory' | 'replaceHistory' | null = 'advanceHistory') { // Mark the channel being switched away from as read. diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index 7f3c847304a..cb0df7f596a 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -44,15 +44,15 @@ function Channel({ channel, onClick, status }: ChannelProps) { @observer export default class JoinChannels extends React.Component { get channels() { - return core.dataStore.chatState.publicChannels; + return core.dataStore.chatState.publicChannels.channels; } get isLoading() { - return core.dataStore.chatState.publicChannelsXhr != null; + return core.dataStore.chatState.publicChannels.xhr != null; } get error() { - return core.dataStore.chatState.publicChannelsXhrError; + return core.dataStore.chatState.publicChannels.error; } @computed @@ -60,6 +60,10 @@ export default class JoinChannels extends React.Component { return new Set(core.dataStore.channelStore.groupedChannels.PUBLIC.map((channel) => channel.channelId)); } + private get buttonModifiers() { + return classWithModifiers('btn-osu-big', 'rounded-thin', { disabled: this.isLoading }); + } + constructor(props: Props) { super(props); @@ -70,7 +74,7 @@ export default class JoinChannels extends React.Component { return (
-
@@ -92,7 +96,7 @@ export default class JoinChannels extends React.Component { }; private readonly handleRefreshClick = () => { - core.dataStore.chatState.loadPublicChannelList(); + core.dataStore.chatState.publicChannels.load(); }; private readonly renderChannel = (channel: ChannelJson) => { @@ -118,7 +122,7 @@ export default class JoinChannels extends React.Component { return ( <>

{trans('errors.load_failed')}

- diff --git a/resources/js/chat/public-channels.ts b/resources/js/chat/public-channels.ts new file mode 100644 index 00000000000..a72bdd3224d --- /dev/null +++ b/resources/js/chat/public-channels.ts @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +import { getPublicChannels } from 'chat/chat-api'; +import ChannelJson from 'interfaces/chat/channel-json'; +import { action, makeObservable, observable, runInAction } from 'mobx'; +import { isJqXHR } from 'utils/ajax'; + +export default class PublicChannels { + @observable channels?: ChannelJson[]; + @observable error = false; + @observable xhr: ReturnType | null= null; + + constructor() { + makeObservable(this); + } + + @action + async load() { + if (this.xhr != null) return; + + try { + this.xhr = getPublicChannels(); + const channels = await this.xhr; + + runInAction(() => { + this.channels = channels; + }); + } catch (error) { + if (!isJqXHR(error)) throw error; + + runInAction(() => { + this.error = true; + }); + } finally { + runInAction(() => { + this.xhr = null; + }); + } + } +} From 33b76af90270ab401d731d3812627812f0132c90 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 26 Feb 2024 22:42:01 +0900 Subject: [PATCH 29/69] don't try to channel you're already in --- resources/js/chat/chat-state-store.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 51e7f97ec0b..aa93113d1d2 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -144,7 +144,9 @@ export default class ChatStateStore implements DispatchListener { // TODO: allow user cancelling operation from UI? @action async addChannel(channelId?: number) { - if (this.isAddingChannel) return; + if (this.isAddingChannel + || channelId != null && this.channelStore.channels.has(channelId) + ) return; showImmediateLoadingOverlay(); From 9434750819b23d8fb9ccc6b0ac514d89deca41b7 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 26 Feb 2024 23:15:09 +0900 Subject: [PATCH 30/69] throttle refresh and sort channel names --- resources/css/bem/chat-join-channel.less | 4 +++- resources/js/chat/join-channels.tsx | 17 +++++++++-------- resources/js/chat/public-channels.ts | 15 ++++++++++++++- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 4cd3f411183..3155483a981 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -42,6 +42,8 @@ } &__toolbar { - + display: flex; + justify-content: flex-end; + padding: 10px; } } diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index cb0df7f596a..a2c644a52a7 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -43,16 +43,17 @@ function Channel({ channel, onClick, status }: ChannelProps) { @observer export default class JoinChannels extends React.Component { + @computed get channels() { - return core.dataStore.chatState.publicChannels.channels; + return this.publicChannels.channels?.slice().sort((x, y) => x.name.localeCompare(y.name)) ?? []; } - get isLoading() { - return core.dataStore.chatState.publicChannels.xhr != null; + get publicChannels() { + return core.dataStore.chatState.publicChannels; } - get error() { - return core.dataStore.chatState.publicChannels.error; + get isLoading() { + return this.publicChannels.xhr != null; } @computed @@ -84,7 +85,7 @@ export default class JoinChannels extends React.Component {
) : (
- {this.channels?.map(this.renderChannel)} + {this.channels.map(this.renderChannel)}
)}
@@ -96,7 +97,7 @@ export default class JoinChannels extends React.Component { }; private readonly handleRefreshClick = () => { - core.dataStore.chatState.publicChannels.load(); + this.publicChannels.load(); }; private readonly renderChannel = (channel: ChannelJson) => { @@ -118,7 +119,7 @@ export default class JoinChannels extends React.Component { }; private renderLoading() { - if (this.error) { + if (this.publicChannels.error) { return ( <>

{trans('errors.load_failed')}

diff --git a/resources/js/chat/public-channels.ts b/resources/js/chat/public-channels.ts index a72bdd3224d..8f9a7bdbc4b 100644 --- a/resources/js/chat/public-channels.ts +++ b/resources/js/chat/public-channels.ts @@ -6,24 +6,37 @@ import ChannelJson from 'interfaces/chat/channel-json'; import { action, makeObservable, observable, runInAction } from 'mobx'; import { isJqXHR } from 'utils/ajax'; +const refreshDelayMs = 30_000; + export default class PublicChannels { @observable channels?: ChannelJson[]; @observable error = false; @observable xhr: ReturnType | null= null; + private lastRefreshTime?: Date; + + private get canRefresh() { + if (this.lastRefreshTime == null) { + return true; + } + + return new Date().getTime() - this.lastRefreshTime.getTime() > refreshDelayMs; + } + constructor() { makeObservable(this); } @action async load() { - if (this.xhr != null) return; + if (this.xhr != null || !this.canRefresh) return; try { this.xhr = getPublicChannels(); const channels = await this.xhr; runInAction(() => { + this.lastRefreshTime = new Date(); this.channels = channels; }); } catch (error) { From 274156a645fe6e4ca9c26ea0a3be9620f9b911c4 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 27 Feb 2024 14:10:21 +0900 Subject: [PATCH 31/69] slice doesn't care about bounds --- resources/js/entrypoints/chat.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/js/entrypoints/chat.tsx b/resources/js/entrypoints/chat.tsx index 96dc9833717..dcd6fdd6bb9 100644 --- a/resources/js/entrypoints/chat.tsx +++ b/resources/js/entrypoints/chat.tsx @@ -77,8 +77,7 @@ core.reactTurbolinks.register('chat', action(() => { core.dataStore.channelStore.lastReceivedMessageId = initial.last_message_id ?? 0; } - const urlHash = currentUrl().hash; - const channelId = urlHash.startsWith('#') ? urlHash.slice(1) : null; + const channelId = currentUrl().hash.slice(1); if (channelId === 'create' || channelId === 'join') { core.dataStore.chatState.selectChannel(channelId, 'replaceHistory'); From e26379c25c5e122f60a59b3045c24442e8f61809 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 4 Mar 2024 19:33:41 +0900 Subject: [PATCH 32/69] don't really need computed for simple comparisons (and pass-throughs, I guess) --- resources/js/chat/chat-state-store.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index aa93113d1d2..33826f9e110 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -43,18 +43,15 @@ export default class ChatStateStore implements DispatchListener { private selectedIndex = 0; @observable private waitAddChannelId: string | number | null = null; - @computed get isChatMounted() { return this.viewsMounted.size > 0; } // Should not join/create another channel if still waiting for a pending request. - @computed get isAddingChannel() { return this.waitAddChannelId != null; } - @computed get joiningChannelId() { return typeof this.waitAddChannelId === 'number' ? this.waitAddChannelId @@ -66,13 +63,11 @@ export default class ChatStateStore implements DispatchListener { return new Set(this.channelStore.groupedChannels.PUBLIC.map((channel) => channel.channelId)); } - @computed get selectedChannel() { return typeof this.selected === 'number' ? this.channelStore.get(this.selected) : null; } // TODO: better name - @computed get selectedView() { return typeof this.selected === 'number' ? this.channelStore.get(this.selected) From 14226b36019134b65dd6c8766dfc2acebe927925 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 4 Mar 2024 19:39:36 +0900 Subject: [PATCH 33/69] don't need that single use typecheck wrapper --- resources/js/chat/chat-state-store.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 33826f9e110..b5bcd615ba9 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -79,13 +79,6 @@ export default class ChatStateStore implements DispatchListener { return supportedChannelTypes.flatMap((type) => this.channelStore.groupedChannels[type]); } - private get waitAddChannelType() { - if (this.waitAddChannelId == null) return null; - return typeof this.waitAddChannelId === 'string' - ? 'announce' - : 'existing'; - } - constructor(protected channelStore: ChannelStore) { this.pingService = new PingService(channelStore); @@ -254,8 +247,8 @@ export default class ChatStateStore implements DispatchListener { const json = event.json; this.channelStore.update(json); - if (this.waitAddChannelType === 'announce' && this.waitAddChannelId === json.uuid - || this.waitAddChannelType === 'existing' && this.waitAddChannelId === json.channel_id + if (typeof this.waitAddChannelId === 'string' && this.waitAddChannelId === json.uuid + || typeof this.waitAddChannelId === 'number' && this.waitAddChannelId === json.channel_id ) { // hide overlay before changing channel if we're waiting for a change to remove it from history navigation. hideLoadingOverlay(); From c8d0fde11dca043f0859558189836414cb2f3ac7 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 4 Mar 2024 22:21:43 +0900 Subject: [PATCH 34/69] remove refresh; also use done/fail/always for load. Not sure why was using try-catch await before, might have been incorrect typing on the error? --- resources/css/bem/chat-join-channel.less | 9 +----- resources/js/chat/join-channels.tsx | 5 --- resources/js/chat/public-channels.ts | 40 ++++++------------------ 3 files changed, 10 insertions(+), 44 deletions(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 3155483a981..e54e55c6b84 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -36,14 +36,7 @@ &__loading { .center-content(); - display: flex; flex-direction: column; - height: 100%; - } - - &__toolbar { - display: flex; - justify-content: flex-end; - padding: 10px; + flex: 1; } } diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index a2c644a52a7..7d7428057f3 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -74,11 +74,6 @@ export default class JoinChannels extends React.Component { render() { return (
-
- -
{this.isLoading ? (
{this.renderLoading()} diff --git a/resources/js/chat/public-channels.ts b/resources/js/chat/public-channels.ts index 8f9a7bdbc4b..644ba5a7ced 100644 --- a/resources/js/chat/public-channels.ts +++ b/resources/js/chat/public-channels.ts @@ -4,51 +4,29 @@ import { getPublicChannels } from 'chat/chat-api'; import ChannelJson from 'interfaces/chat/channel-json'; import { action, makeObservable, observable, runInAction } from 'mobx'; -import { isJqXHR } from 'utils/ajax'; - -const refreshDelayMs = 30_000; export default class PublicChannels { @observable channels?: ChannelJson[]; @observable error = false; @observable xhr: ReturnType | null= null; - private lastRefreshTime?: Date; - - private get canRefresh() { - if (this.lastRefreshTime == null) { - return true; - } - - return new Date().getTime() - this.lastRefreshTime.getTime() > refreshDelayMs; - } - constructor() { makeObservable(this); } @action - async load() { - if (this.xhr != null || !this.canRefresh) return; - - try { - this.xhr = getPublicChannels(); - const channels = await this.xhr; + load() { + if (this.xhr != null) return; - runInAction(() => { - this.lastRefreshTime = new Date(); + this.xhr = getPublicChannels() + .done((channels) => runInAction(() => { this.channels = channels; - }); - } catch (error) { - if (!isJqXHR(error)) throw error; - - runInAction(() => { + })) + .fail(action(() => { this.error = true; - }); - } finally { - runInAction(() => { + })) + .always(action(() => { this.xhr = null; - }); - } + })); } } From b4985bf04df88b5c60f186b7bae42880006e7a80 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 5 Mar 2024 18:28:50 +0900 Subject: [PATCH 35/69] initialize lineHeight to NaN. parseFloat also returns NaN instead of undefined --- resources/js/components/textarea-autosize.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/js/components/textarea-autosize.tsx b/resources/js/components/textarea-autosize.tsx index ed2a4ffd6f7..89137061d85 100644 --- a/resources/js/components/textarea-autosize.tsx +++ b/resources/js/components/textarea-autosize.tsx @@ -11,7 +11,7 @@ interface Props extends React.TextareaHTMLAttributes { } interface State { - lineHeight?: number; + lineHeight: number; } export default class TextareaAutosize extends React.PureComponent { @@ -24,14 +24,14 @@ export default class TextareaAutosize extends React.PureComponent private shouldUpdate = true; private get maxHeight() { - return this.props.maxRows != null && this.state.lineHeight != null && Number.isFinite(this.state.lineHeight) + return this.props.maxRows != null && Number.isFinite(this.state.lineHeight) ? this.state.lineHeight * (this.props.maxRows + 1) // additional row to fit maxRows + slight leeway for scrollbar : null; } constructor(props: Props) { super(props); - this.state = {}; + this.state = { lineHeight: NaN }; } componentDidMount() { From bbde82b1d867a89928e39730007c419ef1106e32 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 15:55:24 +0900 Subject: [PATCH 36/69] rename variable --- resources/js/chat/chat-state-store.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index b5bcd615ba9..8b95a52f46a 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -39,8 +39,8 @@ export default class ChatStateStore implements DispatchListener { private lastHistoryId: number | null = null; private readonly pingService: PingService; + private refocusToIndex = 0; // For refocusing to a channel next to the previously selected one when channel is removed from the list. @observable private selected: ChannelId = null; - private selectedIndex = 0; @observable private waitAddChannelId: string | number | null = null; get isChatMounted() { @@ -203,7 +203,7 @@ export default class ChatStateStore implements DispatchListener { return; } - this.selectedIndex = this.channelList.indexOf(channel); + this.refocusToIndex = this.channelList.indexOf(channel); // TODO: should this be here or have something else figure out if channel needs to be loaded? this.channelStore.loadChannel(channelId); @@ -300,7 +300,7 @@ export default class ChatStateStore implements DispatchListener { if (this.selectedView != null) { this.selectChannel(this.selectedView.channelId); } else { - this.focusChannelAtIndex(this.selectedIndex); + this.focusChannelAtIndex(this.refocusToIndex); } } From 6a9aed6db9594d34457e5800f09dc76e84c83c8f Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 15 Mar 2024 17:31:01 +0900 Subject: [PATCH 37/69] Simplify custom hasher driver Also apparently the option for needsRehash is "rounds" not "cost". --- .../{OsuHasher.php => OsuBcryptHasher.php} | 14 ++++++++------ app/Hashing/OsuHashManager.php | 16 ---------------- app/Providers/AppServiceProvider.php | 12 +++--------- config/app.php | 1 - config/hashing.php | 4 ++-- ...OsuHasherTest.php => OsuBcryptHasherTest.php} | 10 +++++----- 6 files changed, 18 insertions(+), 39 deletions(-) rename app/Hashing/{OsuHasher.php => OsuBcryptHasher.php} (91%) delete mode 100644 app/Hashing/OsuHashManager.php rename tests/Hashing/{OsuHasherTest.php => OsuBcryptHasherTest.php} (75%) diff --git a/app/Hashing/OsuHasher.php b/app/Hashing/OsuBcryptHasher.php similarity index 91% rename from app/Hashing/OsuHasher.php rename to app/Hashing/OsuBcryptHasher.php index b91ad0f062d..83c22e40262 100644 --- a/app/Hashing/OsuHasher.php +++ b/app/Hashing/OsuBcryptHasher.php @@ -7,7 +7,7 @@ use Illuminate\Contracts\Hashing\Hasher; -class OsuHasher implements Hasher +class OsuBcryptHasher implements Hasher { /** * The number of rounds to hash, as 2^n. @@ -37,12 +37,10 @@ public function info($hashedValue) */ public function make($value, array $options = []) { - $cost = array_get($options, 'cost', $this->rounds); - // When we originally moved to bcrypt (quite a few years ago), // we had to migrate everything without waiting for every user to // change their passwords, hence the md5 still being there. - $hash = password_hash(md5($value), PASSWORD_BCRYPT, ['cost' => $cost]); + $hash = password_hash(md5($value), PASSWORD_BCRYPT, ['cost' => $this->cost($options)]); // see static::check() return str_replace('$2y$', '$2a$', $hash); @@ -83,9 +81,13 @@ public function check($value, $hashedValue, array $options = []) */ public function needsRehash($hashedValue, array $options = []) { - $cost = array_get($options, 'cost', $this->rounds); $hashedValue = str_replace('$2a$', '$2y$', $hashedValue); - return password_needs_rehash($hashedValue, PASSWORD_BCRYPT, ['cost' => $cost]); + return password_needs_rehash($hashedValue, PASSWORD_BCRYPT, ['cost' => $this->cost($options)]); + } + + protected function cost(array $options): int + { + return $options['rounds'] ?? $this->rounds; } } diff --git a/app/Hashing/OsuHashManager.php b/app/Hashing/OsuHashManager.php deleted file mode 100644 index 1e8a5213ca4..00000000000 --- a/app/Hashing/OsuHashManager.php +++ /dev/null @@ -1,16 +0,0 @@ -. Licensed under the GNU Affero General Public License v3.0. -// See the LICENCE file in the repository root for full licence text. - -namespace App\Hashing; - -use Illuminate\Hashing\HashManager; - -class OsuHashManager extends HashManager -{ - public function createBcryptDriver() - { - return new OsuHasher(); - } -} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index ff2658f3af1..2bf3d0f79d7 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -5,7 +5,7 @@ namespace App\Providers; -use App\Hashing\OsuHashManager; +use App\Hashing\OsuBcryptHasher; use App\Libraries\MorphMap; use App\Libraries\OsuCookieJar; use App\Libraries\OsuMessageSelector; @@ -79,6 +79,8 @@ public function boot() // newest scribe tries to rename {modelName} parameters to {id} // but it kind of doesn't work with our route handlers. Scribe::normalizeEndpointUrlUsing(fn ($url) => $url); + + \Hash::extend('osubcrypt', fn () => new OsuBcryptHasher()); } /** @@ -99,14 +101,6 @@ public function register() $localCacheManager->registerSingleton(app($name)); } - $this->app->singleton('hash', function ($app) { - return new OsuHashManager($app); - }); - - $this->app->singleton('hash.driver', function ($app) { - return $app['hash']->driver(); - }); - $this->app->singleton('cookie', function ($app) { $config = $GLOBALS['cfg']['session']; diff --git a/config/app.php b/config/app.php index 9217d6732b2..811db062d68 100644 --- a/config/app.php +++ b/config/app.php @@ -178,7 +178,6 @@ 'providers' => ServiceProvider::defaultProviders()->except([ Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, ])->merge([ App\Providers\AppServiceProvider::class, diff --git a/config/hashing.php b/config/hashing.php index 842577087c0..997027ef36e 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -11,11 +11,11 @@ | passwords for your application. By default, the bcrypt algorithm is | used; however, you remain free to modify this option if you wish. | - | Supported: "bcrypt", "argon", "argon2id" + | Supported: "bcrypt", "argon", "argon2id", "osubcrypt" | */ - 'driver' => 'bcrypt', + 'driver' => 'osubcrypt', /* |-------------------------------------------------------------------------- diff --git a/tests/Hashing/OsuHasherTest.php b/tests/Hashing/OsuBcryptHasherTest.php similarity index 75% rename from tests/Hashing/OsuHasherTest.php rename to tests/Hashing/OsuBcryptHasherTest.php index 5bdee59d4e9..459dca20eaf 100644 --- a/tests/Hashing/OsuHasherTest.php +++ b/tests/Hashing/OsuBcryptHasherTest.php @@ -5,27 +5,27 @@ namespace Tests\Hashing; -use App\Hashing\OsuHasher; +use App\Hashing\OsuBcryptHasher; use Illuminate\Contracts\Hashing\Hasher; use PHPUnit\Framework\TestCase; -class OsuHasherTest extends TestCase +class OsuBcryptHasherTest extends TestCase { public function testBasicHashing() { - $hasher = new OsuHasher(); + $hasher = new OsuBcryptHasher(); $value = $hasher->make('password'); $this->assertNotSame('password', $value); $this->assertNotSame(md5('password'), $value); $this->assertTrue($hasher->check('password', $value)); $this->assertFalse($hasher->needsRehash($value)); - $this->assertTrue($hasher->needsRehash($value, ['cost' => 4])); + $this->assertTrue($hasher->needsRehash($value, ['rounds' => 4])); } public function testImplementsHasher() { - $hasher = new OsuHasher(); + $hasher = new OsuBcryptHasher(); $this->assertInstanceOf(Hasher::class, $hasher); } } From c1fee4fd4c4dc5e1fb35b83ae4b0c8d88e4248eb Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 17:02:24 +0900 Subject: [PATCH 38/69] always update title bar --- resources/js/chat/chat-state-store.ts | 51 +++++++++++++++------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 8b95a52f46a..e62e0ef0b6c 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -19,7 +19,7 @@ import { isJqXHR, onError } from 'utils/ajax'; import { trans } from 'utils/lang'; import { hideLoadingOverlay, showImmediateLoadingOverlay } from 'utils/loading-overlay'; import { updateQueryString } from 'utils/url'; -import ChannelId from './channel-id'; +import ChannelId, { AddChannelType } from './channel-id'; import ChannelJoinEvent from './channel-join-event'; import ChannelPartEvent from './channel-part-event'; import { createAnnouncement, getUpdates, joinChannel } from './chat-api'; @@ -183,39 +183,21 @@ export default class ChatStateStore implements DispatchListener { this.selectedChannel?.throttledSendMarkAsRead(); this.selected = channelId; - if (channelId == null) return; - if (typeof channelId === 'string') { - if (mode != null) { - Turbolinks.controller[mode](updateQueryString(null, { - channel_id: null, - sendto: null, - }, channelId)); - } - + this.updateUrl(channelId, mode); return; } + if (channelId == null) return; const channel = this.channelStore.get(channelId); - - if (channel == null) { - console.error(`Trying to switch to non-existent channel ${channelId}`); - return; - } + if (channel == null) return; this.refocusToIndex = this.channelList.indexOf(channel); // TODO: should this be here or have something else figure out if channel needs to be loaded? this.channelStore.loadChannel(channelId); - if (mode != null) { - const params = channel.newPmChannel - ? { channel_id: null, sendto: channel.pmTarget?.toString() } - : { channel_id: channel.channelId.toString(), sendto: null }; - - Turbolinks.controller[mode](updateQueryString(null, params, '')); - } - core.browserTitleWithNotificationCount.title = `${channel.name} · ${trans('page_title.main.chat_controller._')}`; + this.updateUrl(channel, mode); } @action @@ -319,4 +301,27 @@ export default class ChatStateStore implements DispatchListener { this.channelStore.updateWithChatUpdates(json); }); } + + private updateUrl(channel: Channel | AddChannelType, mode: 'advanceHistory' | 'replaceHistory' | null) { + if (mode == null) return; + + let channelName: string; + let hash = ''; + let params: Record<'channel_id' | 'sendto', string | null | undefined>; + + if (typeof channel === 'string') { + channelName = trans(`chat.channels.${channel}`); + hash = channel; + params = { channel_id: null, sendto: null }; + } else { + channelName = channel.name; + params = channel.newPmChannel + ? { channel_id: null, sendto: channel.pmTarget?.toString() } + : { channel_id: channel.channelId.toString(), sendto: null }; + + } + + Turbolinks.controller[mode](updateQueryString(null, params, hash)); + core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; + } } From b7e5b13bce199f977c2e9935de04ebf88f915825 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 18:50:17 +0900 Subject: [PATCH 39/69] rename getter to match selectedChannel --- resources/js/chat/add-channel-button.tsx | 2 +- resources/js/chat/chat-state-store.ts | 10 +++++----- resources/js/chat/conversation-panel.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/js/chat/add-channel-button.tsx b/resources/js/chat/add-channel-button.tsx index f7e871a384a..08924846799 100644 --- a/resources/js/chat/add-channel-button.tsx +++ b/resources/js/chat/add-channel-button.tsx @@ -17,7 +17,7 @@ export default class AddChannelButton extends React.Component { render() { if (this.props.type === 'create' && !core.dataStore.chatState.canChatAnnounce) return null; - const modifiers = { selected: core.dataStore.chatState.selectedView === this.props.type }; + const modifiers = { selected: core.dataStore.chatState.selectedChannelOrType === this.props.type }; return (
diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index e62e0ef0b6c..3b568e7c72d 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -67,8 +67,8 @@ export default class ChatStateStore implements DispatchListener { return typeof this.selected === 'number' ? this.channelStore.get(this.selected) : null; } - // TODO: better name - get selectedView() { + // In most cases we want the Channel or create/add channel type, not the channel id itself. + get selectedChannelOrType() { return typeof this.selected === 'number' ? this.channelStore.get(this.selected) : this.selected; @@ -277,10 +277,10 @@ export default class ChatStateStore implements DispatchListener { * Keeps the current channel in focus, unless deleted, then focus on next channel. */ private refocusSelectedChannel() { - if (typeof this.selectedView === 'string') return; + if (typeof this.selectedChannelOrType === 'string') return; - if (this.selectedView != null) { - this.selectChannel(this.selectedView.channelId); + if (this.selectedChannelOrType != null) { + this.selectChannel(this.selectedChannelOrType.channelId); } else { this.focusChannelAtIndex(this.refocusToIndex); } diff --git a/resources/js/chat/conversation-panel.tsx b/resources/js/chat/conversation-panel.tsx index 5ddd7382966..75e44800909 100644 --- a/resources/js/chat/conversation-panel.tsx +++ b/resources/js/chat/conversation-panel.tsx @@ -23,7 +23,7 @@ export default class ConversationPanel extends React.Component; From 21402b381ddf8ea367022f4435a0f59f44079303 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 19:09:12 +0900 Subject: [PATCH 40/69] update title with autorun --- resources/js/chat/chat-state-store.ts | 16 ++++++++++++---- resources/lang/en/chat.php | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 3b568e7c72d..dc141349cfb 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -125,6 +125,18 @@ export default class ChatStateStore implements DispatchListener { this.publicChannels.load(); } }); + + autorun(() => { + if (this.isChatMounted) { + const selectedChannelOrType = this.selectedChannelOrType; + const channelName = selectedChannelOrType instanceof Channel + ? selectedChannelOrType.name + : trans(`chat.channels.${selectedChannelOrType ?? 'none'}`); + + // FIXME: channelName on sendto= + core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; + } + }); } // Only up to one join/create channel operation should be allowed to be running at any time. @@ -305,16 +317,13 @@ export default class ChatStateStore implements DispatchListener { private updateUrl(channel: Channel | AddChannelType, mode: 'advanceHistory' | 'replaceHistory' | null) { if (mode == null) return; - let channelName: string; let hash = ''; let params: Record<'channel_id' | 'sendto', string | null | undefined>; if (typeof channel === 'string') { - channelName = trans(`chat.channels.${channel}`); hash = channel; params = { channel_id: null, sendto: null }; } else { - channelName = channel.name; params = channel.newPmChannel ? { channel_id: null, sendto: channel.pmTarget?.toString() } : { channel_id: channel.channelId.toString(), sendto: null }; @@ -322,6 +331,5 @@ export default class ChatStateStore implements DispatchListener { } Turbolinks.controller[mode](updateQueryString(null, params, hash)); - core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; } } diff --git a/resources/lang/en/chat.php b/resources/lang/en/chat.php index f146161bbd2..110d72f0b51 100644 --- a/resources/lang/en/chat.php +++ b/resources/lang/en/chat.php @@ -19,6 +19,7 @@ 'confirm_part' => 'Do you want to hide this channel? You will still receive messages from this channel.', 'create' => 'create announcement', 'join' => 'join channel', + 'none' => 'no channel', 'list' => [ 'title' => [ From 9c652705fa9b77a9a83225b2a87991d48ed5e789 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 22:12:39 +0900 Subject: [PATCH 41/69] can just call load() --- resources/js/chat/chat-state-store.ts | 7 ++----- resources/js/stores/channel-store.ts | 6 ------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index dc141349cfb..a41acc53c62 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -110,10 +110,7 @@ export default class ChatStateStore implements DispatchListener { } runInAction(() => { - // TODO: use selectChannel? - if (typeof this.selected === 'number') { - this.channelStore.loadChannel(this.selected); - } + this.selectedChannel?.load(); this.isReady = true; }); @@ -207,7 +204,7 @@ export default class ChatStateStore implements DispatchListener { this.refocusToIndex = this.channelList.indexOf(channel); // TODO: should this be here or have something else figure out if channel needs to be loaded? - this.channelStore.loadChannel(channelId); + channel.load(); this.updateUrl(channel, mode); } diff --git a/resources/js/stores/channel-store.ts b/resources/js/stores/channel-store.ts index 65441b7464e..f6e2d228c90 100644 --- a/resources/js/stores/channel-store.ts +++ b/resources/js/stores/channel-store.ts @@ -125,12 +125,6 @@ export default class ChannelStore implements DispatchListener { } } - // TODO: load is async, needs to be reflected somewhere. - @action - loadChannel(channelId: number) { - this.channels.get(channelId)?.load(); - } - @action loadChannelEarlierMessages(channelId: number) { this.get(channelId)?.loadEarlierMessages(); From fa21618dfdddc402520df5ee8a5a396653b4d285 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 22:38:16 +0900 Subject: [PATCH 42/69] also force channel name on first load --- resources/js/chat/chat-state-store.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index a41acc53c62..11e338c4a68 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -130,8 +130,9 @@ export default class ChatStateStore implements DispatchListener { ? selectedChannelOrType.name : trans(`chat.channels.${selectedChannelOrType ?? 'none'}`); - // FIXME: channelName on sendto= - core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; + const newTitle = `${channelName} · ${trans('page_title.main.chat_controller._')}`; + document.title = newTitle; // initial page load resets to document.title. + core.browserTitleWithNotificationCount.title = newTitle; } }); } From 7cd6b5588cb4022df78ab4ca4e68293bfa4f4fe0 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 22:41:54 +0900 Subject: [PATCH 43/69] default params --- resources/js/chat/chat-state-store.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 11e338c4a68..1d51df37d20 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -316,16 +316,16 @@ export default class ChatStateStore implements DispatchListener { if (mode == null) return; let hash = ''; - let params: Record<'channel_id' | 'sendto', string | null | undefined>; + const params: Record<'channel_id' | 'sendto', string | null | undefined> = { channel_id: null, sendto: null }; if (typeof channel === 'string') { hash = channel; - params = { channel_id: null, sendto: null }; } else { - params = channel.newPmChannel - ? { channel_id: null, sendto: channel.pmTarget?.toString() } - : { channel_id: channel.channelId.toString(), sendto: null }; - + if (channel.newPmChannel) { + params.sendto = channel.pmTarget?.toString(); + } else { + params.channel_id = channel.channelId.toString(); + } } Turbolinks.controller[mode](updateQueryString(null, params, hash)); From 922e76c66b7811d0bfdf083ee0acbe1f9531eeb1 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 15 Mar 2024 23:10:08 +0900 Subject: [PATCH 44/69] figure out different way to fix setting title on load instead --- resources/js/chat/chat-state-store.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 1d51df37d20..d13275d81eb 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -123,6 +123,7 @@ export default class ChatStateStore implements DispatchListener { } }); + // This should really go in ConversationPanel expect the whole double mounted thing wuth turbolinks breaks it when navigating. autorun(() => { if (this.isChatMounted) { const selectedChannelOrType = this.selectedChannelOrType; @@ -130,9 +131,7 @@ export default class ChatStateStore implements DispatchListener { ? selectedChannelOrType.name : trans(`chat.channels.${selectedChannelOrType ?? 'none'}`); - const newTitle = `${channelName} · ${trans('page_title.main.chat_controller._')}`; - document.title = newTitle; // initial page load resets to document.title. - core.browserTitleWithNotificationCount.title = newTitle; + core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; } }); } From 1b7d88e7ad438cb29661f407e69ac8c309242c45 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 18 Mar 2024 17:24:49 +0900 Subject: [PATCH 45/69] set/unset title on channel/chat change --- resources/js/chat/chat-state-store.ts | 2 ++ resources/js/core/browser-title-with-notification-count.ts | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index d13275d81eb..084d7eef74c 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -132,6 +132,8 @@ export default class ChatStateStore implements DispatchListener { : trans(`chat.channels.${selectedChannelOrType ?? 'none'}`); core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; + } else { + core.browserTitleWithNotificationCount.title = null; } }); } diff --git a/resources/js/core/browser-title-with-notification-count.ts b/resources/js/core/browser-title-with-notification-count.ts index ec76708b432..f0cf3e2dd5a 100644 --- a/resources/js/core/browser-title-with-notification-count.ts +++ b/resources/js/core/browser-title-with-notification-count.ts @@ -6,12 +6,13 @@ import OsuCore from 'osu-core'; import { formatNumber } from 'utils/html'; export default class BrowserTitleWithNotificationCount { + @observable private customTitle: string | null = null; private disposer?: () => void; @observable private origTitle = ''; - set title(newTitle: string) { + set title(newTitle: string | null) { runInAction(() => { - this.origTitle = `${newTitle} | osu!`; + this.customTitle = newTitle != null ? `${newTitle} | osu!` : newTitle; }); } @@ -37,7 +38,7 @@ export default class BrowserTitleWithNotificationCount { ? '' : `(${formatNumber(count)}) `; - document.title = `${titlePrefix}${this.origTitle}`; + document.title = `${titlePrefix}${this.customTitle ?? this.origTitle}`; }); }; } From 5afb5ab1a67b337fc69c546ee16b1ce88441e9d3 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 18 Mar 2024 18:07:48 +0900 Subject: [PATCH 46/69] don't call core in things constructed via core --- resources/js/chat/chat-state-store.ts | 15 ---------- resources/js/chat/conversation-panel.tsx | 37 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 084d7eef74c..79153f744a7 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -16,7 +16,6 @@ import CreateAnnouncement from 'models/chat/create-announcement'; import core from 'osu-core-singleton'; import ChannelStore from 'stores/channel-store'; import { isJqXHR, onError } from 'utils/ajax'; -import { trans } from 'utils/lang'; import { hideLoadingOverlay, showImmediateLoadingOverlay } from 'utils/loading-overlay'; import { updateQueryString } from 'utils/url'; import ChannelId, { AddChannelType } from './channel-id'; @@ -122,20 +121,6 @@ export default class ChatStateStore implements DispatchListener { this.publicChannels.load(); } }); - - // This should really go in ConversationPanel expect the whole double mounted thing wuth turbolinks breaks it when navigating. - autorun(() => { - if (this.isChatMounted) { - const selectedChannelOrType = this.selectedChannelOrType; - const channelName = selectedChannelOrType instanceof Channel - ? selectedChannelOrType.name - : trans(`chat.channels.${selectedChannelOrType ?? 'none'}`); - - core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; - } else { - core.browserTitleWithNotificationCount.title = null; - } - }); } // Only up to one join/create channel operation should be allowed to be running at any time. diff --git a/resources/js/chat/conversation-panel.tsx b/resources/js/chat/conversation-panel.tsx index 75e44800909..7af88687823 100644 --- a/resources/js/chat/conversation-panel.tsx +++ b/resources/js/chat/conversation-panel.tsx @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. import Img2x from 'components/img2x'; +import { autorun } from 'mobx'; import { observer } from 'mobx-react'; +import Channel from 'models/chat/channel'; import core from 'osu-core-singleton'; import * as React from 'react'; import { trans } from 'utils/lang'; @@ -14,6 +16,41 @@ const lazerLink = 'https://github.com/ppy/osu/releases'; @observer export default class ConversationPanel extends React.Component> { + private readonly disposer: ReturnType; + private navigatingAway = false; + + constructor(props: Record) { + super(props); + + document.addEventListener('turbolinks:before-cache', this.handleBeforeCache); + + this.disposer = autorun(() => { + // Don't set title if this is on the document that is going away. + // before-cache should be running before the autorun. + if (!this.navigatingAway) { + const selectedChannelOrType = core.dataStore.chatState.selectedChannelOrType; + const channelName = selectedChannelOrType instanceof Channel + ? selectedChannelOrType.name + : trans(`chat.channels.${selectedChannelOrType ?? 'none'}`); + + core.browserTitleWithNotificationCount.title = `${channelName} · ${trans('page_title.main.chat_controller._')}`; + } + }); + } + + componentWillUnmount() { + this.disposer(); + document.removeEventListener('turbolinks:before-cache', this.handleBeforeCache); + + if (!core.dataStore.chatState.isChatMounted) { + core.browserTitleWithNotificationCount.title = null; + } + } + + readonly handleBeforeCache = () => { + this.navigatingAway = true; + }; + render() { return (
From 5356eae9bed7314c4aede385aa92d9c42ed82a35 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 19 Mar 2024 17:39:02 +0900 Subject: [PATCH 47/69] switch to channel if already open --- resources/js/chat/chat-state-store.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 79153f744a7..2bef2e11d10 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -128,19 +128,24 @@ export default class ChatStateStore implements DispatchListener { // TODO: allow user cancelling operation from UI? @action async addChannel(channelId?: number) { - if (this.isAddingChannel - || channelId != null && this.channelStore.channels.has(channelId) - ) return; - - showImmediateLoadingOverlay(); + if (this.isAddingChannel) return; try { if (channelId != null) { + if (this.channelStore.channels.has(channelId)) { + this.selectChannel(channelId); + return; + } + + showImmediateLoadingOverlay(); + this.waitAddChannelId = channelId; await joinChannel(channelId, core.currentUserOrFail.id); } else { if (!this.createAnnouncement.isValid) return; + showImmediateLoadingOverlay(); + const json = this.createAnnouncement.toJson(); this.waitAddChannelId = json.uuid; await createAnnouncement(json); @@ -152,9 +157,9 @@ export default class ChatStateStore implements DispatchListener { runInAction(() => { this.waitAddChannelId = null; }); + } finally { + hideLoadingOverlay(); } - - hideLoadingOverlay(); } handleDispatchAction(event: DispatcherAction) { From 718472eb082cbd229772ee039ac8e972f658c838 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 19 Mar 2024 19:02:04 +0900 Subject: [PATCH 48/69] subgrid and highlight background on hover --- resources/css/bem/chat-join-channel.less | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index e54e55c6b84..5b337d7affa 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -9,12 +9,18 @@ background: hsl(var(--hsl-b4)); &__channel { - display: contents; + .reset-input(); + + display: grid; + grid-template-columns: subgrid; + grid-column: span 3; color: hsl(var(--hsl-l3)); text-align: left; + padding: 5px 10px; .link-hover({ color: hsl(var(--hsl-c1)); + background-color: hsl(var(--hsl-b3)); }); &--joined { @@ -24,8 +30,7 @@ &__channels { display: grid; - gap: 10px; - padding: 10px; + column-gap: 10px; // checkmark is 14px wide grid-template-columns: minmax(14px, max-content) 200px auto; From 5c9121aa99ec909142d8a6078e4dd38cdd6d4d72 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 19 Mar 2024 19:09:12 +0900 Subject: [PATCH 49/69] should be fine since public channels don't really change? :thinking: --- resources/css/bem/chat-join-channel.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 5b337d7affa..61dcdfb293c 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -33,7 +33,7 @@ column-gap: 10px; // checkmark is 14px wide - grid-template-columns: minmax(14px, max-content) 200px auto; + grid-template-columns: minmax(14px, max-content) auto 1fr; overflow-wrap: anywhere; overflow-y: auto; From 2478869d0a81b517fdfa5bbfe9d93378f203459b Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 20 Mar 2024 00:32:14 +0900 Subject: [PATCH 50/69] finally not a great place to hideLoadingOverlay; reset waiting for channel on navigation, and also before throwing. --- resources/js/chat/chat-state-store.ts | 25 +++++++++++++++---------- resources/js/chat/main-view.tsx | 12 ++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/resources/js/chat/chat-state-store.ts b/resources/js/chat/chat-state-store.ts index 2bef2e11d10..8940fa8e9d3 100644 --- a/resources/js/chat/chat-state-store.ts +++ b/resources/js/chat/chat-state-store.ts @@ -16,7 +16,7 @@ import CreateAnnouncement from 'models/chat/create-announcement'; import core from 'osu-core-singleton'; import ChannelStore from 'stores/channel-store'; import { isJqXHR, onError } from 'utils/ajax'; -import { hideLoadingOverlay, showImmediateLoadingOverlay } from 'utils/loading-overlay'; +import { hideLoadingOverlay } from 'utils/loading-overlay'; import { updateQueryString } from 'utils/url'; import ChannelId, { AddChannelType } from './channel-id'; import ChannelJoinEvent from './channel-join-event'; @@ -83,6 +83,8 @@ export default class ChatStateStore implements DispatchListener { makeObservable(this); + document.addEventListener('turbolinks:before-cache', this.handleBeforeCache); + observe(channelStore.channels, (changes) => { // refocus channels if any gets removed if (changes.type === 'delete') { @@ -137,28 +139,22 @@ export default class ChatStateStore implements DispatchListener { return; } - showImmediateLoadingOverlay(); - this.waitAddChannelId = channelId; await joinChannel(channelId, core.currentUserOrFail.id); } else { if (!this.createAnnouncement.isValid) return; - showImmediateLoadingOverlay(); - const json = this.createAnnouncement.toJson(); this.waitAddChannelId = json.uuid; await createAnnouncement(json); } } catch (error) { - if (!isJqXHR(error)) throw error; - - onError(error); runInAction(() => { this.waitAddChannelId = null; }); - } finally { - hideLoadingOverlay(); + + if (!isJqXHR(error)) throw error; + onError(error); } } @@ -178,6 +174,7 @@ export default class ChatStateStore implements DispatchListener { @action selectChannel(channelId: ChannelId, mode: 'advanceHistory' | 'replaceHistory' | null = 'advanceHistory') { + this.waitAddChannelId = null; // reset any waiting for channel. // Mark the channel being switched away from as read. // Marking as read is done here to avoid constantly sending mark-as-read requests // while receiving messages when autoScroll is enabled on the channel. @@ -225,6 +222,14 @@ export default class ChatStateStore implements DispatchListener { this.selectChannel(channel.channelId); } + @action + private readonly handleBeforeCache = () => { + // anything that needs to be reset when navigating away. + // TODO: split the wait for channel/busy and xhr, or maybe use a placeholder channel instead + // of blocking the UI. + this.waitAddChannelId = null; + }; + @action private handleChatChannelJoinEvent(event: ChannelJoinEvent) { const json = event.json; diff --git a/resources/js/chat/main-view.tsx b/resources/js/chat/main-view.tsx index e43cc4d9e50..c53589b4f57 100644 --- a/resources/js/chat/main-view.tsx +++ b/resources/js/chat/main-view.tsx @@ -6,6 +6,7 @@ import { action, autorun, makeObservable, runInAction } from 'mobx'; import { disposeOnUnmount, observer } from 'mobx-react'; import core from 'osu-core-singleton'; import * as React from 'react'; +import { hideLoadingOverlay, showImmediateLoadingOverlay } from 'utils/loading-overlay'; import ConversationList from './conversation-list'; import ConversationPanel from './conversation-panel'; @@ -28,6 +29,17 @@ export default class MainView extends React.Component> { } }), ); + + disposeOnUnmount( + this, + autorun(() => { + if (core.dataStore.chatState.isAddingChannel) { + showImmediateLoadingOverlay(); + } else { + hideLoadingOverlay(); + } + }), + ); } @action From a003603364ed2c534211c424d3da2e3928c83448 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 25 Mar 2024 21:12:13 +0900 Subject: [PATCH 51/69] some how keep forgetting this --- resources/js/chat/conversation-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/chat/conversation-list.tsx b/resources/js/chat/conversation-list.tsx index 0b759887784..d2a8db7a0c8 100644 --- a/resources/js/chat/conversation-list.tsx +++ b/resources/js/chat/conversation-list.tsx @@ -19,7 +19,7 @@ const icons: Record = { function renderChannels(type: SupportedChannelType) { const channels = core.dataStore.channelStore.groupedChannels[type]; - if (channels.length > 0 || type ==='PUBLIC' ||type === 'ANNOUNCE' && core.dataStore.chatState.canChatAnnounce) { + if (channels.length > 0 || type === 'PUBLIC' ||type === 'ANNOUNCE' && core.dataStore.chatState.canChatAnnounce) { const title = trans(`chat.channels.list.title.${type}`); // Optimization so that the channel list can be rendered as several smaller layers. From b59ddbe7547d8e0fb48a5a3b4cfcfc02df023593 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Mon, 25 Mar 2024 23:58:03 +0900 Subject: [PATCH 52/69] use anchor instead --- resources/css/bem/chat-join-channel.less | 1 + resources/js/chat/join-channels.tsx | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/css/bem/chat-join-channel.less b/resources/css/bem/chat-join-channel.less index 61dcdfb293c..4e2bfce126f 100644 --- a/resources/css/bem/chat-join-channel.less +++ b/resources/css/bem/chat-join-channel.less @@ -10,6 +10,7 @@ &__channel { .reset-input(); + .link-plain(); display: grid; grid-template-columns: subgrid; diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index 7d7428057f3..e734e567033 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -33,11 +33,12 @@ function Channel({ channel, onClick, status }: ChannelProps) { } return ( - + ); } From 3c998d59771b2eddf7e23ade63bd95c6dd65f162 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Tue, 26 Mar 2024 00:01:14 +0900 Subject: [PATCH 53/69] also another space --- resources/js/chat/conversation-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/chat/conversation-list.tsx b/resources/js/chat/conversation-list.tsx index d2a8db7a0c8..4b5bd0cb5dc 100644 --- a/resources/js/chat/conversation-list.tsx +++ b/resources/js/chat/conversation-list.tsx @@ -19,7 +19,7 @@ const icons: Record = { function renderChannels(type: SupportedChannelType) { const channels = core.dataStore.channelStore.groupedChannels[type]; - if (channels.length > 0 || type === 'PUBLIC' ||type === 'ANNOUNCE' && core.dataStore.chatState.canChatAnnounce) { + if (channels.length > 0 || type === 'PUBLIC' || type === 'ANNOUNCE' && core.dataStore.chatState.canChatAnnounce) { const title = trans(`chat.channels.list.title.${type}`); // Optimization so that the channel list can be rendered as several smaller layers. From 4295ac9db1913d6c0384e03e9fdeb950b772ce15 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 26 Mar 2024 10:39:03 +0900 Subject: [PATCH 54/69] Skip middlewares for media url --- app/Http/Controllers/BeatmapDiscussionsController.php | 2 +- routes/web.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/BeatmapDiscussionsController.php b/app/Http/Controllers/BeatmapDiscussionsController.php index 502dd0df411..9fa7e02ea6c 100644 --- a/app/Http/Controllers/BeatmapDiscussionsController.php +++ b/app/Http/Controllers/BeatmapDiscussionsController.php @@ -120,7 +120,7 @@ public function index() public function mediaUrl() { - $url = get_string(request('url')); + $url = presence(get_string(request('url'))) ?? abort(422); // Tell browser not to request url for a while. return redirect(proxy_media($url))->header('Cache-Control', 'max-age=600'); diff --git a/routes/web.php b/routes/web.php index c76d39ec6a8..d9924add284 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,6 +5,9 @@ use App\Http\Middleware\ThrottleRequests; +Route::get('wiki/images/{path}', 'WikiController@image')->name('wiki.image')->where('path', '.+'); +Route::get('beatmapsets/discussions/media-url', 'BeatmapDiscussionsController@mediaUrl')->name('beatmapsets.discussions.media-url'); + Route::group(['middleware' => ['web']], function () { Route::group(['as' => 'admin.', 'prefix' => 'admin', 'namespace' => 'Admin'], function () { Route::get('/beatmapsets/{beatmapset}/covers', 'BeatmapsetsController@covers')->name('beatmapsets.covers'); @@ -70,7 +73,6 @@ Route::resource('watches', 'BeatmapsetWatchesController', ['only' => ['update', 'destroy']]); Route::group(['prefix' => 'discussions', 'as' => 'discussions.'], function () { - Route::get('media-url', 'BeatmapDiscussionsController@mediaUrl')->name('media-url'); Route::put('{discussion}/vote', 'BeatmapDiscussionsController@vote')->name('vote'); Route::post('{discussion}/restore', 'BeatmapDiscussionsController@restore')->name('restore'); Route::post('{discussion}/deny-kudosu', 'BeatmapDiscussionsController@denyKudosu')->name('deny-kudosu'); @@ -322,7 +324,6 @@ Route::resource('users', 'UsersController', ['only' => ['store']]); Route::get('wiki/{locale}/Sitemap', 'WikiController@sitemap')->name('wiki.sitemap'); - Route::get('wiki/images/{path}', 'WikiController@image')->name('wiki.image')->where('path', '.+'); Route::get('wiki/{locale?}/{path?}', 'WikiController@show')->name('wiki.show')->where('path', '.+'); Route::put('wiki/{locale}/{path}', 'WikiController@update')->where('path', '.+'); Route::get('wiki-suggestions', 'WikiController@suggestions')->name('wiki-suggestions'); From 2ee3c26075b5e5eed759a22640534cb2713cd623 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 26 Mar 2024 11:02:32 +0900 Subject: [PATCH 55/69] Simplify wiki image path handling --- app/Http/Controllers/WikiController.php | 10 +++++----- app/Http/Kernel.php | 1 - app/Http/Middleware/StripCookies.php | 23 ----------------------- app/Models/Wiki/Image.php | 17 ----------------- 4 files changed, 5 insertions(+), 46 deletions(-) delete mode 100644 app/Http/Middleware/StripCookies.php diff --git a/app/Http/Controllers/WikiController.php b/app/Http/Controllers/WikiController.php index 78368135ae4..adff7890633 100644 --- a/app/Http/Controllers/WikiController.php +++ b/app/Http/Controllers/WikiController.php @@ -123,16 +123,16 @@ public function image($path) return response('Invalid file format', 422); } - $image = Wiki\Image::lookupForController($path, Request::url(), Request::header('referer')); - - request()->attributes->set('strip_cookies', true); + $image = (new Wiki\Image($path))->sync(); if (!$image->isVisible()) { return response('Not found', 404); } - return response($image->get()['content'], 200) - ->header('Content-Type', $image->get()['type']) + $imageData = $image->get(); + + return response($imageData['content'], 200) + ->header('Content-Type', $imageData['type']) // 10 years max-age ->header('Cache-Control', 'max-age=315360000, public'); } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 12cb86edc9a..ecbdf0ca68f 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -27,7 +27,6 @@ class Kernel extends HttpKernel Middleware\VerifyUserAlways::class, ], 'web' => [ - Middleware\StripCookies::class, Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, diff --git a/app/Http/Middleware/StripCookies.php b/app/Http/Middleware/StripCookies.php deleted file mode 100644 index 05168a701ab..00000000000 --- a/app/Http/Middleware/StripCookies.php +++ /dev/null @@ -1,23 +0,0 @@ -. Licensed under the GNU Affero General Public License v3.0. -// See the LICENCE file in the repository root for full licence text. - -namespace App\Http\Middleware; - -use Closure; - -class StripCookies -{ - public function handle($request, Closure $next) - { - $result = $next($request); - - if ($request->attributes->get('strip_cookies')) { - // strip all cookies from response - $result->headers->remove('set-cookie'); - } - - return $result; - } -} diff --git a/app/Models/Wiki/Image.php b/app/Models/Wiki/Image.php index c9b6acf1a71..e78fb70857d 100644 --- a/app/Models/Wiki/Image.php +++ b/app/Models/Wiki/Image.php @@ -18,23 +18,6 @@ class Image implements WikiObject private $cache; - public static function lookupForController($path, $url = null, $referrer = null) - { - $url = presence($url); - $referrer = presence($referrer); - $image = (new static($path))->sync(); - - if (!$image->isVisible()) { - if ($url !== null && $referrer !== null && starts_with($url, $referrer)) { - $newPath = 'shared/'.substr($url, strlen($referrer)); - - return (new static($newPath))->sync(); - } - } - - return $image; - } - public function __construct($path) { $this->path = OsuWiki::cleanPath($path); From 296ac31092cd861b43e51ca04bcb3875b4c7e240 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 26 Mar 2024 11:23:01 +0900 Subject: [PATCH 56/69] Return static error page --- app/Http/Controllers/BeatmapDiscussionsController.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/BeatmapDiscussionsController.php b/app/Http/Controllers/BeatmapDiscussionsController.php index 9fa7e02ea6c..27bf4c84d28 100644 --- a/app/Http/Controllers/BeatmapDiscussionsController.php +++ b/app/Http/Controllers/BeatmapDiscussionsController.php @@ -120,7 +120,11 @@ public function index() public function mediaUrl() { - $url = presence(get_string(request('url'))) ?? abort(422); + $url = presence(get_string(request('url'))); + + if (!isset($url)) { + return response('Missing url parameter', 422); + } // Tell browser not to request url for a while. return redirect(proxy_media($url))->header('Cache-Control', 'max-age=600'); From 5e2ecfb21116f354db91599215413410ebddc408 Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 26 Mar 2024 13:54:58 +0900 Subject: [PATCH 57/69] Ignore invalid beatmap image --- .../ImageProcessorServiceException.php | 2 +- app/Libraries/ImageProcessorService.php | 22 ++++++++++++------- app/Models/Beatmapset.php | 10 ++++++++- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/Exceptions/ImageProcessorServiceException.php b/app/Exceptions/ImageProcessorServiceException.php index d657101daca..412a9dbc57c 100644 --- a/app/Exceptions/ImageProcessorServiceException.php +++ b/app/Exceptions/ImageProcessorServiceException.php @@ -9,5 +9,5 @@ class ImageProcessorServiceException extends Exception { - // doesn't really contain anything + const INVALID_IMAGE = 1; } diff --git a/app/Libraries/ImageProcessorService.php b/app/Libraries/ImageProcessorService.php index bbc52d9290a..e57cefb86b5 100644 --- a/app/Libraries/ImageProcessorService.php +++ b/app/Libraries/ImageProcessorService.php @@ -7,6 +7,8 @@ use App\Exceptions\ImageProcessorServiceException; use App\Models\Beatmapset; +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; class ImageProcessorService { @@ -42,15 +44,19 @@ public function process($method, $src) $src = preg_replace('/https?:\/\//', '', $src); try { $tmpFile = tmpfile(); - $bytesWritten = fwrite($tmpFile, file_get_contents($this->endpoint."/{$method}/{$src}")); - } catch (\ErrorException $e) { - if (strpos($e->getMessage(), 'HTTP request failed!') !== false) { - throw new ImageProcessorServiceException('HTTP request failed!'); - } elseif (strpos($e->getMessage(), 'Connection refused') !== false) { - throw new ImageProcessorServiceException('Connection refused.'); - } else { - throw $e; + $bytesWritten = fwrite( + $tmpFile, + (new Client())->request('GET', "{$this->endpoint}/{$method}/{$src}")->getBody()->getContents(), + ); + } catch (GuzzleException $e) { + if (str_contains($e->getMessage(), 'VipsJpeg: Premature end of input file')) { + throw new ImageProcessorServiceException( + 'Invalid image file', + ImageProcessorServiceException::INVALID_IMAGE, + $e, + ); } + throw new ImageProcessorServiceException('HTTP request failed!', 0, $e); } if ($bytesWritten === false || $bytesWritten < 100) { diff --git a/app/Models/Beatmapset.php b/app/Models/Beatmapset.php index a0b00860d01..4a5d70efc2f 100644 --- a/app/Models/Beatmapset.php +++ b/app/Models/Beatmapset.php @@ -7,6 +7,7 @@ use App\Enums\Ruleset; use App\Exceptions\BeatmapProcessorException; +use App\Exceptions\ImageProcessorServiceException; use App\Exceptions\InvariantException; use App\Jobs\CheckBeatmapsetCovers; use App\Jobs\EsDocument; @@ -553,7 +554,14 @@ public function regenerateCovers(array $sizesToRegenerate = null) $processor = new ImageProcessorService(); // upload optimized full-size version - $optimized = $processor->optimize($this->coverURL('raw', $timestamp)); + try { + $optimized = $processor->optimize($this->coverURL('raw', $timestamp)); + } catch (ImageProcessorServiceException $e) { + if ($e->getCode() === ImageProcessorServiceException::INVALID_IMAGE) { + return false; + } + throw $e; + } $this->storeCover('fullsize.jpg', get_stream_filename($optimized)); // use thumbnailer to generate (and then upload) all our variants From 2b61d99f1bda87ad5e84af9fccb4b30cdae218cc Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 29 Mar 2024 15:01:16 +0900 Subject: [PATCH 58/69] Wrap mod list in score page The original structure was so mods don't really affect overall action alignment but looking again it doesn't seem to really matter anyway. --- resources/css/bem/score-player.less | 20 ++++++++++++-------- resources/js/scores-show/player.tsx | 12 +++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/resources/css/bem/score-player.less b/resources/css/bem/score-player.less index d4c1950b789..ab895a81fea 100644 --- a/resources/css/bem/score-player.less +++ b/resources/css/bem/score-player.less @@ -2,17 +2,17 @@ // See the LICENCE file in the repository root for full licence text. .score-player { - &__mod { - margin: 3px; - font-size: 22px; // icon size - flex: none; + width: 250px; + max-width: 100%; + @media @desktop { + width: auto; } &__mods { - margin: -3px; + font-size: 22px; // icon size display: flex; - position: absolute; - top: -5px; + flex-wrap: wrap; + gap: 2px; } &__row { @@ -29,6 +29,10 @@ grid-template-columns: max-content; grid-gap: 0 10px; } + + &--score { + display: grid; + } } &__rank { @@ -50,7 +54,7 @@ &__score { font-size: @font-size--large-4; font-weight: 300; - margin-bottom: -10px; + line-height: 1; @media @desktop { font-size: @font-size--extra-large; } diff --git a/resources/js/scores-show/player.tsx b/resources/js/scores-show/player.tsx index 5b12ac8cc26..99c39a52325 100644 --- a/resources/js/scores-show/player.tsx +++ b/resources/js/scores-show/player.tsx @@ -28,17 +28,15 @@ export default function Player(props: Props) { return (
-
- {formatNumber(totalScore(props.score))} -
-
{filterMods(props.score).map((mod) => ( -
- -
+ ))}
+ +
+ {formatNumber(totalScore(props.score))} +
From cffb30991941f110a0a499e646f06429464563a8 Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 3 Apr 2024 15:38:29 +0900 Subject: [PATCH 59/69] Add original tracks filter option on featured artists track search --- app/Libraries/Search/ArtistTrackSearch.php | 8 ++++ .../Search/ArtistTrackSearchParams.php | 1 + .../ArtistTrackSearchParamsFromRequest.php | 6 +++ resources/css/bem-index.less | 1 + .../artist-track-search-form-switches.less | 34 +++++++++++++++ resources/js/artist-tracks-index/make-link.ts | 13 +++++- .../js/artist-tracks-index/search-form.tsx | 41 +++++++++++++++---- resources/lang/en/artist.php | 6 +++ 8 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 resources/css/bem/artist-track-search-form-switches.less diff --git a/app/Libraries/Search/ArtistTrackSearch.php b/app/Libraries/Search/ArtistTrackSearch.php index efc47a4fe75..8030d394579 100644 --- a/app/Libraries/Search/ArtistTrackSearch.php +++ b/app/Libraries/Search/ArtistTrackSearch.php @@ -33,10 +33,18 @@ public function getQuery() $this->addQueryStringFilter($query); $this->addSimpleFilters($query); $this->addTextFilters($query); + $this->addExclusiveOnlyFilter($query); return $query; } + private function addExclusiveOnlyFilter(BoolQuery $query): void + { + if ($this->params->exclusiveOnly) { + $query->filter(['term' => ['exclusive' => true]]); + } + } + private function addQueryStringFilter($query): void { $value = $this->params->queryString; diff --git a/app/Libraries/Search/ArtistTrackSearchParams.php b/app/Libraries/Search/ArtistTrackSearchParams.php index d48db2e73da..26f28ed7322 100644 --- a/app/Libraries/Search/ArtistTrackSearchParams.php +++ b/app/Libraries/Search/ArtistTrackSearchParams.php @@ -27,6 +27,7 @@ class ArtistTrackSearchParams extends SearchParams public ?string $album; public ?string $artist; public ?array $bpm; + public bool $exclusiveOnly = false; public ?string $genre; public bool $isDefaultSort = false; public ?array $length; diff --git a/app/Libraries/Search/ArtistTrackSearchParamsFromRequest.php b/app/Libraries/Search/ArtistTrackSearchParamsFromRequest.php index 2c12c007488..247bce6a7a6 100644 --- a/app/Libraries/Search/ArtistTrackSearchParamsFromRequest.php +++ b/app/Libraries/Search/ArtistTrackSearchParamsFromRequest.php @@ -18,6 +18,7 @@ public static function fromArray(array $rawParams) 'album', 'artist', 'bpm:array', + 'exclusive_only:bool', 'genre', 'is_default_sort:bool', 'length:array', @@ -37,6 +38,10 @@ public static function fromArray(array $rawParams) $params->parseSort($paramsArray['sort'], $paramsArray['is_default_sort']); $params->searchAfter = SearchAfterParam::make($params, cursor_from_params($rawParams)); // TODO: enforce value types + if (isset($paramsArray['exclusive_only'])) { + $params->exclusiveOnly = $paramsArray['exclusive_only']; + } + return $params; } @@ -46,6 +51,7 @@ public static function toArray(ArtistTrackSearchParams $params) 'album' => $params->album, 'artist' => $params->artist, 'bpm' => $params->bpmInput, + 'exclusive_only' => $params->exclusiveOnly, 'genre' => $params->genre, 'is_default_sort' => $params->isDefaultSort, 'length' => $params->lengthInput, diff --git a/resources/css/bem-index.less b/resources/css/bem-index.less index 7aa517faf3f..343805fe0e8 100644 --- a/resources/css/bem-index.less +++ b/resources/css/bem-index.less @@ -16,6 +16,7 @@ @import "bem/artist-sidebar-album"; @import "bem/artist-track"; @import "bem/artist-track-search-form"; +@import "bem/artist-track-search-form-switches"; @import "bem/artist-track-search-sort"; @import "bem/audio-player"; @import "bem/audio-player-floating"; diff --git a/resources/css/bem/artist-track-search-form-switches.less b/resources/css/bem/artist-track-search-form-switches.less new file mode 100644 index 00000000000..017bae6a4b9 --- /dev/null +++ b/resources/css/bem/artist-track-search-form-switches.less @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +.artist-track-search-form-switches { + display: grid; + gap: 5px; + grid-template-columns: repeat(auto-fill, minmax(125px, 1fr)); + + &__link { + .link-plain(); + font-size: @font-size--normal; + font-weight: bold; + background: hsl(var(--hsl-b3)); + color: hsl(var(--hsl-c1)); + padding: 2px 15px 3px; // slightly more on the bottom to compensate font vertical alignment + border-radius: 10000px; + transition: none; + text-align: center; + white-space: nowrap; + + &:focus, &:active { + color: hsl(var(--hsl-c1)); + } + + &:hover { + background: hsl(var(--hsl-l1)); + color: hsl(var(--hsl-b6)); + } + + &--active { + background: hsl(var(--hsl-b1)); + } + } +} diff --git a/resources/js/artist-tracks-index/make-link.ts b/resources/js/artist-tracks-index/make-link.ts index 2765f8d8265..10fce496be2 100644 --- a/resources/js/artist-tracks-index/make-link.ts +++ b/resources/js/artist-tracks-index/make-link.ts @@ -5,5 +5,16 @@ import { route } from 'laroute'; import { ArtistTrackSearch } from './search-form'; export default function makeLink(params: ArtistTrackSearch) { - return `${route('artists.tracks.index')}?${$.param(params)}`; + const urlParams: Partial = { ...params }; + if (!urlParams.exclusive_only) { + delete urlParams.exclusive_only; + } + if (urlParams.is_default_sort) { + // no need to set sort params if default + delete urlParams.sort; + } + // backend automatically determines this + delete urlParams.is_default_sort; + + return route('artists.tracks.index', urlParams); } diff --git a/resources/js/artist-tracks-index/search-form.tsx b/resources/js/artist-tracks-index/search-form.tsx index 164198c6f93..e0285f49333 100644 --- a/resources/js/artist-tracks-index/search-form.tsx +++ b/resources/js/artist-tracks-index/search-form.tsx @@ -43,6 +43,7 @@ const artistTrackSearchNumberRangeParams = ['bpm', 'length'] as const; type ArtistTrackSearchNumberRangeParam = typeof artistTrackSearchNumberRangeParams[number]; export type ArtistTrackSearch = { + exclusive_only: boolean; genre?: Nullable; is_default_sort: boolean; sort: ArtistTrackSort; @@ -63,12 +64,13 @@ export default class SearchForm extends React.Component { @computed private get url() { - return this.makeLink(); + return makeLink(this.params); } @computed private get emptySearch() { return { + exclusive_only: false, is_default_sort: this.params.is_default_sort, sort: this.params.sort, }; @@ -172,17 +174,35 @@ export default class SearchForm extends React.Component { -
+
{this.renderGenreLink(trans('artist.tracks.index.form.genre_all'), null)} {this.props.availableGenres.map((genre) => this.renderGenreLink(genre, genre))}
+ + +
+ {([['all', false], ['exclusive_only', true]] as const).map(([label, value]) => ( + + {trans(`artist.tracks.index.exclusive_only.${label}`)} + + ))} +
+
{ } }; + @action + private readonly handleExclusiveOnlyLinkClick = (e: React.MouseEvent) => { + e.preventDefault(); + this.params.exclusive_only = e.currentTarget.dataset.value === '1'; + this.props.onNewSearch(e.currentTarget.href); + }; + @action private readonly handleGenreLinkClick = (e: React.MouseEvent) => { e.preventDefault(); @@ -278,19 +305,15 @@ export default class SearchForm extends React.Component { } }; - private makeLink(params: ArtistTrackSearch = this.params) { - return makeLink(params); - } - private renderGenreLink(name: string, value: Nullable) { return ( {name} diff --git a/resources/lang/en/artist.php b/resources/lang/en/artist.php index 7517ec12726..500d0c52b6d 100644 --- a/resources/lang/en/artist.php +++ b/resources/lang/en/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'track search', + 'exclusive_only' => [ + 'all' => 'All', + 'exclusive_only' => 'osu! original', + ], + 'form' => [ 'advanced' => 'Advanced Search', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Minimum', 'bpm_lte' => 'BPM Maximum', 'empty' => 'No tracks matching search criteria were found.', + 'exclusive_only' => 'Type', 'genre' => 'Genre', 'genre_all' => 'All', 'length_gte' => 'Length Minimum', From 050c6dba9660f726799bb9b6e85e1b24bebe8b11 Mon Sep 17 00:00:00 2001 From: nanaya Date: Thu, 4 Apr 2024 19:43:06 +0900 Subject: [PATCH 60/69] Print caller line of count change test on failure --- tests/TestCase.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/TestCase.php b/tests/TestCase.php index 7123d3d81a0..8092d01c19d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -249,6 +249,12 @@ protected function createToken(?User $user, ?array $scopes = null, ?Client $clie protected function expectCountChange(callable $callback, int $change, string $message = '') { + $traceEntry = debug_backtrace(0, 1)[0]; + if ($message !== '') { + $message .= "\n"; + } + $message .= "{$traceEntry['file']}:{$traceEntry['line']}"; + $this->expectedCountsCallbacks[] = [ 'callback' => $callback, 'expected' => $callback() + $change, From 17517f72a33108e4b48fd6d11be15b5a9820b2fb Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 16 Feb 2024 22:56:23 +0900 Subject: [PATCH 61/69] Limit score pinning support to new score table --- .env.example | 1 - app/Http/Controllers/ScorePinsController.php | 56 ++---- app/Http/Controllers/ScoresController.php | 12 +- app/Jobs/RenumberUserScorePins.php | 4 +- app/Models/ScorePin.php | 36 +--- app/Singletons/OsuAuthorize.php | 4 - app/Singletons/UserScorePins.php | 41 ++-- .../CurrentUserAttributesTransformer.php | 5 +- config/osu.php | 1 - database/factories/Solo/ScoreFactory.php | 5 +- ...19_063204_nullable_score_pins_score_id.php | 29 +++ resources/js/core/user/score-pins.ts | 23 +-- resources/js/interfaces/score-json.ts | 1 - resources/js/profile-page/controller.tsx | 2 +- tests/Controllers/ScorePinsControllerTest.php | 181 ++++++++---------- 15 files changed, 170 insertions(+), 231 deletions(-) create mode 100644 database/migrations/2024_02_19_063204_nullable_score_pins_score_id.php diff --git a/.env.example b/.env.example index 91e787ec225..932cf21d3d2 100644 --- a/.env.example +++ b/.env.example @@ -246,7 +246,6 @@ CLIENT_CHECK_VERSION=false # USER_MAX_SCORE_PINS=10 # USER_MAX_SCORE_PINS_SUPPORTER=50 -# USER_HIDE_PINNED_SOLO_SCORES=true # the content is in markdown format # USER_PROFILE_SCORES_NOTICE= diff --git a/app/Http/Controllers/ScorePinsController.php b/app/Http/Controllers/ScorePinsController.php index ed12cefdf96..9d416a2a79e 100644 --- a/app/Http/Controllers/ScorePinsController.php +++ b/app/Http/Controllers/ScorePinsController.php @@ -6,8 +6,6 @@ namespace App\Http\Controllers; use App\Jobs\RenumberUserScorePins; -use App\Libraries\MorphMap; -use App\Models\Beatmap; use App\Models\ScorePin; use App\Models\Solo; use Exception; @@ -23,29 +21,31 @@ public function __construct() public function destroy() { - auth()->user()->scorePins()->where($this->getScoreParams(request()->all()))->delete(); + \Auth::user()->scorePins()->whereKey(get_int(Request('score_id')))->delete(); return response()->noContent(); } public function reorder() { - $rawParams = request()->all(); - $targetParams = $this->getScoreParams($rawParams); + $rawParams = \Request::all(); + $targetId = get_int($rawParams['score_id'] ?? null); - $pinsQuery = auth()->user()->scorePins(); - $target = $pinsQuery->clone()->where($targetParams)->firstOrFail(); + $pinsQuery = \Auth::user()->scorePins(); + $target = $pinsQuery->clone()->findOrFail($targetId); + $rulesetId = $target->ruleset_id; + $pinsQuery->where('ruleset_id', $rulesetId); $adjacentScores = []; foreach (['order1', 'order3'] as $position) { - $adjacentScores[$position] = $this->getScoreParams(get_arr($rawParams[$position] ?? null) ?? []); + $adjacentScoreIds[$position] = get_int($rawParams[$position]['score_id'] ?? null); } - $order1Item = isset($adjacentScores['order1']['score_id']) - ? $pinsQuery->clone()->where($adjacentScores['order1'])->first() + $order1Item = isset($adjacentScoreIds['order1']) + ? $pinsQuery->clone()->find($adjacentScoreIds['order1']) : null; - $order3Item = $order1Item === null && isset($adjacentScores['order3']['score_id']) - ? $pinsQuery->clone()->where($adjacentScores['order3'])->first() + $order3Item = $order1Item === null && isset($adjacentScoreIds['order3']) + ? $pinsQuery->clone()->find($adjacentScoreIds['order3']) : null; abort_if($order1Item === null && $order3Item === null, 422, 'no valid pinned score reference is specified'); @@ -63,7 +63,7 @@ public function reorder() $order2 = ($order1 + $order3) / 2; if ($order3 - $order1 < 0.1) { - dispatch(new RenumberUserScorePins($target->user_id, $target->score_type)); + dispatch(new RenumberUserScorePins($target->user_id, $target->ruleset_id)); } $target->update(['display_order' => $order2]); @@ -73,33 +73,25 @@ public function reorder() public function store() { - $params = $this->getScoreParams(request()->all()); - - abort_if(!ScorePin::isValidType($params['score_type']), 422, 'invalid score_type'); - - $score = MorphMap::getClass($params['score_type'])::find($params['score_id']); + $id = get_int(Request('score_id')); + $score = Solo\Score::find($id); abort_if($score === null, 422, "specified score couldn't be found"); - $user = auth()->user(); + $user = \Auth::user(); - $pin = ScorePin::where(['user_id' => $user->getKey()])->whereMorphedTo('score', $score)->first(); + $pin = $user->scorePins()->find($id); if ($pin === null) { priv_check('ScorePin', $score)->ensureCan(); - $rulesetId = Beatmap::MODES[$score->getMode()]; + $rulesetId = $score->ruleset_id; $currentMinDisplayOrder = $user->scorePins()->where('ruleset_id', $rulesetId)->min('display_order') ?? 2500; - $soloScore = $score instanceof Solo\Score - ? $score - : Solo\Score::firstWhere(['ruleset_id' => $rulesetId, 'legacy_score_id' => $score->getKey()]); - try { (new ScorePin([ 'display_order' => $currentMinDisplayOrder - 100, 'ruleset_id' => $rulesetId, - 'new_score_id' => $soloScore?->getKey(), ]))->user()->associate($user) ->score()->associate($score) ->saveOrExplode(); @@ -109,19 +101,9 @@ public function store() } } - if ($score instanceof Solo\Score) { - $score->update(['preserve' => true]); - } + $score->update(['preserve' => true]); } return response()->noContent(); } - - private function getScoreParams(array $form) - { - return get_params($form, null, [ - 'score_type:string', - 'score_id:int', - ], ['null_missing' => true]); - } } diff --git a/app/Http/Controllers/ScoresController.php b/app/Http/Controllers/ScoresController.php index 28a92d5c405..cfdae5f942c 100644 --- a/app/Http/Controllers/ScoresController.php +++ b/app/Http/Controllers/ScoresController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; +use App\Enums\Ruleset; use App\Models\Score\Best\Model as ScoreBest; use App\Models\Solo\Score as SoloScore; use App\Transformers\ScoreTransformer; @@ -90,10 +91,13 @@ public function download($rulesetOrSoloId, $id = null) public function show($rulesetOrSoloId, $legacyId = null) { - [$scoreClass, $id] = $legacyId === null - ? [SoloScore::class, $rulesetOrSoloId] - : [ScoreBest::getClass($rulesetOrSoloId), $legacyId]; - $score = $scoreClass::whereHas('beatmap.beatmapset')->visibleUsers()->findOrFail($id); + $scoreQuery = $legacyId === null + ? SoloScore::whereKey($rulesetOrSoloId) + : SoloScore::where([ + 'ruleset_id' => Ruleset::tryFromName($rulesetOrSoloId) ?? abort(404, 'unknown ruleset name'), + 'legacy_score_id' => $legacyId, + ]); + $score = $scoreQuery->whereHas('beatmap.beatmapset')->visibleUsers()->firstOrFail(); $userIncludes = array_map(function ($include) { return "user.{$include}"; diff --git a/app/Jobs/RenumberUserScorePins.php b/app/Jobs/RenumberUserScorePins.php index ed6336b3447..11afce044f6 100644 --- a/app/Jobs/RenumberUserScorePins.php +++ b/app/Jobs/RenumberUserScorePins.php @@ -15,7 +15,7 @@ class RenumberUserScorePins implements ShouldQueue { use InteractsWithQueue, Queueable; - public function __construct(private int $userId, private string $scoreType) + public function __construct(private int $userId, private int $rulesetId) { } @@ -24,7 +24,7 @@ public function handle() DB::transaction(function () { $pins = ScorePin ::where([ - 'score_type' => $this->scoreType, + 'ruleset_id' => $this->rulesetId, 'user_id' => $this->userId, ])->orderBy('display_order', 'asc') ->lockForUpdate() diff --git a/app/Models/ScorePin.php b/app/Models/ScorePin.php index 5ac30be41fb..eb4e206f782 100644 --- a/app/Models/ScorePin.php +++ b/app/Models/ScorePin.php @@ -7,36 +7,19 @@ namespace App\Models; -use App\Libraries\MorphMap; -use App\Models\Score\Best as ScoreBest; -use Ds\Set; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphTo; /** * @property \Carbon\Carbon|null $created_at - * @property \App\Models\Score\Best\Model $score + * @property \App\Models\Solo\Score $score * @property \Carbon\Carbon|null $updated_at */ class ScorePin extends Model { - const SCORES = [ - MorphMap::MAP[ScoreBest\Fruits::class], - MorphMap::MAP[ScoreBest\Mania::class], - MorphMap::MAP[ScoreBest\Osu::class], - MorphMap::MAP[ScoreBest\Taiko::class], - MorphMap::MAP[Solo\Score::class], - ]; + public $incrementing = false; - public static function isValidType(string|null $type): bool - { - static $lookup; - - $lookup ??= new Set(static::SCORES); - - return $lookup->contains($type); - } + protected $primaryKey = 'new_score_id'; public function scopeForRuleset($query, string $ruleset): Builder { @@ -45,19 +28,12 @@ public function scopeForRuleset($query, string $ruleset): Builder public function scopeWithVisibleScore($query): Builder { - $scoreModels = static::SCORES; - - if ($GLOBALS['cfg']['osu']['user']['hide_pinned_solo_scores']) { - $soloScoreIndex = array_search_null(MorphMap::MAP[Solo\Score::class], $scoreModels); - array_splice($scoreModels, $soloScoreIndex, 1); - } - - return $query->whereHasMorph('score', $scoreModels, fn ($q) => $q->whereHas('beatmap.beatmapset')); + return $query->whereHas('score', fn ($q) => $q->whereHas('beatmap.beatmapset')); } - public function score(): MorphTo + public function score(): BelongsTo { - return $this->morphTo(); + return $this->belongsTo(Solo\Score::class, 'new_score_id'); } public function user(): BelongsTo diff --git a/app/Singletons/OsuAuthorize.php b/app/Singletons/OsuAuthorize.php index aa7da433ce1..1ae47f43737 100644 --- a/app/Singletons/OsuAuthorize.php +++ b/app/Singletons/OsuAuthorize.php @@ -1943,10 +1943,6 @@ public function checkScorePin(?User $user, ScoreBest|Solo\Score $score): string return $prefix.'failed'; } - if ($score instanceof Solo\Score && $GLOBALS['cfg']['osu']['user']['hide_pinned_solo_scores']) { - return $prefix.'disabled_type'; - } - $pinned = $user->scorePins()->forRuleset($score->getMode())->withVisibleScore()->count(); if ($pinned >= $user->maxScorePins()) { diff --git a/app/Singletons/UserScorePins.php b/app/Singletons/UserScorePins.php index 15b6753debd..f83fbb2c9df 100644 --- a/app/Singletons/UserScorePins.php +++ b/app/Singletons/UserScorePins.php @@ -7,48 +7,31 @@ namespace App\Singletons; -use App\Libraries\MorphMap; -use App\Models\Beatmap; -use App\Models\Score; -use App\Models\ScorePin; use App\Models\Solo; +use Ds\Set; class UserScorePins { - const REQUEST_ATTRIBUTE_KEY_PREFIX = 'current_user_score_pins:'; + const REQUEST_ATTRIBUTE_KEY = 'current_user_score_pins'; - public function isPinned(Score\Best\Model|Solo\Score $score): bool + public function isPinned(Solo\Score $score): bool { - $type = $score->getMorphClass(); - $key = static::REQUEST_ATTRIBUTE_KEY_PREFIX.$type; - $pins = request()->attributes->get($key); + $attributes = \Request::instance()->attributes; + $pins = $attributes->get(static::REQUEST_ATTRIBUTE_KEY); if ($pins === null) { - $user = auth()->user(); - $pins = $user === null - ? [] - : $user->scorePins() - ->select('score_id') - ->where(['score_type' => $type]) - ->get() - ->keyBy(fn (ScorePin $p) => $p->score_id); - - request()->attributes->set($key, $pins); + $pins = new Set(\Auth::user()?->scorePins()->pluck('new_score_id') ?? []); + + $attributes->set(static::REQUEST_ATTRIBUTE_KEY, $pins); } - return isset($pins[$score->getKey()]); + return $pins->contains($score->getKey()); } public function reset(): void { - $prefix = static::REQUEST_ATTRIBUTE_KEY_PREFIX; - $attributes = request()->attributes; - - $attributes->remove($prefix.MorphMap::getType(Solo\Score::class)); - - foreach (Beatmap::MODES as $ruleset => $rulesetId) { - $type = MorphMap::getType(Score\Best\Model::getClass($ruleset)); - $attributes->remove("{$prefix}{$type}"); - } + \Request::instance() + ->attributes + ->remove(static::REQUEST_ATTRIBUTE_KEY); } } diff --git a/app/Transformers/Score/CurrentUserAttributesTransformer.php b/app/Transformers/Score/CurrentUserAttributesTransformer.php index 88767d9fba6..2a3b440af13 100644 --- a/app/Transformers/Score/CurrentUserAttributesTransformer.php +++ b/app/Transformers/Score/CurrentUserAttributesTransformer.php @@ -17,9 +17,7 @@ class CurrentUserAttributesTransformer extends TransformerAbstract { public function transform(LegacyMatch\Score|MultiplayerScoreLink|ScoreModel|SoloScore $score): array { - if ($score instanceof ScoreModel) { - $pinnable = $score->best; - } elseif ($score instanceof SoloScore) { + if ($score instanceof SoloScore) { $pinnable = $score; } elseif ($score instanceof MultiplayerScoreLink) { $pinnable = $score->score; @@ -32,7 +30,6 @@ public function transform(LegacyMatch\Score|MultiplayerScoreLink|ScoreModel|Solo ? [ 'is_pinned' => app('score-pins')->isPinned($pinnable), 'score_id' => $pinnable->getKey(), - 'score_type' => $pinnable->getMorphClass(), ] : null, ]; } diff --git a/config/osu.php b/config/osu.php index ebda91fb287..7163489eb4d 100644 --- a/config/osu.php +++ b/config/osu.php @@ -246,7 +246,6 @@ 'allow_registration' => get_bool(env('ALLOW_REGISTRATION')) ?? true, 'allowed_rename_groups' => explode(' ', env('USER_ALLOWED_RENAME_GROUPS', 'default')), 'bypass_verification' => get_bool(env('USER_BYPASS_VERIFICATION')) ?? false, - 'hide_pinned_solo_scores' => get_bool(env('USER_HIDE_PINNED_SOLO_SCORES')) ?? true, 'inactive_force_password_reset' => get_bool(env('USER_INACTIVE_FORCE_PASSWORD_RESET') ?? false), 'inactive_seconds_verification' => (get_int(env('USER_INACTIVE_DAYS_VERIFICATION')) ?? 180) * 86400, 'min_plays_for_posting' => get_int(env('USER_MIN_PLAYS_FOR_POSTING')) ?? 10, diff --git a/database/factories/Solo/ScoreFactory.php b/database/factories/Solo/ScoreFactory.php index fb4bbfa35a7..fe9d211a2b6 100644 --- a/database/factories/Solo/ScoreFactory.php +++ b/database/factories/Solo/ScoreFactory.php @@ -21,13 +21,16 @@ public function definition(): array { return [ 'accuracy' => fn (): float => $this->faker->randomFloat(1, 0, 1), - 'beatmap_id' => Beatmap::factory()->ranked(), 'ended_at' => new \DateTime(), 'pp' => fn (): float => $this->faker->randomFloat(4, 0, 1000), 'rank' => fn () => array_rand_val(ScoreRank::cases())->value, 'total_score' => fn (): int => $this->faker->randomNumber(7), 'user_id' => User::factory(), + 'beatmap_id' => fn (array $attr) => is_int($attr['ruleset_id'] ?? null) + ? Beatmap::factory()->state(['playmode' => $attr['ruleset_id']])->ranked() + : Beatmap::factory()->ranked(), + // depends on beatmap_id 'ruleset_id' => fn (array $attr) => Beatmap::find($attr['beatmap_id'])->playmode, diff --git a/database/migrations/2024_02_19_063204_nullable_score_pins_score_id.php b/database/migrations/2024_02_19_063204_nullable_score_pins_score_id.php new file mode 100644 index 00000000000..0e910aca23e --- /dev/null +++ b/database/migrations/2024_02_19_063204_nullable_score_pins_score_id.php @@ -0,0 +1,29 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +declare(strict_types=1); + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + public function up(): void + { + Schema::table('score_pins', function (Blueprint $table) { + $table->unsignedBigInteger('score_id')->nullable(true)->change(); + $table->index(['user_id', 'new_score_id']); + }); + } + + public function down(): void + { + Schema::table('score_pins', function (Blueprint $table) { + $table->unsignedBigInteger('score_id')->nullable(true)->change(); + $table->dropIndex(['user_id', 'new_score_id']); + }); + } +}; diff --git a/resources/js/core/user/score-pins.ts b/resources/js/core/user/score-pins.ts index 61caf87bc80..f0e420cec6c 100644 --- a/resources/js/core/user/score-pins.ts +++ b/resources/js/core/user/score-pins.ts @@ -1,13 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. -import { ScoreCurrentUserPinJson } from 'interfaces/score-json'; import SoloScoreJson from 'interfaces/solo-score-json'; import { route } from 'laroute'; import { action, makeObservable, observable, runInAction } from 'mobx'; export default class ScorePins { - @observable pins = new Map(); + @observable pins = new Map(); constructor() { makeObservable(this); @@ -40,15 +39,13 @@ export default class ScorePins { return false; } - const mapKey = this.mapKey(pin); - - if (!this.pins.has(mapKey)) { + if (!this.pins.has(pin.score_id)) { runInAction(() => { - this.pins.set(mapKey, pin.is_pinned); + this.pins.set(pin.score_id, pin.is_pinned); }); } - return this.pins.get(mapKey); + return this.pins.get(pin.score_id); } @action @@ -56,16 +53,6 @@ export default class ScorePins { const pin = score.current_user_attributes.pin; if (pin == null) return; - const mapKey = this.mapKey(pin); - - if (mapKey == null) { - return null; - } - - this.pins.set(mapKey, isPinned); - } - - private mapKey(pin: ScoreCurrentUserPinJson) { - return `${pin.score_type}:${pin.score_id}`; + this.pins.set(pin.score_id, isPinned); } } diff --git a/resources/js/interfaces/score-json.ts b/resources/js/interfaces/score-json.ts index d52a2bef0bc..eca81a39cd2 100644 --- a/resources/js/interfaces/score-json.ts +++ b/resources/js/interfaces/score-json.ts @@ -10,7 +10,6 @@ import UserJson from './user-json'; export interface ScoreCurrentUserPinJson { is_pinned: boolean; score_id: number; - score_type: `score_best_${GameMode}` | 'solo_score'; } export type ScoreStatisticsAttribute = 'count_50' | 'count_100' | 'count_300' | 'count_geki' | 'count_katu' | 'count_miss'; diff --git a/resources/js/profile-page/controller.tsx b/resources/js/profile-page/controller.tsx index 58cc05b8550..d9e4f6a35ad 100644 --- a/resources/js/profile-page/controller.tsx +++ b/resources/js/profile-page/controller.tsx @@ -79,7 +79,7 @@ interface LazyPages { export type Page = ProfileExtraPage | 'main'; -type ScorePinReorderParamsBase = Pick; +type ScorePinReorderParamsBase = Pick; interface ScorePinReorderParams extends ScorePinReorderParamsBase { order1?: ScorePinReorderParamsBase; diff --git a/tests/Controllers/ScorePinsControllerTest.php b/tests/Controllers/ScorePinsControllerTest.php index 3b4b043e28f..3c0f4b6a265 100644 --- a/tests/Controllers/ScorePinsControllerTest.php +++ b/tests/Controllers/ScorePinsControllerTest.php @@ -7,56 +7,70 @@ namespace Tests\Controllers; -use App\Models\Score\Best\Osu; -use App\Models\Score\Best\Taiko; +use App\Enums\Ruleset; use App\Models\ScorePin; +use App\Models\Solo\Score; use App\Models\User; use Tests\TestCase; class ScorePinsControllerTest extends TestCase { + private static function createScore(?User $user = null, ?Ruleset $ruleset = null, ?bool $passed = null): Score + { + if ($ruleset !== null) { + $params['ruleset_id'] = $ruleset->value; + } + if ($user !== null) { + $params['user_id'] = $user; + } + $params['passed'] = $passed ?? true; + + return Score::factory()->create($params); + } + + private static function makeParams(Score $score): array + { + return [ + 'score_id' => $score->getKey(), + ]; + } + public function testDestroy() { - $pin = ScorePin::factory()->withScore(Osu::factory()->create())->create(); + $pin = ScorePin::factory()->withScore(static::createScore())->create(); - $initialPinCount = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), -1); $this->actAsUser($pin->user, true); $this - ->delete(route('score-pins.destroy', $this->makeParams($pin->score))) + ->delete(route('score-pins.destroy', static::makeParams($pin->score))) ->assertSuccessful(); - - $this->assertSame(ScorePin::count(), $initialPinCount - 1); } public function testDestroyAsDifferentUser() { - $pin = ScorePin::factory()->withScore(Osu::factory()->create())->create(); + $pin = ScorePin::factory()->withScore(static::createScore())->create(); $otherUser = User::factory()->create(); - $initialPinCount = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 0); $this->actAsUser($otherUser, true); $this - ->delete(route('score-pins.destroy', $this->makeParams($pin->score))) + ->delete(route('score-pins.destroy', static::makeParams($pin->score))) ->assertSuccessful(); - - $this->assertSame(ScorePin::count(), $initialPinCount); } public function testDestroyAsGuest() { - $pin = ScorePin::factory()->withScore(Osu::factory()->create())->create(); + $pin = ScorePin::factory()->withScore(static::createScore())->create(); - $initialPinCount = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 0); $this - ->delete(route('score-pins.destroy', $this->makeParams($pin->score))) + ->delete(route('score-pins.destroy', static::makeParams($pin->score))) ->assertStatus(401); - - $this->assertSame(ScorePin::count(), $initialPinCount); } // moving: [0]. expected order: [1] < [0] @@ -65,13 +79,15 @@ public function testReorderMoveBottom() $user = User::factory()->create(); $pins = collect([0, 1])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(Osu::factory(['user_id' => $user])->create()) + ->withScore(static::createScore($user, Ruleset::osu)) ->create()); $this->actAsUser($user, true); $this - ->put(route('score-pins.reorder'), array_merge($this->makeParams($pins[0]->score), ['order1' => $this->makeParams($pins[1]->score)])) - ->assertSuccessful(); + ->put(route('score-pins.reorder'), [ + ...static::makeParams($pins[0]->score), + 'order1' => static::makeParams($pins[1]->score), + ])->assertSuccessful(); $pins->map->refresh(); $this->assertTrue($pins[1]->display_order < $pins[0]->display_order); @@ -83,13 +99,15 @@ public function testReorderMoveDown() $user = User::factory()->create(); $pins = collect([0, 1, 2])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(Osu::factory(['user_id' => $user])->create()) + ->withScore(static::createScore($user, Ruleset::osu)) ->create()); $this->actAsUser($user, true); $this - ->put(route('score-pins.reorder'), array_merge($this->makeParams($pins[0]->score), ['order1' => $this->makeParams($pins[1]->score)])) - ->assertSuccessful(); + ->put(route('score-pins.reorder'), [ + ...static::makeParams($pins[0]->score), + 'order1' => static::makeParams($pins[1]->score), + ])->assertSuccessful(); $pins->map->refresh(); $this->assertTrue($pins[1]->display_order < $pins[0]->display_order); @@ -102,13 +120,15 @@ public function testReorderMoveTop() $user = User::factory()->create(); $pins = collect([0, 1])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(Osu::factory(['user_id' => $user])->create()) + ->withScore(static::createScore($user, Ruleset::osu)) ->create()); $this->actAsUser($user, true); $this - ->put(route('score-pins.reorder'), array_merge($this->makeParams($pins[1]->score), ['order3' => $this->makeParams($pins[0]->score)])) - ->assertSuccessful(); + ->put(route('score-pins.reorder'), [ + ...static::makeParams($pins[1]->score), + 'order3' => static::makeParams($pins[0]->score), + ])->assertSuccessful(); $pins->map->refresh(); $this->assertTrue($pins[1]->display_order < $pins[0]->display_order); @@ -120,13 +140,15 @@ public function testReorderMoveUp() $user = User::factory()->create(); $pins = collect([0, 1, 2])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(Osu::factory(['user_id' => $user])->create()) + ->withScore(static::createScore($user, Ruleset::osu)) ->create()); $this->actAsUser($user, true); $this - ->put(route('score-pins.reorder'), array_merge($this->makeParams($pins[2]->score), ['order1' => $this->makeParams($pins[0]->score)])) - ->assertSuccessful(); + ->put(route('score-pins.reorder'), [ + ...static::makeParams($pins[2]->score), + 'order1' => static::makeParams($pins[0]->score), + ])->assertSuccessful(); $pins->map->refresh(); $this->assertTrue($pins[0]->display_order < $pins[2]->display_order); @@ -135,46 +157,40 @@ public function testReorderMoveUp() public function testStore() { - $score = Osu::factory()->create(); + $score = static::createScore(); - $initialPinCont = ScorePin::count(); + $this->expectCountChange(fn () => $score->user->scorePins()->count(), 1); $this->actAsUser($score->user, true); $this - ->post(route('score-pins.store'), $this->makeParams($score)) + ->post(route('score-pins.store'), static::makeParams($score)) ->assertSuccessful(); - - $this->assertSame(ScorePin::count(), $initialPinCont + 1); } public function testStoreAsGuest() { - $score = Osu::factory()->create(); + $score = static::createScore(); - $initialPinCont = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 0); $this - ->post(route('score-pins.store'), $this->makeParams($score)) + ->post(route('score-pins.store'), static::makeParams($score)) ->assertStatus(401); - - $this->assertSame(ScorePin::count(), $initialPinCont); } public function testStoreAsNonOwner() { - $score = Osu::factory()->create(); + $score = static::createScore(); $otherUser = User::factory()->create(); - $initialPinCont = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 0); $this->actAsUser($otherUser, true); $this - ->post(route('score-pins.store'), $this->makeParams($score)) + ->post(route('score-pins.store'), static::makeParams($score)) ->assertStatus(403); - - $this->assertSame(ScorePin::count(), $initialPinCont); } // new score pin should always be above existing ones @@ -183,64 +199,45 @@ public function testStoreDisplayOrder() $user = User::factory()->create([ 'osu_subscriber' => false, ]); - $score1 = Osu::factory()->create(['user_id' => $user]); - $score2 = Osu::factory()->create(['user_id' => $user]); - $pin1 = ScorePin::factory()->withScore($score1, 'score')->create(); + $score1 = static::createScore($user, Ruleset::osu); + $score2 = static::createScore($user, Ruleset::osu); + $pin1 = ScorePin::factory()->withScore($score1)->create(); $this->actAsUser($user, true); $this - ->post(route('score-pins.store'), $this->makeParams($score2)) + ->post(route('score-pins.store'), static::makeParams($score2)) ->assertSuccessful(); - $responsePin = $user->scorePins()->whereMorphedTo('score', $score2)->first(); - $this->assertTrue($pin1->display_order > $responsePin->display_order); + $pin2 = $user->scorePins()->find($score2->getKey()); + $this->assertTrue($pin1->display_order > $pin2->display_order); } public function testStoreDuplicate() { - $score = Osu::factory()->create(); - $pin = ScorePin::factory()->withScore($score, 'score')->create(); + $score = static::createScore(); + $pin = ScorePin::factory()->withScore($score)->create(); - $initialPinCont = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 0); $this->actAsUser($score->user, true); $this - ->post(route('score-pins.store'), $this->makeParams($score)) + ->post(route('score-pins.store'), static::makeParams($score)) ->assertSuccessful(); - - $this->assertSame(ScorePin::count(), $initialPinCont); } public function testStoreInvalidScoreId() { - Osu::find(1)?->destroy(); + Score::whereKey(1)->delete(); - $initialPinCont = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 0); $this->actAsUser(User::factory()->create(), true); $this - ->post(route('score-pins.store'), ['score_type' => 'score_best_osu', 'score_id' => 1]) + ->post(route('score-pins.store'), ['score_id' => 1]) ->assertStatus(422); - - $this->assertSame(ScorePin::count(), $initialPinCont); - } - - public function testStoreInvalidScoreMode() - { - $score = Osu::factory()->create(); - - $initialPinCont = ScorePin::count(); - - $this->actAsUser(User::factory()->create(), true); - - $this - ->post(route('score-pins.store'), ['score_type' => '_invalid', 'score_id' => $score->getKey()]) - ->assertStatus(422); - - $this->assertSame(ScorePin::count(), $initialPinCont); } public function testStoreLimit() @@ -250,19 +247,17 @@ public function testStoreLimit() $user = User::factory()->create([ 'osu_subscriber' => false, ]); - $score1 = Osu::factory()->create(['user_id' => $user]); - $score2 = Osu::factory()->create(['user_id' => $user]); - $pin1 = ScorePin::factory()->withScore($score1, 'score')->create(); + $score1 = static::createScore($user, Ruleset::osu); + $score2 = static::createScore($user, Ruleset::osu); + $pin1 = ScorePin::factory()->withScore($score1)->create(); - $initialPinCont = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 0); $this->actAsUser($user, true); $this - ->post(route('score-pins.store'), $this->makeParams($score2)) + ->post(route('score-pins.store'), static::makeParams($score2)) ->assertStatus(403); - - $this->assertSame(ScorePin::count(), $initialPinCont); } public function testStoreLimitDifferentMode() @@ -272,26 +267,16 @@ public function testStoreLimitDifferentMode() $user = User::factory()->create([ 'osu_subscriber' => false, ]); - $score1 = Osu::factory()->create(['user_id' => $user]); - $score2 = Taiko::factory()->create(['user_id' => $user]); - $pin1 = ScorePin::factory()->withScore($score1, 'score')->create(); + $score1 = static::createScore($user, Ruleset::osu); + $score2 = static::createScore($user, Ruleset::taiko); + $pin1 = ScorePin::factory()->withScore($score1)->create(); - $initialPinCont = ScorePin::count(); + $this->expectCountChange(fn () => ScorePin::count(), 1); $this->actAsUser($user, true); $this - ->post(route('score-pins.store'), $this->makeParams($score2)) + ->post(route('score-pins.store'), static::makeParams($score2)) ->assertSuccessful(); - - $this->assertSame(ScorePin::count(), $initialPinCont + 1); - } - - private function makeParams($score) - { - return [ - 'score_id' => $score->getKey(), - 'score_type' => $score->getMorphClass(), - ]; } } From e8f24f4b70bccefffe36a921e1c2148235105eed Mon Sep 17 00:00:00 2001 From: nanaya Date: Wed, 3 Apr 2024 10:22:22 +0900 Subject: [PATCH 62/69] Correct case It's the helper function, not facade. --- app/Http/Controllers/ScorePinsController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/ScorePinsController.php b/app/Http/Controllers/ScorePinsController.php index 9d416a2a79e..68a958deb90 100644 --- a/app/Http/Controllers/ScorePinsController.php +++ b/app/Http/Controllers/ScorePinsController.php @@ -21,7 +21,7 @@ public function __construct() public function destroy() { - \Auth::user()->scorePins()->whereKey(get_int(Request('score_id')))->delete(); + \Auth::user()->scorePins()->whereKey(get_int(request('score_id')))->delete(); return response()->noContent(); } @@ -73,7 +73,7 @@ public function reorder() public function store() { - $id = get_int(Request('score_id')); + $id = get_int(request('score_id')); $score = Solo\Score::find($id); abort_if($score === null, 422, "specified score couldn't be found"); From d622b3d805f9020f92d63d1008e0041f4f3cad2a Mon Sep 17 00:00:00 2001 From: nanaya Date: Thu, 4 Apr 2024 21:46:29 +0900 Subject: [PATCH 63/69] Adjust migration plan --- app/Http/Controllers/ScorePinsController.php | 11 +++++++++++ ...php => 2024_04_04_063204_score_pins_new_index.php} | 2 -- 2 files changed, 11 insertions(+), 2 deletions(-) rename database/migrations/{2024_02_19_063204_nullable_score_pins_score_id.php => 2024_04_04_063204_score_pins_new_index.php} (82%) diff --git a/app/Http/Controllers/ScorePinsController.php b/app/Http/Controllers/ScorePinsController.php index 68a958deb90..c70d7977132 100644 --- a/app/Http/Controllers/ScorePinsController.php +++ b/app/Http/Controllers/ScorePinsController.php @@ -92,6 +92,17 @@ public function store() (new ScorePin([ 'display_order' => $currentMinDisplayOrder - 100, 'ruleset_id' => $rulesetId, + + /** + * TODO: + * 1. update score_id = new_score_id + * 2. remove duplicated score_id + * 3. use score_id as primary key (both model and database) + * 4. remove setting score_type below + * 5. remove new_score_id and score_type columns + */ + 'score_id' => $score->getKey(), + 'score_type' => $score->getMorphClass(), ]))->user()->associate($user) ->score()->associate($score) ->saveOrExplode(); diff --git a/database/migrations/2024_02_19_063204_nullable_score_pins_score_id.php b/database/migrations/2024_04_04_063204_score_pins_new_index.php similarity index 82% rename from database/migrations/2024_02_19_063204_nullable_score_pins_score_id.php rename to database/migrations/2024_04_04_063204_score_pins_new_index.php index 0e910aca23e..a9a1d0959b4 100644 --- a/database/migrations/2024_02_19_063204_nullable_score_pins_score_id.php +++ b/database/migrations/2024_04_04_063204_score_pins_new_index.php @@ -14,7 +14,6 @@ public function up(): void { Schema::table('score_pins', function (Blueprint $table) { - $table->unsignedBigInteger('score_id')->nullable(true)->change(); $table->index(['user_id', 'new_score_id']); }); } @@ -22,7 +21,6 @@ public function up(): void public function down(): void { Schema::table('score_pins', function (Blueprint $table) { - $table->unsignedBigInteger('score_id')->nullable(true)->change(); $table->dropIndex(['user_id', 'new_score_id']); }); } From e3f5d9411ed3a72f259ef91be8de348e47aa7158 Mon Sep 17 00:00:00 2001 From: nanaya Date: Thu, 4 Apr 2024 21:53:41 +0900 Subject: [PATCH 64/69] Fix tests --- database/factories/ScorePinFactory.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/database/factories/ScorePinFactory.php b/database/factories/ScorePinFactory.php index d6a656f2e01..6daa9a44bc6 100644 --- a/database/factories/ScorePinFactory.php +++ b/database/factories/ScorePinFactory.php @@ -8,7 +8,6 @@ namespace Database\Factories; use App\Models\Beatmap; -use App\Models\Score\Best\Model as ScoreBestModel; use App\Models\ScorePin; use App\Models\Solo; use App\Models\User; @@ -25,11 +24,13 @@ public function definition(): array ]; } - public function withScore(ScoreBestModel|Solo\Score $score): static + public function withScore(Solo\Score $score): static { return $this ->state([ 'ruleset_id' => Beatmap::MODES[$score->getMode()], + 'score_id' => $score->getKey(), + 'score_type' => $score->getMorphClass(), 'user_id' => $score->user, ])->for($score, 'score'); } From 662a30199c1fd45692f06c0f81d58cea7592bebd Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 5 Apr 2024 18:41:45 +0900 Subject: [PATCH 65/69] Use ruleset id directly --- tests/Controllers/ScorePinsControllerTest.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/Controllers/ScorePinsControllerTest.php b/tests/Controllers/ScorePinsControllerTest.php index 3c0f4b6a265..4827abb84e0 100644 --- a/tests/Controllers/ScorePinsControllerTest.php +++ b/tests/Controllers/ScorePinsControllerTest.php @@ -7,18 +7,18 @@ namespace Tests\Controllers; -use App\Enums\Ruleset; use App\Models\ScorePin; use App\Models\Solo\Score; use App\Models\User; +use App\Models\Beatmap; use Tests\TestCase; class ScorePinsControllerTest extends TestCase { - private static function createScore(?User $user = null, ?Ruleset $ruleset = null, ?bool $passed = null): Score + private static function createScore(?User $user = null, ?int $rulesetId = null, ?bool $passed = null): Score { - if ($ruleset !== null) { - $params['ruleset_id'] = $ruleset->value; + if ($rulesetId !== null) { + $params['ruleset_id'] = $rulesetId; } if ($user !== null) { $params['user_id'] = $user; @@ -79,7 +79,7 @@ public function testReorderMoveBottom() $user = User::factory()->create(); $pins = collect([0, 1])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(static::createScore($user, Ruleset::osu)) + ->withScore(static::createScore($user, Beatmap::MODES['osu'])) ->create()); $this->actAsUser($user, true); @@ -99,7 +99,7 @@ public function testReorderMoveDown() $user = User::factory()->create(); $pins = collect([0, 1, 2])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(static::createScore($user, Ruleset::osu)) + ->withScore(static::createScore($user, Beatmap::MODES['osu'])) ->create()); $this->actAsUser($user, true); @@ -120,7 +120,7 @@ public function testReorderMoveTop() $user = User::factory()->create(); $pins = collect([0, 1])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(static::createScore($user, Ruleset::osu)) + ->withScore(static::createScore($user, Beatmap::MODES['osu'])) ->create()); $this->actAsUser($user, true); @@ -140,7 +140,7 @@ public function testReorderMoveUp() $user = User::factory()->create(); $pins = collect([0, 1, 2])->map(fn ($order) => ScorePin ::factory(['display_order' => $order]) - ->withScore(static::createScore($user, Ruleset::osu)) + ->withScore(static::createScore($user, Beatmap::MODES['osu'])) ->create()); $this->actAsUser($user, true); @@ -199,8 +199,8 @@ public function testStoreDisplayOrder() $user = User::factory()->create([ 'osu_subscriber' => false, ]); - $score1 = static::createScore($user, Ruleset::osu); - $score2 = static::createScore($user, Ruleset::osu); + $score1 = static::createScore($user, Beatmap::MODES['osu']); + $score2 = static::createScore($user, Beatmap::MODES['osu']); $pin1 = ScorePin::factory()->withScore($score1)->create(); $this->actAsUser($user, true); @@ -247,8 +247,8 @@ public function testStoreLimit() $user = User::factory()->create([ 'osu_subscriber' => false, ]); - $score1 = static::createScore($user, Ruleset::osu); - $score2 = static::createScore($user, Ruleset::osu); + $score1 = static::createScore($user, Beatmap::MODES['osu']); + $score2 = static::createScore($user, Beatmap::MODES['osu']); $pin1 = ScorePin::factory()->withScore($score1)->create(); $this->expectCountChange(fn () => ScorePin::count(), 0); @@ -267,8 +267,8 @@ public function testStoreLimitDifferentMode() $user = User::factory()->create([ 'osu_subscriber' => false, ]); - $score1 = static::createScore($user, Ruleset::osu); - $score2 = static::createScore($user, Ruleset::taiko); + $score1 = static::createScore($user, Beatmap::MODES['osu']); + $score2 = static::createScore($user, Beatmap::MODES['taiko']); $pin1 = ScorePin::factory()->withScore($score1)->create(); $this->expectCountChange(fn () => ScorePin::count(), 1); From 442ecc2e56aeb18210ff62e6ee68883b40ac84ca Mon Sep 17 00:00:00 2001 From: nanaya Date: Fri, 5 Apr 2024 18:44:32 +0900 Subject: [PATCH 66/69] Lint fix --- tests/Controllers/ScorePinsControllerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Controllers/ScorePinsControllerTest.php b/tests/Controllers/ScorePinsControllerTest.php index 4827abb84e0..e35610ac205 100644 --- a/tests/Controllers/ScorePinsControllerTest.php +++ b/tests/Controllers/ScorePinsControllerTest.php @@ -7,10 +7,10 @@ namespace Tests\Controllers; +use App\Models\Beatmap; use App\Models\ScorePin; use App\Models\Solo\Score; use App\Models\User; -use App\Models\Beatmap; use Tests\TestCase; class ScorePinsControllerTest extends TestCase From 7a54a83bf241076452b6d77d64c1bd6669e10f9d Mon Sep 17 00:00:00 2001 From: bakaneko Date: Fri, 5 Apr 2024 22:28:55 +0900 Subject: [PATCH 67/69] add href and handle click --- resources/js/chat/join-channels.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/resources/js/chat/join-channels.tsx b/resources/js/chat/join-channels.tsx index e734e567033..da0618bbf95 100644 --- a/resources/js/chat/join-channels.tsx +++ b/resources/js/chat/join-channels.tsx @@ -3,6 +3,7 @@ import { Spinner } from 'components/spinner'; import ChannelJson from 'interfaces/chat/channel-json'; +import { route } from 'laroute'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import core from 'osu-core-singleton'; @@ -21,7 +22,12 @@ interface ChannelProps { function Channel({ channel, onClick, status }: ChannelProps) { const handleClick = React.useCallback( - () => onClick(channel.channel_id), + (event: React.MouseEvent) => { + // prevent navigation and new tab, it's supposed to be a button plus new tab doesn't join the channel. + // context menu is still a problem... + event.preventDefault(); + onClick(channel.channel_id); + }, [channel.channel_id, onClick], ); @@ -34,7 +40,12 @@ function Channel({ channel, onClick, status }: ChannelProps) { return ( // anchor instead of button due to Firefox having an issue with button padding in subgrid. - + {statusElement} {channel.name} {channel.description} From e86c77a356b15218fc2204530f09bdc7402de066 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Apr 2024 16:14:28 +0800 Subject: [PATCH 68/69] Update translations from crowdin --- resources/lang/ar/artist.php | 6 ++ resources/lang/ar/chat.php | 6 ++ resources/lang/ar/common.php | 1 + resources/lang/ar/page_title.php | 3 + resources/lang/ar/user_cover_presets.php | 37 ++++++++++++ resources/lang/ar/users.php | 1 + resources/lang/be/artist.php | 6 ++ resources/lang/be/chat.php | 6 ++ resources/lang/be/common.php | 1 + resources/lang/be/page_title.php | 3 + resources/lang/be/user_cover_presets.php | 37 ++++++++++++ resources/lang/be/users.php | 1 + resources/lang/bg/artist.php | 6 ++ resources/lang/bg/chat.php | 6 ++ resources/lang/bg/common.php | 1 + resources/lang/bg/page_title.php | 3 + resources/lang/bg/user_cover_presets.php | 37 ++++++++++++ resources/lang/bg/users.php | 1 + resources/lang/ca/artist.php | 6 ++ resources/lang/ca/chat.php | 6 ++ resources/lang/ca/common.php | 1 + resources/lang/ca/page_title.php | 3 + resources/lang/ca/user_cover_presets.php | 37 ++++++++++++ resources/lang/ca/users.php | 1 + resources/lang/cs/artist.php | 6 ++ resources/lang/cs/chat.php | 6 ++ resources/lang/cs/common.php | 1 + resources/lang/cs/page_title.php | 3 + resources/lang/cs/user_cover_presets.php | 37 ++++++++++++ resources/lang/cs/users.php | 1 + resources/lang/da/artist.php | 6 ++ resources/lang/da/chat.php | 6 ++ resources/lang/da/common.php | 1 + resources/lang/da/page_title.php | 3 + resources/lang/da/user_cover_presets.php | 37 ++++++++++++ resources/lang/da/users.php | 1 + resources/lang/de/artist.php | 6 ++ resources/lang/de/chat.php | 6 ++ resources/lang/de/common.php | 1 + resources/lang/de/page_title.php | 3 + resources/lang/de/user_cover_presets.php | 37 ++++++++++++ resources/lang/de/users.php | 1 + resources/lang/el/artist.php | 6 ++ resources/lang/el/chat.php | 6 ++ resources/lang/el/common.php | 1 + resources/lang/el/page_title.php | 3 + resources/lang/el/user_cover_presets.php | 37 ++++++++++++ resources/lang/el/users.php | 1 + resources/lang/es/artist.php | 6 ++ resources/lang/es/chat.php | 6 ++ resources/lang/es/common.php | 1 + resources/lang/es/events.php | 2 +- resources/lang/es/home.php | 4 +- resources/lang/es/page_title.php | 3 + resources/lang/es/user_cover_presets.php | 37 ++++++++++++ resources/lang/es/users.php | 1 + resources/lang/fa-IR/artist.php | 6 ++ resources/lang/fa-IR/chat.php | 6 ++ resources/lang/fa-IR/common.php | 1 + resources/lang/fa-IR/page_title.php | 3 + resources/lang/fa-IR/user_cover_presets.php | 37 ++++++++++++ resources/lang/fa-IR/users.php | 1 + resources/lang/fi/artist.php | 6 ++ resources/lang/fi/chat.php | 6 ++ resources/lang/fi/common.php | 1 + resources/lang/fi/community.php | 2 +- resources/lang/fi/forum.php | 2 +- resources/lang/fi/page_title.php | 3 + resources/lang/fi/score_tokens.php | 2 +- resources/lang/fi/user_cover_presets.php | 37 ++++++++++++ resources/lang/fi/users.php | 5 +- resources/lang/fil/artist.php | 6 ++ resources/lang/fil/chat.php | 6 ++ resources/lang/fil/common.php | 1 + resources/lang/fil/page_title.php | 3 + resources/lang/fil/user_cover_presets.php | 37 ++++++++++++ resources/lang/fil/users.php | 1 + resources/lang/fr/artist.php | 6 ++ resources/lang/fr/chat.php | 6 ++ resources/lang/fr/common.php | 1 + resources/lang/fr/page_title.php | 3 + resources/lang/fr/user_cover_presets.php | 37 ++++++++++++ resources/lang/fr/users.php | 1 + resources/lang/he/artist.php | 6 ++ resources/lang/he/chat.php | 6 ++ resources/lang/he/common.php | 1 + resources/lang/he/page_title.php | 3 + resources/lang/he/user_cover_presets.php | 37 ++++++++++++ resources/lang/he/users.php | 1 + resources/lang/hr-HR/artist.php | 6 ++ resources/lang/hr-HR/chat.php | 6 ++ resources/lang/hr-HR/common.php | 1 + resources/lang/hr-HR/page_title.php | 3 + resources/lang/hr-HR/user_cover_presets.php | 37 ++++++++++++ resources/lang/hr-HR/users.php | 1 + resources/lang/hu/artist.php | 6 ++ resources/lang/hu/chat.php | 6 ++ resources/lang/hu/common.php | 1 + resources/lang/hu/page_title.php | 3 + resources/lang/hu/user_cover_presets.php | 37 ++++++++++++ resources/lang/hu/users.php | 1 + resources/lang/id/accounts.php | 2 +- resources/lang/id/artist.php | 6 ++ resources/lang/id/authorization.php | 24 ++++---- resources/lang/id/beatmaps.php | 16 +++--- resources/lang/id/chat.php | 6 ++ resources/lang/id/common.php | 1 + resources/lang/id/forum.php | 36 ++++++------ resources/lang/id/layout.php | 6 +- resources/lang/id/model_validation.php | 4 +- resources/lang/id/page_title.php | 3 + resources/lang/id/store.php | 12 ++-- resources/lang/id/user_cover_presets.php | 37 ++++++++++++ resources/lang/id/users.php | 5 +- resources/lang/it/artist.php | 6 ++ resources/lang/it/chat.php | 6 ++ resources/lang/it/common.php | 1 + resources/lang/it/page_title.php | 3 + resources/lang/it/user_cover_presets.php | 37 ++++++++++++ resources/lang/it/users.php | 1 + resources/lang/ja/artist.php | 6 ++ resources/lang/ja/chat.php | 6 ++ resources/lang/ja/common.php | 1 + resources/lang/ja/page_title.php | 3 + resources/lang/ja/user_cover_presets.php | 37 ++++++++++++ resources/lang/ja/users.php | 1 + resources/lang/kk-KZ/artist.php | 6 ++ resources/lang/kk-KZ/chat.php | 6 ++ resources/lang/kk-KZ/common.php | 1 + resources/lang/kk-KZ/page_title.php | 3 + resources/lang/kk-KZ/user_cover_presets.php | 37 ++++++++++++ resources/lang/kk-KZ/users.php | 1 + resources/lang/ko/artist.php | 6 ++ resources/lang/ko/chat.php | 6 ++ resources/lang/ko/common.php | 1 + resources/lang/ko/page_title.php | 3 + resources/lang/ko/user_cover_presets.php | 37 ++++++++++++ resources/lang/ko/users.php | 1 + resources/lang/lt/artist.php | 6 ++ resources/lang/lt/chat.php | 6 ++ resources/lang/lt/common.php | 1 + resources/lang/lt/page_title.php | 3 + resources/lang/lt/user_cover_presets.php | 37 ++++++++++++ resources/lang/lt/users.php | 1 + resources/lang/lv-LV/artist.php | 6 ++ resources/lang/lv-LV/chat.php | 6 ++ resources/lang/lv-LV/common.php | 1 + resources/lang/lv-LV/page_title.php | 3 + resources/lang/lv-LV/user_cover_presets.php | 37 ++++++++++++ resources/lang/lv-LV/users.php | 1 + resources/lang/ms-MY/artist.php | 6 ++ resources/lang/ms-MY/chat.php | 6 ++ resources/lang/ms-MY/common.php | 1 + resources/lang/ms-MY/page_title.php | 3 + resources/lang/ms-MY/user_cover_presets.php | 37 ++++++++++++ resources/lang/ms-MY/users.php | 1 + resources/lang/nl/artist.php | 6 ++ resources/lang/nl/chat.php | 6 ++ resources/lang/nl/common.php | 1 + resources/lang/nl/page_title.php | 3 + resources/lang/nl/user_cover_presets.php | 37 ++++++++++++ resources/lang/nl/users.php | 1 + resources/lang/no/artist.php | 6 ++ resources/lang/no/chat.php | 6 ++ resources/lang/no/common.php | 1 + resources/lang/no/page_title.php | 3 + resources/lang/no/user_cover_presets.php | 37 ++++++++++++ resources/lang/no/users.php | 1 + resources/lang/pl/artist.php | 6 ++ resources/lang/pl/chat.php | 6 ++ resources/lang/pl/common.php | 1 + resources/lang/pl/page_title.php | 3 + resources/lang/pl/score_tokens.php | 2 +- resources/lang/pl/user_cover_presets.php | 37 ++++++++++++ resources/lang/pl/users.php | 1 + resources/lang/pt-br/artist.php | 6 ++ resources/lang/pt-br/chat.php | 6 ++ resources/lang/pt-br/common.php | 1 + resources/lang/pt-br/page_title.php | 3 + resources/lang/pt-br/user_cover_presets.php | 37 ++++++++++++ resources/lang/pt-br/users.php | 1 + resources/lang/pt/artist.php | 6 ++ resources/lang/pt/chat.php | 6 ++ resources/lang/pt/common.php | 1 + resources/lang/pt/page_title.php | 3 + resources/lang/pt/user_cover_presets.php | 37 ++++++++++++ resources/lang/pt/users.php | 1 + resources/lang/ro/artist.php | 6 ++ resources/lang/ro/authorization.php | 4 +- resources/lang/ro/chat.php | 6 ++ resources/lang/ro/common.php | 1 + resources/lang/ro/forum.php | 2 +- resources/lang/ro/page_title.php | 3 + resources/lang/ro/user_cover_presets.php | 37 ++++++++++++ resources/lang/ro/users.php | 7 ++- resources/lang/ru/artist.php | 6 ++ resources/lang/ru/chat.php | 6 ++ resources/lang/ru/common.php | 1 + resources/lang/ru/page_title.php | 3 + resources/lang/ru/user_cover_presets.php | 37 ++++++++++++ resources/lang/ru/users.php | 1 + resources/lang/si-LK/artist.php | 6 ++ resources/lang/si-LK/chat.php | 6 ++ resources/lang/si-LK/common.php | 1 + resources/lang/si-LK/page_title.php | 3 + resources/lang/si-LK/user_cover_presets.php | 37 ++++++++++++ resources/lang/si-LK/users.php | 1 + resources/lang/sk/artist.php | 6 ++ resources/lang/sk/chat.php | 6 ++ resources/lang/sk/common.php | 1 + resources/lang/sk/page_title.php | 3 + resources/lang/sk/user_cover_presets.php | 37 ++++++++++++ resources/lang/sk/users.php | 1 + resources/lang/sl/artist.php | 6 ++ resources/lang/sl/chat.php | 6 ++ resources/lang/sl/common.php | 1 + resources/lang/sl/page_title.php | 3 + resources/lang/sl/user_cover_presets.php | 37 ++++++++++++ resources/lang/sl/users.php | 1 + resources/lang/sr/artist.php | 6 ++ resources/lang/sr/chat.php | 6 ++ resources/lang/sr/common.php | 1 + resources/lang/sr/page_title.php | 3 + resources/lang/sr/user_cover_presets.php | 37 ++++++++++++ resources/lang/sr/users.php | 1 + resources/lang/sv/accounts.php | 2 +- resources/lang/sv/artist.php | 6 ++ resources/lang/sv/authorization.php | 4 +- resources/lang/sv/beatmaps.php | 2 +- resources/lang/sv/chat.php | 6 ++ resources/lang/sv/comments.php | 2 +- resources/lang/sv/common.php | 1 + resources/lang/sv/contest.php | 24 ++++---- resources/lang/sv/page_title.php | 3 + resources/lang/sv/password_reset.php | 2 +- resources/lang/sv/scores.php | 12 ++-- resources/lang/sv/store.php | 62 ++++++++++----------- resources/lang/sv/user_cover_presets.php | 37 ++++++++++++ resources/lang/sv/users.php | 5 +- resources/lang/sv/wiki.php | 6 +- resources/lang/tg-TJ/artist.php | 6 ++ resources/lang/tg-TJ/chat.php | 6 ++ resources/lang/tg-TJ/common.php | 1 + resources/lang/tg-TJ/page_title.php | 3 + resources/lang/tg-TJ/user_cover_presets.php | 37 ++++++++++++ resources/lang/tg-TJ/users.php | 1 + resources/lang/th/artist.php | 6 ++ resources/lang/th/chat.php | 6 ++ resources/lang/th/common.php | 1 + resources/lang/th/page_title.php | 3 + resources/lang/th/user_cover_presets.php | 37 ++++++++++++ resources/lang/th/users.php | 1 + resources/lang/tr/artist.php | 6 ++ resources/lang/tr/chat.php | 6 ++ resources/lang/tr/common.php | 1 + resources/lang/tr/page_title.php | 3 + resources/lang/tr/user_cover_presets.php | 37 ++++++++++++ resources/lang/tr/users.php | 1 + resources/lang/uk/artist.php | 6 ++ resources/lang/uk/chat.php | 6 ++ resources/lang/uk/common.php | 1 + resources/lang/uk/page_title.php | 3 + resources/lang/uk/user_cover_presets.php | 37 ++++++++++++ resources/lang/uk/users.php | 1 + resources/lang/vi/artist.php | 6 ++ resources/lang/vi/chat.php | 6 ++ resources/lang/vi/common.php | 1 + resources/lang/vi/page_title.php | 3 + resources/lang/vi/user_cover_presets.php | 37 ++++++++++++ resources/lang/vi/users.php | 1 + resources/lang/zh-tw/artist.php | 6 ++ resources/lang/zh-tw/chat.php | 6 ++ resources/lang/zh-tw/common.php | 1 + resources/lang/zh-tw/page_title.php | 3 + resources/lang/zh-tw/user_cover_presets.php | 37 ++++++++++++ resources/lang/zh-tw/users.php | 1 + resources/lang/zh/artist.php | 6 ++ resources/lang/zh/beatmaps.php | 2 +- resources/lang/zh/chat.php | 6 ++ resources/lang/zh/common.php | 1 + resources/lang/zh/model_validation.php | 4 +- resources/lang/zh/page_title.php | 3 + resources/lang/zh/user_cover_presets.php | 37 ++++++++++++ resources/lang/zh/users.php | 3 +- 284 files changed, 2453 insertions(+), 131 deletions(-) create mode 100644 resources/lang/ar/user_cover_presets.php create mode 100644 resources/lang/be/user_cover_presets.php create mode 100644 resources/lang/bg/user_cover_presets.php create mode 100644 resources/lang/ca/user_cover_presets.php create mode 100644 resources/lang/cs/user_cover_presets.php create mode 100644 resources/lang/da/user_cover_presets.php create mode 100644 resources/lang/de/user_cover_presets.php create mode 100644 resources/lang/el/user_cover_presets.php create mode 100644 resources/lang/es/user_cover_presets.php create mode 100644 resources/lang/fa-IR/user_cover_presets.php create mode 100644 resources/lang/fi/user_cover_presets.php create mode 100644 resources/lang/fil/user_cover_presets.php create mode 100644 resources/lang/fr/user_cover_presets.php create mode 100644 resources/lang/he/user_cover_presets.php create mode 100644 resources/lang/hr-HR/user_cover_presets.php create mode 100644 resources/lang/hu/user_cover_presets.php create mode 100644 resources/lang/id/user_cover_presets.php create mode 100644 resources/lang/it/user_cover_presets.php create mode 100644 resources/lang/ja/user_cover_presets.php create mode 100644 resources/lang/kk-KZ/user_cover_presets.php create mode 100644 resources/lang/ko/user_cover_presets.php create mode 100644 resources/lang/lt/user_cover_presets.php create mode 100644 resources/lang/lv-LV/user_cover_presets.php create mode 100644 resources/lang/ms-MY/user_cover_presets.php create mode 100644 resources/lang/nl/user_cover_presets.php create mode 100644 resources/lang/no/user_cover_presets.php create mode 100644 resources/lang/pl/user_cover_presets.php create mode 100644 resources/lang/pt-br/user_cover_presets.php create mode 100644 resources/lang/pt/user_cover_presets.php create mode 100644 resources/lang/ro/user_cover_presets.php create mode 100644 resources/lang/ru/user_cover_presets.php create mode 100644 resources/lang/si-LK/user_cover_presets.php create mode 100644 resources/lang/sk/user_cover_presets.php create mode 100644 resources/lang/sl/user_cover_presets.php create mode 100644 resources/lang/sr/user_cover_presets.php create mode 100644 resources/lang/sv/user_cover_presets.php create mode 100644 resources/lang/tg-TJ/user_cover_presets.php create mode 100644 resources/lang/th/user_cover_presets.php create mode 100644 resources/lang/tr/user_cover_presets.php create mode 100644 resources/lang/uk/user_cover_presets.php create mode 100644 resources/lang/vi/user_cover_presets.php create mode 100644 resources/lang/zh-tw/user_cover_presets.php create mode 100644 resources/lang/zh/user_cover_presets.php diff --git a/resources/lang/ar/artist.php b/resources/lang/ar/artist.php index abc1e4a4c0c..aedcde0253a 100644 --- a/resources/lang/ar/artist.php +++ b/resources/lang/ar/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'البحث عن الإغاني', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'بحث متقدم', 'album' => 'ألبوم', @@ -52,6 +57,7 @@ 'bpm_gte' => 'الحد الأدنى لـ BPM', 'bpm_lte' => 'الحد الأقصى لـ BPM', 'empty' => 'لم تم العثور على أي اغاني تطابق معايير البحث.', + 'exclusive_only' => '', 'genre' => 'الصنف', 'genre_all' => 'الكل', 'length_gte' => 'ادنى للطول', diff --git a/resources/lang/ar/chat.php b/resources/lang/ar/chat.php index 8fb256cc7d2..a1384f0bf22 100644 --- a/resources/lang/ar/chat.php +++ b/resources/lang/ar/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'هل تريد إخفاء هذه القناة؟ ستظل تتلقى رسائل من هذه القناة.', 'create' => 'إنشاء إعلان', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'القنوات العامة التي تنضم اليها عن طريق osu!lazer سوف تكون مرئية هنا ايضا.', 'title' => 'لا محادثات بعد', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/ar/common.php b/resources/lang/ar/common.php index 6d559d12467..3f02fbbc39d 100644 --- a/resources/lang/ar/common.php +++ b/resources/lang/ar/common.php @@ -39,6 +39,7 @@ 'pin' => 'ثبّت', 'post' => 'نشر', 'read_more' => 'إقرأ المزيد', + 'refresh' => '', 'reply' => 'رد', 'reply_reopen' => 'الرد وإعادة فتح', 'reply_resolve' => 'الرد والاِصلاح', diff --git a/resources/lang/ar/page_title.php b/resources/lang/ar/page_title.php index 1641288855e..a5f382fb63c 100644 --- a/resources/lang/ar/page_title.php +++ b/resources/lang/ar/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'البطولات', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'معلومات اللاعب', 'create' => 'إنشاء حساب', diff --git a/resources/lang/ar/user_cover_presets.php b/resources/lang/ar/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/ar/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/ar/users.php b/resources/lang/ar/users.php index 0a29bc8f12c..b935dfdc07c 100644 --- a/resources/lang/ar/users.php +++ b/resources/lang/ar/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "لم تستعمل حسابك منذ وقتِِ طويل.", ], ], diff --git a/resources/lang/be/artist.php b/resources/lang/be/artist.php index 181717d3f93..2bab42bf382 100644 --- a/resources/lang/be/artist.php +++ b/resources/lang/be/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'пошук трэкаў', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Пашыраны пошук', 'album' => 'Альбом', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Мінімум', 'bpm_lte' => 'BPM Максімум', 'empty' => 'Не знойдзена трэкаў, супадаючых з гэтымі крытэрыямі пошука.', + 'exclusive_only' => '', 'genre' => 'Жанр', 'genre_all' => 'Усё', 'length_gte' => 'Мінімальная даўжыня', diff --git a/resources/lang/be/chat.php b/resources/lang/be/chat.php index c1c6aacae7c..b5aa6af506f 100644 --- a/resources/lang/be/chat.php +++ b/resources/lang/be/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Вы сапраўды хочаце схаваць гэты канал? Вы ўсё яшчэ будзеце атрымліваць паведамленні з яго.', 'create' => 'стварыць аб\'яву', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Публічныя каналы, да якіх вы далучыліся праз osu!lazer будуць бачныя тут.', 'title' => 'яшчэ няма размоў', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/be/common.php b/resources/lang/be/common.php index 50c24dd248f..28bbcbbc89c 100644 --- a/resources/lang/be/common.php +++ b/resources/lang/be/common.php @@ -39,6 +39,7 @@ 'pin' => 'замацаваць', 'post' => 'Размясціць', 'read_more' => 'чытаць далей', + 'refresh' => '', 'reply' => 'Адказаць', 'reply_reopen' => 'Адказаць і пераадкрыць', 'reply_resolve' => 'Адказаць і вырашыць', diff --git a/resources/lang/be/page_title.php b/resources/lang/be/page_title.php index 6a386f10fb7..bb473617259 100644 --- a/resources/lang/be/page_title.php +++ b/resources/lang/be/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'турніры', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'інфармацыя пра гульца', 'create' => 'стварыць акаўнт', diff --git a/resources/lang/be/user_cover_presets.php b/resources/lang/be/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/be/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/be/users.php b/resources/lang/be/users.php index 1ec647b4241..4ffa3e6e46b 100644 --- a/resources/lang/be/users.php +++ b/resources/lang/be/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Ваш акаўнт не выкарыстоўваўся доўгі час.", ], ], diff --git a/resources/lang/bg/artist.php b/resources/lang/bg/artist.php index 04a5947370a..3eb5451d4b9 100644 --- a/resources/lang/bg/artist.php +++ b/resources/lang/bg/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'търсене на песен', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Разширено търсене', 'album' => 'Албум', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Минимален BPM', 'bpm_lte' => 'Максимален BPM', 'empty' => 'Не са намерени резултати по критерия за търсене.', + 'exclusive_only' => '', 'genre' => 'Жанр', 'genre_all' => 'Всеки', 'length_gte' => 'Минимална продължителност', diff --git a/resources/lang/bg/chat.php b/resources/lang/bg/chat.php index f49d0eac146..e2d9539e8c2 100644 --- a/resources/lang/bg/chat.php +++ b/resources/lang/bg/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Искате ли да скриете този канал? Ще продължите да получавате съобщения от канала.', 'create' => 'създаване на оповестяване', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Публичните канали към които сте се присъединили чрез osu!lazer ще са видими и тук.', 'title' => 'няма налични дискусии', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/bg/common.php b/resources/lang/bg/common.php index ef2237a2c66..7872ac7d0e0 100644 --- a/resources/lang/bg/common.php +++ b/resources/lang/bg/common.php @@ -39,6 +39,7 @@ 'pin' => 'закачи', 'post' => 'Публикуване', 'read_more' => 'прочети повече', + 'refresh' => '', 'reply' => 'Отговор', 'reply_reopen' => 'Отговор и отваряне отново', 'reply_resolve' => 'Отговор и разрешаване', diff --git a/resources/lang/bg/page_title.php b/resources/lang/bg/page_title.php index 2c4beb22a47..8e5a079a104 100644 --- a/resources/lang/bg/page_title.php +++ b/resources/lang/bg/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'турнири', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'инфо за играч', 'create' => 'създаване на профил', diff --git a/resources/lang/bg/user_cover_presets.php b/resources/lang/bg/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/bg/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/bg/users.php b/resources/lang/bg/users.php index 59601d8b0c6..6dd5b9a7b9a 100644 --- a/resources/lang/bg/users.php +++ b/resources/lang/bg/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Вашият акаунт не е ползван от дълго време.", ], ], diff --git a/resources/lang/ca/artist.php b/resources/lang/ca/artist.php index 2da2a6e4c44..f12b6692d51 100644 --- a/resources/lang/ca/artist.php +++ b/resources/lang/ca/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'cerca de pistes', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Cerca avançada', 'album' => 'Àlbum', @@ -52,6 +57,7 @@ 'bpm_gte' => 'PPM mínim', 'bpm_lte' => 'PPM màxim', 'empty' => 'No s\'ha trobat cap pista que coincideixi amb els criteris de cerca.', + 'exclusive_only' => '', 'genre' => 'Gènere', 'genre_all' => 'Tots', 'length_gte' => 'Durada mínima', diff --git a/resources/lang/ca/chat.php b/resources/lang/ca/chat.php index afb554bc4d5..b08f4ff3d4f 100644 --- a/resources/lang/ca/chat.php +++ b/resources/lang/ca/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Vols amagar aquest canal? Encara rebràs missatges d\'aquest canal.', 'create' => 'crear un anunci', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Els canals públics als quals t\'uneixis per osu!lazer també seran visibles aquí.', 'title' => 'encara no tens conversacions', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/ca/common.php b/resources/lang/ca/common.php index c1d4a54cf7f..f200383d895 100644 --- a/resources/lang/ca/common.php +++ b/resources/lang/ca/common.php @@ -39,6 +39,7 @@ 'pin' => 'fixar', 'post' => 'Publica', 'read_more' => 'llegir més', + 'refresh' => '', 'reply' => 'Respondre', 'reply_reopen' => 'Respondre i reobrir', 'reply_resolve' => 'Respondre i resoldre', diff --git a/resources/lang/ca/page_title.php b/resources/lang/ca/page_title.php index e8edd29345f..63dc8fdb25c 100644 --- a/resources/lang/ca/page_title.php +++ b/resources/lang/ca/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'tornejos', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informació del jugador', 'create' => 'crear un compte', diff --git a/resources/lang/ca/user_cover_presets.php b/resources/lang/ca/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/ca/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/ca/users.php b/resources/lang/ca/users.php index 999ced7fbb9..5e855392771 100644 --- a/resources/lang/ca/users.php +++ b/resources/lang/ca/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "El teu compte no s'ha utilitzat durant molt de temps.", ], ], diff --git a/resources/lang/cs/artist.php b/resources/lang/cs/artist.php index 8fea9aa1f29..9005ff43699 100644 --- a/resources/lang/cs/artist.php +++ b/resources/lang/cs/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'hledání skladeb', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Pokročilé vyhledávání', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimální BPM', 'bpm_lte' => 'Maximální BPM', 'empty' => 'Nebyly nalezeny žádné skladby odpovídající kritériím vyhledávání.', + 'exclusive_only' => '', 'genre' => 'Žánr', 'genre_all' => 'Vše', 'length_gte' => 'Minimální délka', diff --git a/resources/lang/cs/chat.php b/resources/lang/cs/chat.php index 904abce90e3..b1127aa4200 100644 --- a/resources/lang/cs/chat.php +++ b/resources/lang/cs/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Chceš skrýt tento kanál? Stále budeš dostávat zprávy z tohoto kanálu.', 'create' => 'vytvořit oznámení', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Veřejné místnosti, do kterých se připojíte skrz osu!lazer, zde budete moct taky vidět.', 'title' => 'zatím žádné konverzace', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/cs/common.php b/resources/lang/cs/common.php index c9bfe601098..613e354703d 100644 --- a/resources/lang/cs/common.php +++ b/resources/lang/cs/common.php @@ -39,6 +39,7 @@ 'pin' => 'připnout', 'post' => 'Přidat příspěvek', 'read_more' => 'číst více', + 'refresh' => '', 'reply' => 'Odpovědět', 'reply_reopen' => 'Odpovědět a znovu otevřít', 'reply_resolve' => 'Odpovědět a archivovat', diff --git a/resources/lang/cs/page_title.php b/resources/lang/cs/page_title.php index cd0d40f91a6..fb9855806a4 100644 --- a/resources/lang/cs/page_title.php +++ b/resources/lang/cs/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnaje', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informace o hráči', 'create' => 'vytvořit účet', diff --git a/resources/lang/cs/user_cover_presets.php b/resources/lang/cs/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/cs/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/cs/users.php b/resources/lang/cs/users.php index 4a857466caf..46949137a0e 100644 --- a/resources/lang/cs/users.php +++ b/resources/lang/cs/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Váš účet nebyl využíván dlouhou dobu.", ], ], diff --git a/resources/lang/da/artist.php b/resources/lang/da/artist.php index fcc47382821..f2e560e703a 100644 --- a/resources/lang/da/artist.php +++ b/resources/lang/da/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'spor søgning', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Avanceret søgning', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimum BPM', 'bpm_lte' => 'Maksimum BPM', 'empty' => 'Ingen spor som matcher søgekriterier blev fundet.', + 'exclusive_only' => '', 'genre' => 'Genre', 'genre_all' => 'Alle', 'length_gte' => 'Minimum Længde', diff --git a/resources/lang/da/chat.php b/resources/lang/da/chat.php index b188669773f..9e8d1e85243 100644 --- a/resources/lang/da/chat.php +++ b/resources/lang/da/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Vil du skjule denne kanal? Du vil stadig modtage beskeder fra denne kanal.', 'create' => 'opret annoncering', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Offentlige kanaler du joiner via osu!lazer vil også vises her.', 'title' => 'ingen samtaler "endnu"', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/da/common.php b/resources/lang/da/common.php index 0599b0e0835..c92f50bf2c9 100644 --- a/resources/lang/da/common.php +++ b/resources/lang/da/common.php @@ -39,6 +39,7 @@ 'pin' => 'fastgør', 'post' => 'Slå op', 'read_more' => 'læs mere', + 'refresh' => '', 'reply' => 'Svar', 'reply_reopen' => 'Svar og Genåben', 'reply_resolve' => 'Svar og Markér som Løst', diff --git a/resources/lang/da/page_title.php b/resources/lang/da/page_title.php index a32f40fb29f..7a6ab523111 100644 --- a/resources/lang/da/page_title.php +++ b/resources/lang/da/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turneringer', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'spiller info', 'create' => 'opret konto', diff --git a/resources/lang/da/user_cover_presets.php b/resources/lang/da/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/da/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/da/users.php b/resources/lang/da/users.php index d4254a94c12..742ef1b0cb1 100644 --- a/resources/lang/da/users.php +++ b/resources/lang/da/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Din account har ikke været i brug i lang tid.", ], ], diff --git a/resources/lang/de/artist.php b/resources/lang/de/artist.php index bdea9ac254a..6320d7eee19 100644 --- a/resources/lang/de/artist.php +++ b/resources/lang/de/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'Liedersuche', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Erweiterte Suche', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Mindest-BPM', 'bpm_lte' => 'Maximal-BPM', 'empty' => 'Keine Lieder mit den gegebenen Suchkriterien gefunden.', + 'exclusive_only' => '', 'genre' => 'Genre', 'genre_all' => 'Alle', 'length_gte' => 'Mindestlänge', diff --git a/resources/lang/de/chat.php b/resources/lang/de/chat.php index 90447a7baed..027463fd7fa 100644 --- a/resources/lang/de/chat.php +++ b/resources/lang/de/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Möchtest du diesen Channel ausblenden? Du erhältst weiterhin Nachrichten aus diesem Channel.', 'create' => 'Ankündigung erstellen', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Öffentliche Channel, welche du mit osu!lazer betrittst, werden hier auch sichtbar sein.', 'title' => 'noch keine Unterhaltungen', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/de/common.php b/resources/lang/de/common.php index 5a62cec5801..d9deca09108 100644 --- a/resources/lang/de/common.php +++ b/resources/lang/de/common.php @@ -39,6 +39,7 @@ 'pin' => 'anheften', 'post' => 'Senden', 'read_more' => 'mehr anzeigen', + 'refresh' => '', 'reply' => 'Antworten', 'reply_reopen' => 'Antworten und wiedereröffnen', 'reply_resolve' => 'Antworten und lösen', diff --git a/resources/lang/de/page_title.php b/resources/lang/de/page_title.php index 3ac2dbebe5c..001944510c0 100644 --- a/resources/lang/de/page_title.php +++ b/resources/lang/de/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'Turniere', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'Spielerinfo', 'create' => 'account erstellen', diff --git a/resources/lang/de/user_cover_presets.php b/resources/lang/de/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/de/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/de/users.php b/resources/lang/de/users.php index c58eb98839d..93702974ec0 100644 --- a/resources/lang/de/users.php +++ b/resources/lang/de/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Dein Konto wurde längere Zeit nicht benutzt.", ], ], diff --git a/resources/lang/el/artist.php b/resources/lang/el/artist.php index d1c7c2d7375..3a999826efb 100644 --- a/resources/lang/el/artist.php +++ b/resources/lang/el/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'αναζήτηση κομματιών', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Σύνθετη Αναζήτηση', 'album' => 'Άλμπουμ', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Ελάχιστο BPM', 'bpm_lte' => 'Μέγιστο BPM', 'empty' => 'Δεν βρέθηκαν κομμάτια που να ταιριάζουν με τα κριτήρια αναζήτησης.', + 'exclusive_only' => '', 'genre' => 'Είδος', 'genre_all' => 'Όλα', 'length_gte' => 'Ελάχιστο Μήκος', diff --git a/resources/lang/el/chat.php b/resources/lang/el/chat.php index aa24e54d830..ac959b8592a 100644 --- a/resources/lang/el/chat.php +++ b/resources/lang/el/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Θέλετε να αποκρύψετε αυτό το κανάλι? Θα εξακολουθείτε να λαμβάνετε μηνύματα από αυτό το κανάλι.', 'create' => 'δημιουργία ανακοίνωσης', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Δημόσια κανάλια που συνδέεστε μέσω του osu!lazer θα επίσης εμφανίζονται εδώ.', 'title' => 'δεν υπάρχουν συνομιλίες', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/el/common.php b/resources/lang/el/common.php index 8775dede385..a5540882c78 100644 --- a/resources/lang/el/common.php +++ b/resources/lang/el/common.php @@ -39,6 +39,7 @@ 'pin' => 'καρφίτσωμα', 'post' => 'Δημοσίευση', 'read_more' => 'διαβάστε περισσότερα', + 'refresh' => '', 'reply' => 'Απάντηση', 'reply_reopen' => 'Απάντηση και εκ νέου άνοιγμα', 'reply_resolve' => 'Απάντηση και Αρχειοθέτηση', diff --git a/resources/lang/el/page_title.php b/resources/lang/el/page_title.php index e54dd66957a..2bcf74402c5 100644 --- a/resources/lang/el/page_title.php +++ b/resources/lang/el/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'πρωταθλήματα', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'πληροφορίες παίκτη', 'create' => 'δημιούργησε λογαριασμό', diff --git a/resources/lang/el/user_cover_presets.php b/resources/lang/el/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/el/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/el/users.php b/resources/lang/el/users.php index a044ad374ef..a6178b29e22 100644 --- a/resources/lang/el/users.php +++ b/resources/lang/el/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Ο λογαριασμός σας δεν έχει χρησιμοποιηθεί εδώ και πολύ καιρό.", ], ], diff --git a/resources/lang/es/artist.php b/resources/lang/es/artist.php index ba2a517085c..26aeaac06c1 100644 --- a/resources/lang/es/artist.php +++ b/resources/lang/es/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'búsqueda de pistas', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Búsqueda avanzada', 'album' => 'Álbum', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM mínimo', 'bpm_lte' => 'BPM máximo', 'empty' => 'No se encontraron pistas que coincidieran con ese criterio de búsqueda.', + 'exclusive_only' => '', 'genre' => 'Género', 'genre_all' => 'Todos', 'length_gte' => 'Duración mínima', diff --git a/resources/lang/es/chat.php b/resources/lang/es/chat.php index 3403e587f87..9dd396192e6 100644 --- a/resources/lang/es/chat.php +++ b/resources/lang/es/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '¿Quieres ocultar este canal? Seguirás recibiendo mensajes de este canal.', 'create' => 'crear anuncio', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Los canales públicos a los que se unas a través de osu!lazer también serán visibles aquí.', 'title' => 'aún no hay conversaciones', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/es/common.php b/resources/lang/es/common.php index 0a2b8b4dab2..3e8dc8a748d 100644 --- a/resources/lang/es/common.php +++ b/resources/lang/es/common.php @@ -39,6 +39,7 @@ 'pin' => 'anclar', 'post' => 'Publicar', 'read_more' => 'leer más', + 'refresh' => '', 'reply' => 'Responder', 'reply_reopen' => 'Responder y reabrir', 'reply_resolve' => 'Responder y resolver', diff --git a/resources/lang/es/events.php b/resources/lang/es/events.php index 9ee704c33c7..cb26b81908b 100644 --- a/resources/lang/es/events.php +++ b/resources/lang/es/events.php @@ -4,7 +4,7 @@ // See the LICENCE file in the repository root for full licence text. return [ - 'achievement' => '¡:user ha desbloqueado el logro «:achievement»!', + 'achievement' => '¡:user ha desbloqueado la medalla «:achievement»!', 'beatmap_playcount' => '¡:beatmap ha sido jugado :count veces!', 'beatmapset_approve' => '¡:beatmapset por :user ha sido :approval!', 'beatmapset_delete' => ':beatmapset ha sido eliminado.', diff --git a/resources/lang/es/home.php b/resources/lang/es/home.php index 1283d6543cd..5b16267e021 100644 --- a/resources/lang/es/home.php +++ b/resources/lang/es/home.php @@ -81,7 +81,7 @@ 'action_title' => 'descargar osu!', 'for_os' => 'para :os', 'macos-fallback' => 'usuarios de macOS', - 'mirror' => 'link alternativo', + 'mirror' => 'enlace alternativo', 'or' => 'o', 'os_version_or_later' => ':os_version o posterior', 'other_os' => 'otras plataformas', @@ -92,7 +92,7 @@ 'help' => [ '_' => 'si tienes problemas para iniciar el juego o para obtener una cuenta, :help_forum_link o :support_button.', 'help_forum_link' => 'consulta el foro de ayuda', - 'support_button' => 'contacta con soporte', + 'support_button' => 'contacta al soporte', ], 'os' => [ diff --git a/resources/lang/es/page_title.php b/resources/lang/es/page_title.php index 57f057d6d10..095bb3c9ec4 100644 --- a/resources/lang/es/page_title.php +++ b/resources/lang/es/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'torneos', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'información del jugador', 'create' => 'crear una cuenta', diff --git a/resources/lang/es/user_cover_presets.php b/resources/lang/es/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/es/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/es/users.php b/resources/lang/es/users.php index a7b62120a2c..f078fa1979f 100644 --- a/resources/lang/es/users.php +++ b/resources/lang/es/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Tu cuenta no ha sido usada en mucho tiempo.", ], ], diff --git a/resources/lang/fa-IR/artist.php b/resources/lang/fa-IR/artist.php index c6dcefeb9a5..10b81f48ec0 100644 --- a/resources/lang/fa-IR/artist.php +++ b/resources/lang/fa-IR/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'جستجوی قطعه ها', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'جستجوی پیشرفته', 'album' => 'آلبوم', @@ -52,6 +57,7 @@ 'bpm_gte' => 'کمترین ضرب در دقیقه', 'bpm_lte' => 'بیشترین ضرب در دقیقه', 'empty' => 'هیچ قطعه ای مطابق جستجوی شما پیدا نشد.', + 'exclusive_only' => '', 'genre' => 'ژانر', 'genre_all' => 'همه', 'length_gte' => 'کمترین طول', diff --git a/resources/lang/fa-IR/chat.php b/resources/lang/fa-IR/chat.php index 1015053cfda..3473e21f281 100644 --- a/resources/lang/fa-IR/chat.php +++ b/resources/lang/fa-IR/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '', 'create' => '', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'کانال های عمومی ای که از osu!lazer به آن ها بپیوندید هم اینجا قابل مشاهده خواهند بود.', 'title' => 'هنوز هیچ گفتگویی وجود ندارد', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/fa-IR/common.php b/resources/lang/fa-IR/common.php index 830a057485a..d7ce03f447f 100644 --- a/resources/lang/fa-IR/common.php +++ b/resources/lang/fa-IR/common.php @@ -39,6 +39,7 @@ 'pin' => '', 'post' => '', 'read_more' => '', + 'refresh' => '', 'reply' => '', 'reply_reopen' => '', 'reply_resolve' => '', diff --git a/resources/lang/fa-IR/page_title.php b/resources/lang/fa-IR/page_title.php index 63320de7de0..2a6fbe54c4f 100644 --- a/resources/lang/fa-IR/page_title.php +++ b/resources/lang/fa-IR/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '', 'create' => '', diff --git a/resources/lang/fa-IR/user_cover_presets.php b/resources/lang/fa-IR/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/fa-IR/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/fa-IR/users.php b/resources/lang/fa-IR/users.php index c0ea037eb7e..10600df8c5b 100644 --- a/resources/lang/fa-IR/users.php +++ b/resources/lang/fa-IR/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "حساب کاربری شما مدت هاست استفاده نشده است.", ], ], diff --git a/resources/lang/fi/artist.php b/resources/lang/fi/artist.php index 8d3627eb064..61bc49c49a9 100644 --- a/resources/lang/fi/artist.php +++ b/resources/lang/fi/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'kappalehaku', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Laajennettu haku', 'album' => 'Albumi', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM vähintään', 'bpm_lte' => 'BPM enintään', 'empty' => 'Hakukriteerejä vastaavia kappaleita ei löytynyt.', + 'exclusive_only' => '', 'genre' => 'Tyylilaji', 'genre_all' => 'Kaikki', 'length_gte' => 'Vähimmäispituus', diff --git a/resources/lang/fi/chat.php b/resources/lang/fi/chat.php index 64d68104e07..2f427cd38f0 100644 --- a/resources/lang/fi/chat.php +++ b/resources/lang/fi/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Haluatko piilottaa tämän kanavan? Tulet yhä saamaan viestejä tältä kanavalta.', 'create' => 'luo tiedote', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Julkiset kanavat joihin olet liittynyt osu!lazerilla näkyvät myös täällä.', 'title' => 'ei keskusteluja...', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/fi/common.php b/resources/lang/fi/common.php index 4dfa88bd7d5..b4fc0e0a3c5 100644 --- a/resources/lang/fi/common.php +++ b/resources/lang/fi/common.php @@ -39,6 +39,7 @@ 'pin' => 'kiinnitä', 'post' => 'Lähetä', 'read_more' => 'lue lisää', + 'refresh' => '', 'reply' => 'Vastaa', 'reply_reopen' => 'Vastaa ja avaa uudelleen', 'reply_resolve' => 'Vastaa ja merkitse ratkaistuksi', diff --git a/resources/lang/fi/community.php b/resources/lang/fi/community.php index 88a6cac3132..df7405c9aa1 100644 --- a/resources/lang/fi/community.php +++ b/resources/lang/fi/community.php @@ -20,7 +20,7 @@ ], 'infra' => [ 'title' => 'Palvelininfrastruktuuri', - 'description' => 'Osa avustuksista menee palvelimia varten ja niillä ylläpidetään verkkosivustoa, moninpelipalveluita, verkon pistetaulukoita jne.', + 'description' => 'Osa avustuksista menee palvelimia varten ja niillä ylläpidetään verkkosivustoa, moninpelipalveluita, verkon tulostauluja jne.', ], 'featured-artists' => [ 'title' => 'Esitellyt artistit', diff --git a/resources/lang/fi/forum.php b/resources/lang/fi/forum.php index bde985e136a..57a9c69234e 100644 --- a/resources/lang/fi/forum.php +++ b/resources/lang/fi/forum.php @@ -372,7 +372,7 @@ 'to_watching' => 'Kirjanmerkkeihin', 'to_watching_mail' => 'Kirjanmerkkeihin ilmoituksella', 'tooltip_mail_disable' => 'Ilmoitus on päällä. Klikkaa poistaaksesi käytöstä', - 'tooltip_mail_enable' => 'Ilmoitus on pois käytöstä. Klikkaa laittaaksesi päälle', + 'tooltip_mail_enable' => 'Ilmoitus on poissa käytöstä. Klikkaa laittaaksesi päälle', ], ], ]; diff --git a/resources/lang/fi/page_title.php b/resources/lang/fi/page_title.php index 512153195f2..51d77861912 100644 --- a/resources/lang/fi/page_title.php +++ b/resources/lang/fi/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnaukset', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'pelaajan tiedot', 'create' => 'luo tili', diff --git a/resources/lang/fi/score_tokens.php b/resources/lang/fi/score_tokens.php index 1351cc97f00..7e8aaa9e06e 100644 --- a/resources/lang/fi/score_tokens.php +++ b/resources/lang/fi/score_tokens.php @@ -6,6 +6,6 @@ return [ 'create' => [ 'beatmap_hash_invalid' => 'virheellinen tai puuttuva rytmikartan tiiviste (beatmap_hash)', - 'submission_disabled' => 'pisteiden lähetys on pois käytöstä', + 'submission_disabled' => 'pisteiden lähetys on poissa käytöstä', ], ]; diff --git a/resources/lang/fi/user_cover_presets.php b/resources/lang/fi/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/fi/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/fi/users.php b/resources/lang/fi/users.php index deda0b9b694..a0fb4b18721 100644 --- a/resources/lang/fi/users.php +++ b/resources/lang/fi/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Käyttäjätiliäsi ei ole käytetty pitkään aikaan.", ], ], @@ -420,8 +421,8 @@ 'website' => 'Verkkosivu', ], 'not_found' => [ - 'reason_1' => 'Käyttäjänimi saattaa olla vaihtunut.', - 'reason_2' => 'Käyttäjätunnus voi olla tilapäisesti pois käytöstä tietoturvasyistä tai väärinkäytön seurauksena.', + 'reason_1' => 'Hän on saattanut vaihtaa käyttäjänimensä.', + 'reason_2' => 'Käyttäjätunnus voi olla tilapäisesti poissa käytöstä turvallisuussyistä tai väärinkäytön seurauksena.', 'reason_3' => 'Teit mahdollisesti kirjoitusvirheen!', 'reason_header' => 'Tähän on muutama mahdollinen syy:', 'title' => 'Käyttäjää ei löytynyt! ;_;', diff --git a/resources/lang/fil/artist.php b/resources/lang/fil/artist.php index 893ccef081f..6aa1990d649 100644 --- a/resources/lang/fil/artist.php +++ b/resources/lang/fil/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'paghahanap ng mga track', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Advanced na Paghahanap', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Pinakamababang BPM', 'bpm_lte' => 'Pinakamataasang PM', 'empty' => 'Walang mga track na nakitang tumutugma sa pamantayan sa paghahanap.', + 'exclusive_only' => '', 'genre' => 'Dyanra', 'genre_all' => 'Lahat', 'length_gte' => 'Pinakamababang Haba', diff --git a/resources/lang/fil/chat.php b/resources/lang/fil/chat.php index be704dc7c8a..f563a078bee 100644 --- a/resources/lang/fil/chat.php +++ b/resources/lang/fil/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Gusto mo bang itago ang channel na ito? Ikaw ay makakatanggap pa rin ng mga mensahe mula sa channel na ito.', 'create' => 'lumikha ng anunsyo', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Ang mga public channel na sinalihan mo sa osu!lazer ay makikita din dito.', 'title' => 'wala pang mga usapan', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/fil/common.php b/resources/lang/fil/common.php index df9706573d2..79ced2e9887 100644 --- a/resources/lang/fil/common.php +++ b/resources/lang/fil/common.php @@ -39,6 +39,7 @@ 'pin' => 'aspile', 'post' => 'Post', 'read_more' => 'magbasa pa', + 'refresh' => '', 'reply' => 'Sumagot', 'reply_reopen' => 'Sagutin at Muling Buksan', 'reply_resolve' => 'Sagutin at Lutasin', diff --git a/resources/lang/fil/page_title.php b/resources/lang/fil/page_title.php index b636154073c..2aa51914423 100644 --- a/resources/lang/fil/page_title.php +++ b/resources/lang/fil/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'tournaments', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'player info', 'create' => 'maglikha ng account', diff --git a/resources/lang/fil/user_cover_presets.php b/resources/lang/fil/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/fil/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/fil/users.php b/resources/lang/fil/users.php index 58f1e0ba9f7..cda03d9b679 100644 --- a/resources/lang/fil/users.php +++ b/resources/lang/fil/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Ang iyong account ay hindi nagamit ng mahabang panahon.", ], ], diff --git a/resources/lang/fr/artist.php b/resources/lang/fr/artist.php index 47745b56db5..affa123b2f2 100644 --- a/resources/lang/fr/artist.php +++ b/resources/lang/fr/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'recherche de titres', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Recherche Avancée', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Minimum', 'bpm_lte' => 'BPM Maximum', 'empty' => 'Aucune musique correspondant aux critères de recherche n\'a été trouvée.', + 'exclusive_only' => '', 'genre' => 'Genre', 'genre_all' => 'Tous', 'length_gte' => 'Durée minimale', diff --git a/resources/lang/fr/chat.php b/resources/lang/fr/chat.php index 2e7a42b7268..e8f461a3f66 100644 --- a/resources/lang/fr/chat.php +++ b/resources/lang/fr/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Voulez-vous masquer ce canal ? Vous recevrez toujours des messages de ce canal.', 'create' => 'créer une annonce', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Les canaux de discussion que vous rejoignez via osu!lazer seront aussi visibles ici.', 'title' => 'aucune conversation', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/fr/common.php b/resources/lang/fr/common.php index f00f9d084d5..cca3edf083f 100644 --- a/resources/lang/fr/common.php +++ b/resources/lang/fr/common.php @@ -39,6 +39,7 @@ 'pin' => 'épingler', 'post' => 'Poster', 'read_more' => 'lire plus', + 'refresh' => '', 'reply' => 'Répondre', 'reply_reopen' => 'Répondre et rouvrir', 'reply_resolve' => 'Répondre et résoudre', diff --git a/resources/lang/fr/page_title.php b/resources/lang/fr/page_title.php index 5c064053481..39fb141400a 100644 --- a/resources/lang/fr/page_title.php +++ b/resources/lang/fr/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'tournois', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informations du joueur', 'create' => 'créer un compte', diff --git a/resources/lang/fr/user_cover_presets.php b/resources/lang/fr/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/fr/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/fr/users.php b/resources/lang/fr/users.php index f9df9889f94..5dca80afc43 100644 --- a/resources/lang/fr/users.php +++ b/resources/lang/fr/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Votre compte n'a pas été utilisé depuis longtemps.", ], ], diff --git a/resources/lang/he/artist.php b/resources/lang/he/artist.php index fb3eb42b41a..40f1a8551c4 100644 --- a/resources/lang/he/artist.php +++ b/resources/lang/he/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => '', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => '', 'album' => '', @@ -52,6 +57,7 @@ 'bpm_gte' => '', 'bpm_lte' => '', 'empty' => '', + 'exclusive_only' => '', 'genre' => '', 'genre_all' => '', 'length_gte' => '', diff --git a/resources/lang/he/chat.php b/resources/lang/he/chat.php index dab60d70470..2a07f8a4ec1 100644 --- a/resources/lang/he/chat.php +++ b/resources/lang/he/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '', 'create' => '', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'ערוצים ציבוריים שאתה מצטרף אליהם דרך osu!lazer יהיו כאן.', 'title' => 'אין שיחות עדיין', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/he/common.php b/resources/lang/he/common.php index 0ce1cad1c3d..c60bc0b8496 100644 --- a/resources/lang/he/common.php +++ b/resources/lang/he/common.php @@ -39,6 +39,7 @@ 'pin' => 'נעץ', 'post' => 'פרסם', 'read_more' => 'קרא עוד', + 'refresh' => '', 'reply' => 'השב', 'reply_reopen' => 'השב ופתח מחדש', 'reply_resolve' => 'השב ופתור', diff --git a/resources/lang/he/page_title.php b/resources/lang/he/page_title.php index 655dcf574f8..cd464a002c7 100644 --- a/resources/lang/he/page_title.php +++ b/resources/lang/he/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'טורנירים', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'מידע שחקן', 'create' => '', diff --git a/resources/lang/he/user_cover_presets.php b/resources/lang/he/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/he/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/he/users.php b/resources/lang/he/users.php index 66804a024c0..12c7dd3f1ed 100644 --- a/resources/lang/he/users.php +++ b/resources/lang/he/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "החשבון שלך לא שומש הרבה זמן.", ], ], diff --git a/resources/lang/hr-HR/artist.php b/resources/lang/hr-HR/artist.php index 45ee7340a50..203dc9f36b0 100644 --- a/resources/lang/hr-HR/artist.php +++ b/resources/lang/hr-HR/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'pretraživanje pjesama', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Napredno pretraživanje', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimalni BPM', 'bpm_lte' => 'Maximalni BPM', 'empty' => 'Nisu pronađene pjesme koje odgovaraju kriterijima pretraživanja.', + 'exclusive_only' => '', 'genre' => 'Žanr', 'genre_all' => 'Sve', 'length_gte' => 'Minimalna dužina', diff --git a/resources/lang/hr-HR/chat.php b/resources/lang/hr-HR/chat.php index 7fe8a18319b..83a097a5cc8 100644 --- a/resources/lang/hr-HR/chat.php +++ b/resources/lang/hr-HR/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Želiš li sakriti ovaj kanal? I dalje ćeš primati poruke iz ovog kanala.', 'create' => 'napravi obavijest', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Ovdje će također biti vidljivi javni kanali kojima se pridružiš putem osu!lazera.', 'title' => 'još nema razgovora', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/hr-HR/common.php b/resources/lang/hr-HR/common.php index 436108f3af3..4cc6a07c1f6 100644 --- a/resources/lang/hr-HR/common.php +++ b/resources/lang/hr-HR/common.php @@ -39,6 +39,7 @@ 'pin' => 'prikvači', 'post' => 'Objavi', 'read_more' => 'pročitaj više', + 'refresh' => '', 'reply' => 'Odgovori', 'reply_reopen' => 'Odgovori i ponovno otvori', 'reply_resolve' => 'Odgovori i riješi', diff --git a/resources/lang/hr-HR/page_title.php b/resources/lang/hr-HR/page_title.php index 23f6787846f..77252c57cf0 100644 --- a/resources/lang/hr-HR/page_title.php +++ b/resources/lang/hr-HR/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turniri', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informacije o igraču', 'create' => '', diff --git a/resources/lang/hr-HR/user_cover_presets.php b/resources/lang/hr-HR/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/hr-HR/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/hr-HR/users.php b/resources/lang/hr-HR/users.php index 7ab56202312..4a3bf67451b 100644 --- a/resources/lang/hr-HR/users.php +++ b/resources/lang/hr-HR/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Tvoj račun nije korišten dugo vremena.", ], ], diff --git a/resources/lang/hu/artist.php b/resources/lang/hu/artist.php index d4cd0039988..1c0f6efa8cb 100644 --- a/resources/lang/hu/artist.php +++ b/resources/lang/hu/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'zeneszám keresés', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Részletes keresés', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimum BPM', 'bpm_lte' => 'Maximum BPM', 'empty' => 'Nem található a keresési feltételeknek megfelelő zeneszám.', + 'exclusive_only' => '', 'genre' => 'Műfaj', 'genre_all' => 'Mind', 'length_gte' => 'Minimum hossz', diff --git a/resources/lang/hu/chat.php b/resources/lang/hu/chat.php index 60b46f3b14b..4cfbb907d17 100644 --- a/resources/lang/hu/chat.php +++ b/resources/lang/hu/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Szeretnéd elrejteni a csatornát? Továbbra is kapni fogsz üzeneteket ebből a csatornáról.', 'create' => 'közlemény készítése', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'A nyilvános csatornák, amikbe osu!lazer-en keresztül csatlakozol, itt is láthatóak lesznek.', 'title' => 'még nincsenek beszélgetések', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/hu/common.php b/resources/lang/hu/common.php index 2604ab64f2a..1f1eb09c813 100644 --- a/resources/lang/hu/common.php +++ b/resources/lang/hu/common.php @@ -39,6 +39,7 @@ 'pin' => 'kitűzés', 'post' => 'Küldés', 'read_more' => 'több megjelenítése', + 'refresh' => '', 'reply' => 'Válasz', 'reply_reopen' => 'Válaszolás és újranyitás', 'reply_resolve' => 'Válaszolás és megoldás', diff --git a/resources/lang/hu/page_title.php b/resources/lang/hu/page_title.php index 0384a0912b7..2f701038095 100644 --- a/resources/lang/hu/page_title.php +++ b/resources/lang/hu/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'bajnokságok', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'játékos információi', 'create' => 'fiók létrehozása', diff --git a/resources/lang/hu/user_cover_presets.php b/resources/lang/hu/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/hu/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/hu/users.php b/resources/lang/hu/users.php index e2aa9be0c63..f4b558953fc 100644 --- a/resources/lang/hu/users.php +++ b/resources/lang/hu/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "A fiókod hosszú ideje nem volt használva.", ], ], diff --git a/resources/lang/id/accounts.php b/resources/lang/id/accounts.php index 1fba080ddcf..2a8fc01aa40 100644 --- a/resources/lang/id/accounts.php +++ b/resources/lang/id/accounts.php @@ -63,7 +63,7 @@ ], 'github_user' => [ - 'info' => "Apabila kamu merupakan kontributor repositori open-source osu!, kamu dapat menautkan akun GitHub kamu di sini untuk menghubungkan entrimu pada riwayat perubahan dengan profil osu! milikmu. Akun GitHub yang tidak memiliki riwayat kontribusi terhadap osu! tidak dapat dihubungkan.", + 'info' => "Apabila kamu merupakan kontributor repositori open-source osu!, kamu dapat menautkan akun GitHub kamu di sini untuk menghubungkan entrimu pada riwayat perubahan dengan profil osu! milikmu. Akun GitHub yang tidak memiliki riwayat kontribusi terhadap osu! tidak dapat ditautkan.", 'link' => 'Tautkan Akun GitHub', 'title' => 'GitHub', 'unlink' => 'Lepas Tautan Akun GitHub', diff --git a/resources/lang/id/artist.php b/resources/lang/id/artist.php index f5643577d2f..8432eb2f9f4 100644 --- a/resources/lang/id/artist.php +++ b/resources/lang/id/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'pencarian lagu', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Pencarian Lanjutan', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Minimal', 'bpm_lte' => 'BPM Maksimal', 'empty' => 'Tidak ada lagu yang sesuai dengan kriteria pencarian yang ditentukan.', + 'exclusive_only' => '', 'genre' => 'Aliran', 'genre_all' => 'Semua', 'length_gte' => 'Durasi Minimal', diff --git a/resources/lang/id/authorization.php b/resources/lang/id/authorization.php index d4a8f0bb4af..fa0a13d4387 100644 --- a/resources/lang/id/authorization.php +++ b/resources/lang/id/authorization.php @@ -40,13 +40,13 @@ 'beatmap_discussion_post' => [ 'destroy' => [ - 'not_owner' => 'Kamu hanya dapat menghapus postingan milik diri sendiri.', + 'not_owner' => 'Kamu hanya dapat menghapus postingan milikmu sendiri.', 'resolved' => 'Kamu tidak dapat menghapus postingan pada topik diskusi yang telah terjawab.', 'system_generated' => 'Postingan yang dihasilkan secara otomatis tidak dapat dihapus.', ], 'edit' => [ - 'not_owner' => 'Hanya pemilik topik yang diperbolehkan untuk menyunting kiriman.', + 'not_owner' => 'Postingan ini hanya dapat disunting oleh pengirimnya.', 'resolved' => 'Kamu tidak dapat menyunting postingan pada topik diskusi yang telah terjawab.', 'system_generated' => 'Postingan yang dihasilkan secara otomatis tidak dapat disunting.', ], @@ -76,7 +76,7 @@ 'disabled' => 'Komentar dinonaktifkan', ], 'update' => [ - 'deleted' => "Tidak dapat menyunting post yang telah dihapus.", + 'deleted' => "Postingan yang telah dihapus tidak dapat disunting.", ], ], @@ -86,7 +86,7 @@ 'entry' => [ 'limit_reached' => 'Kamu telah mencapai batas entri untuk kontes ini', - 'over' => 'Terima kasih telah mengirimkan entrimu! Pengumpulan entri untuk kontes ini telah ditutup dan pemungutan suara akan segera berlangsung.', + 'over' => 'Terima kasih atas entrimu! Pengumpulan entri untuk kontes ini telah ditutup dan pemungutan suara akan segera dibuka.', ], ], @@ -97,23 +97,23 @@ 'post' => [ 'delete' => [ - 'only_last_post' => 'Hanya kiriman terakhir yang dapat dihapus.', - 'locked' => 'Tidak dapat menghapus kiriman di topik yang telah dikunci.', + 'only_last_post' => 'Hanya postingan terakhir yang dapat dihapus.', + 'locked' => 'Postingan pada topik yang telah dikunci tidak dapat dihapus.', 'no_forum_access' => 'Kamu tidak memiliki akses ke forum yang dituju.', - 'not_owner' => 'Hanya pemilik topik yang dapat menghapus kiriman.', + 'not_owner' => 'Postingan ini hanya dapat dihapus oleh pengirimnya.', ], 'edit' => [ - 'deleted' => 'Tidak dapat menyunting postingan yang telah dihapus.', + 'deleted' => 'Postingan yang telah dihapus tidak dapat disunting.', 'locked' => 'Penyuntingan pada postingan ini telah dikunci.', 'no_forum_access' => 'Kamu tidak memiliki akses ke forum yang dituju.', - 'not_owner' => 'Hanya pemilik topik yang dapat menyunting kiriman.', - 'topic_locked' => 'Tidak dapat menyunting kiriman di topik yang telah dikunci.', + 'not_owner' => 'Postingan ini hanya dapat disunting oleh pengirimnya.', + 'topic_locked' => 'Postingan pada topik yang telah dikunci tidak dapat disunting.', ], 'store' => [ 'play_more' => 'Kamu harus terlebih dahulu bermain sebelum kamu dapat membuat postingan pada forum! Apabila kamu mengalami masalah saat bermain, silakan kunjungi forum Help & Support.', - 'too_many_help_posts' => "Kamu harus lebih banyak bermain sebelum kamu dapat membuat postingan tambahan. Apabila kamu masih memerlukan bantuan lebih lanjut, silakan kirim email ke support@ppy.sh", // FIXME: unhardcode email address. + 'too_many_help_posts' => "Kamu perlu untuk bermain lebih banyak sebelum kamu dapat membuat postingan tambahan. Apabila kamu masih menemui masalah dalam bermain, silakan kirim email ke support@ppy.sh", // FIXME: unhardcode email address. ], ], @@ -134,7 +134,7 @@ 'store' => [ 'no_forum_access' => 'Kamu tidak memiliki akses ke forum yang dituju.', 'no_permission' => 'Tidak memiliki izin untuk membuat topik baru.', - 'forum_closed' => 'Forum ditutup sehingga tidak dapat membuat postingan.', + 'forum_closed' => 'Forum ini telah ditutup dan tidak lagi dapat ditulis.', ], 'vote' => [ diff --git a/resources/lang/id/beatmaps.php b/resources/lang/id/beatmaps.php index e711e128821..885d61ba676 100644 --- a/resources/lang/id/beatmaps.php +++ b/resources/lang/id/beatmaps.php @@ -21,7 +21,7 @@ 'guest' => 'Guest difficulty oleh :user', 'kudosu_denied' => 'Perolehan kudosu ditolak.', 'message_placeholder_deleted_beatmap' => 'Tingkat kesulitan ini telah dihapus sehingga diskusi lebih lanjut tidak lagi diperkenankan.', - 'message_placeholder_locked' => 'Laman diskusi pada beatmap ini telah ditutup.', + 'message_placeholder_locked' => 'Diskusi pada beatmap ini telah ditutup.', 'message_placeholder_silenced' => "Kamu tidak dapat membuka topik diskusi baru ketika akunmu sedang di-silence.", 'message_type_select' => 'Pilih Jenis Komentar', 'reply_notice' => 'Tekan enter untuk membalas.', @@ -50,19 +50,19 @@ 'prompt' => [ 'lock' => 'Alasan penguncian', - 'unlock' => 'Apakah kamu yakin untuk membuka kunci topik diskusi ini?', + 'unlock' => 'Apakah kamu yakin untuk membuka kunci halaman diskusi ini?', ], ], 'message_hint' => [ - 'in_general' => 'Topik-topik diskusi ini berlaku untuk keseluruhan mapset secara umum. Untuk membuka topik diskusi baru, coba memulai pesan dengan keterangan waktu (contoh: 00:12:345).', - 'in_timeline' => 'Topik-topik diskusi ini berlaku untuk masing-masing tingkat kesulitan secara spesifik. Untuk memulai topik diskusi baru, salin keterangan waktu dari editor disertai dengan komentar Anda (satu topik per keterangan waktu).', + 'in_general' => 'Topik diskusi ini akan tertuju pada Umum (Seluruh tingkat kesulitan). Untuk membuka topik diskusi baru khusus bagi tingkat kesulitan ini, mulailah pesanmu dengan keterangan waktu (mis: 00:12:345).', + 'in_timeline' => 'Untuk memberikan mod pada beberapa keterangan waktu, pisahkan mod kamu ke dalam beberapa topik diskusi (satu topik per keterangan waktunya).', ], 'message_placeholder' => [ 'general' => 'Ketik di sini untuk membuka topik diskusi baru pada Umum (:version)', 'generalAll' => 'Ketik di sini untuk membuka topik diskusi baru pada Umum (Seluruh tingkat kesulitan)', - 'review' => 'Ketik di sini untuk membuka kajian baru', + 'review' => 'Ketik di sini untuk menulis kajian', 'timeline' => 'Ketik di sini untuk membuka topik diskusi baru pada Linimasa (:version)', ], @@ -106,7 +106,7 @@ 'timestamp' => 'Keterangan Waktu', 'timestamp_missing' => 'salin (ctrl+c) objek di editor dan tempelkan (ctrl+v) pada boks di atas untuk membubuhkan keterangan waktu!', 'title' => 'Topik Diskusi Baru', - 'unpin' => 'Lepas sematan', + 'unpin' => 'Lepas Sematan', ], 'review' => [ @@ -117,8 +117,8 @@ 'unlink' => 'Lepas Tautan', 'unsaved' => 'Belum Tersimpan', 'timestamp' => [ - 'all-diff' => 'Kamu tidak dapat membubuhkan keterangan waktu pada topik diskusi yang tertuju pada "Umum (Seluruh tingkat kesulitan)".', - 'diff' => 'Apabila post ini dimulai dengan keterangan waktu, post yang bersangkutan akan muncul pada tab Linimasa.', + 'all-diff' => 'Keterangan waktu tidak dapat dibubuhkan pada topik diskusi yang tertuju pada "Umum (Seluruh tingkat kesulitan)".', + 'diff' => 'Apabila topik diskusi ini dimulai dengan keterangan waktu, topik ini akan muncul pada tab Linimasa.', ], ], 'insert-block' => [ diff --git a/resources/lang/id/chat.php b/resources/lang/id/chat.php index fcc20303e08..65dfca730ce 100644 --- a/resources/lang/id/chat.php +++ b/resources/lang/id/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Apakah kamu ingin menyembunyikan kanal percakapan ini? Kamu akan tetap menerima pesan dari kanal percakapan ini.', 'create' => 'buat pengumuman', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Kanal percakapan publik yang kamu buka melalui osu!lazer juga akan terlihat di sini.', 'title' => 'belum ada percakapan', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/id/common.php b/resources/lang/id/common.php index f52f1026275..6763206340d 100644 --- a/resources/lang/id/common.php +++ b/resources/lang/id/common.php @@ -39,6 +39,7 @@ 'pin' => 'sematkan', 'post' => 'Kirim', 'read_more' => 'baca selengkapnya', + 'refresh' => '', 'reply' => 'Balas', 'reply_reopen' => 'Balas dan Buka Kembali', 'reply_resolve' => 'Balas dan Tutup', diff --git a/resources/lang/id/forum.php b/resources/lang/id/forum.php index 889109108bf..ebafff27584 100644 --- a/resources/lang/id/forum.php +++ b/resources/lang/id/forum.php @@ -26,7 +26,7 @@ 'forums' => [ 'forums' => 'Forum', - 'latest_post' => 'Kiriman Terbaru', + 'latest_post' => 'Postingan Terkini', 'index' => [ 'title' => 'Indeks Forum', @@ -44,17 +44,17 @@ ], 'post' => [ - 'confirm_destroy' => 'Apakah kamu yakin untuk menghapus post ini?', - 'confirm_restore' => 'Apakah kamu yakin untuk memulihkan post ini?', + 'confirm_destroy' => 'Apakah kamu yakin untuk menghapus postingan ini?', + 'confirm_restore' => 'Apakah kamu yakin untuk memulihkan postingan ini?', 'edited' => 'Terakhir disunting oleh :user :when, dengan total penyuntingan sebanyak :count_delimited kali.|Terakhir disunting oleh :user :when, dengan total penyuntingan sebanyak :count_delimited kali.', 'posted_at' => 'diposting :when', 'posted_by_in' => 'diposting oleh :username pada :forum', 'actions' => [ 'destroy' => 'Hapus post', - 'edit' => 'Sunting post', - 'report' => 'Laporkan post', - 'restore' => 'Pulihkan post', + 'edit' => 'Sunting postingan', + 'report' => 'Laporkan postingan', + 'restore' => 'Pulihkan postingan', ], 'create' => [ @@ -70,8 +70,8 @@ ], 'search' => [ - 'go_to_post' => 'Tuju post', - 'post_number_input' => 'masukkan nomor post', + 'go_to_post' => 'Tuju postingan', + 'post_number_input' => 'masukkan nomor postingan', 'total_posts' => ':posts_count total postingan', ], @@ -106,22 +106,22 @@ 'submit' => 'Kirim', 'necropost' => [ - 'default' => 'Topik ini sudah tidak lagi aktif. Harap untuk tidak membuka balasan baru pada topik ini kecuali apabila kamu memiliki alasan khusus untuk melakukannya.', + 'default' => 'Topik ini sudah tidak lagi aktif. Mohon untuk tidak membuka balasan baru pada topik ini kecuali apabila kamu memiliki alasan tertentu untuk melakukannya.', 'new_topic' => [ - '_' => "Topik ini sudah tidak lagi aktif. Apabila kamu tidak memiliki alasan khusus untuk membuka balasan baru pada topik ini, mohon :create.", + '_' => "Topik ini sudah tidak lagi aktif. Apabila kamu tidak memiliki alasan tertentu untuk membuka balasan baru pada topik ini, silakan :create.", 'create' => 'buat topik baru', ], ], 'placeholder' => [ - 'body' => 'Ketik konten post di sini', + 'body' => 'Ketik konten postingan di sini', 'title' => 'Klik di sini untuk mengatur judul', ], ], 'jump' => [ - 'enter' => 'klik untuk memasukkan nomor post tertentu', + 'enter' => 'klik untuk memasukkan nomor postingan tertentu', 'first' => 'tuju postingan pertama', 'last' => 'tuju postingan terakhir', 'next' => 'lewati 10 postingan berikutnya', @@ -141,7 +141,7 @@ 'data' => [ 'add_tag' => 'tag ":tag" disematkan', 'announcement' => 'topik disematkan dan ditandai sebagai pengumuman', - 'edit_topic' => 'menuju :title', + 'edit_topic' => 'menjadi :title', 'fork' => 'dari :topic', 'pin' => 'topik yang disematkan', 'post_operation' => 'di-post oleh :username', @@ -167,8 +167,8 @@ 'restore_post' => 'Postingan dipulihkan', 'restore_topic' => 'Topik dipulihkan', 'split_destination' => 'Postingan-postingan yang telah dipisah dipindahkan', - 'split_source' => 'Pisahkan post-post yang ada', - 'topic_type' => 'Tentukan tipe topik', + 'split_source' => 'Pisahkan postingan', + 'topic_type' => 'Jenis topik ditentukan', 'topic_type_changed' => 'Tipe topik diubah', 'unlock' => 'Kunci topik dibuka', 'unpin' => 'Sematan topik dilepas', @@ -296,9 +296,9 @@ 'lock' => [ 'is_locked' => 'Topik ini telah dikunci dan tidak dapat dibalas', - 'to_0' => 'Buka topik', + 'to_0' => 'Buka kunci topik', 'to_0_confirm' => 'Buka kunci topik?', - 'to_0_done' => 'Topik telah dibuka', + 'to_0_done' => 'Kunci topik telah dibuka', 'to_1' => 'Kunci topik', 'to_1_confirm' => 'Kunci topik?', 'to_1_done' => 'Topik telah dikunci', @@ -327,7 +327,7 @@ 'show' => [ 'deleted-posts' => 'Postingan yang Dihapus', - 'total_posts' => 'Jumlah Post', + 'total_posts' => 'Jumlah Postingan', 'feature_vote' => [ 'current' => 'Prioritas Saat Ini: +:count', diff --git a/resources/lang/id/layout.php b/resources/lang/id/layout.php index 6ca2c0765eb..5f83d7175d2 100644 --- a/resources/lang/id/layout.php +++ b/resources/lang/id/layout.php @@ -150,15 +150,15 @@ ], '500' => [ 'error' => 'Oh, tidak! Sepertinya ada sesuatu yang bermasalah! ;_;', - 'description' => "Sistem osu! akan secara otomatis memberitahukan kami setiap kali terdapat suatu masalah.", + 'description' => "Kami menerima notifikasi secara otomatis setiap kali terdapat suatu masalah.", ], 'fatal' => [ 'error' => 'Oh, tidak! Sepertinya ada sesuatu yang (sangat) bermasalah! ;_;', - 'description' => "Sistem osu! akan secara otomatis memberitahukan kami setiap kali terdapat suatu masalah.", + 'description' => "Kami menerima notifikasi secara otomatis setiap kali terdapat suatu masalah.", ], '503' => [ 'error' => 'Sedang dalam pemeliharaan!', - 'description' => "Proses pemeliharaan pada umumnya dapat memakan waktu yang cukup beragam, mulai dari 5 detik hingga lebih dari 10 menit. Jika proses pemeliharaan berlangsung lebih lama dari yang sewajarnya, harap kunjungi :link untuk informasi lebih lanjut.", + 'description' => "Proses pemeliharaan pada umumnya dapat memakan waktu yang cukup beragam, mulai dari 5 detik hingga 10 menit. Apabila proses ini berlangsung lebih lama dari yang sewajarnya, kunjungi :link untuk informasi lebih lanjut.", 'link' => [ 'text' => '', 'href' => '', diff --git a/resources/lang/id/model_validation.php b/resources/lang/id/model_validation.php index 5642df6beca..20bda169cad 100644 --- a/resources/lang/id/model_validation.php +++ b/resources/lang/id/model_validation.php @@ -72,8 +72,8 @@ ], 'post' => [ - 'beatmapset_post_no_delete' => 'Menghapus posting metadata beatmap tidak diizinkan.', - 'beatmapset_post_no_edit' => 'Menyunting posting metadata beatmap tidak diizinkan.', + 'beatmapset_post_no_delete' => 'Postingan metadata beatmap tidak diperkenankan untuk dihapus.', + 'beatmapset_post_no_edit' => 'Postingan metadata beatmap tidak diperkenankan untuk disunting.', 'first_post_no_delete' => 'Tidak dapat menghapus postingan awal', 'missing_topic' => 'Postingan ini tidak memiliki topik', 'only_quote' => 'Balasanmu hanya berisi kutipan.', diff --git a/resources/lang/id/page_title.php b/resources/lang/id/page_title.php index 175bf758f48..0a40c16935c 100644 --- a/resources/lang/id/page_title.php +++ b/resources/lang/id/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnamen', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'info pemain', 'create' => 'buat akun', diff --git a/resources/lang/id/store.php b/resources/lang/id/store.php index 8dbf0acd6ae..fae27da8265 100644 --- a/resources/lang/id/store.php +++ b/resources/lang/id/store.php @@ -14,26 +14,26 @@ 'total' => 'total', 'errors_no_checkout' => [ - 'line_1' => 'Uh-oh, terdapat masalah dengan keranjangmu yang menghalangi proses checkout!', + 'line_1' => 'Uh-oh, terdapat masalah dengan keranjang belanjamu yang menghalangi proses checkout!', 'line_2' => 'Hapus atau perbarui rangkaian item di atas untuk melanjutkan.', ], 'empty' => [ - 'text' => 'Keranjangmu masih kosong.', + 'text' => 'Keranjang belanjamu masih kosong.', 'return_link' => [ - '_' => 'Kembali ke tautan :link untuk mencari merchandise!', + '_' => 'Kembalilah ke halaman :link untuk menelusuri berbagai cindera mata!', 'link_text' => 'etalase toko', ], ], ], 'checkout' => [ - 'cart_problems' => 'Uh oh, terdapat masalah dengan keranjangmu!', - 'cart_problems_edit' => 'Klik di sini untuk menyunting isi keranjangmu.', + 'cart_problems' => 'Uh oh, terdapat masalah dengan keranjang belanjamu!', + 'cart_problems_edit' => 'Klik di sini untuk menyuntingnya.', 'declined' => 'Pembayaran dibatalkan.', 'delayed_shipping' => 'Kami sedang kebanjiran pesanan! Apabila kamu memesan sekarang, mohon beri kami waktu tambahan **selama 1-2 minggu** untuk memproses pesananmu karena kami saat ini masih harus mengurus pesanan yang ada.', 'hide_from_activity' => 'Sembunyikan seluruh tag osu!supporter pada pesanan ini dari aktivitas saya', - 'old_cart' => 'Keranjangmu sepertinya telah kedaluwarsa dan telah dimuat ulang. Silakan coba lagi.', + 'old_cart' => 'Keranjang belanjamu sepertinya telah kedaluwarsa dan telah dimuat ulang. Silakan coba lagi.', 'pay' => 'Checkout melalui Paypal', 'title_compact' => 'checkout', diff --git a/resources/lang/id/user_cover_presets.php b/resources/lang/id/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/id/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/id/users.php b/resources/lang/id/users.php index fb257de5477..4714d6b3e39 100644 --- a/resources/lang/id/users.php +++ b/resources/lang/id/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Akunmu telah lama tidak digunakan.", ], ], @@ -347,9 +348,9 @@ 'title' => 'Permainan Playlist', ], 'posts' => [ - 'title' => 'Posting', + 'title' => 'Postingan', 'title_longer' => 'Postingan Terkini', - 'show_more' => 'lihat lebih banyak post', + 'show_more' => 'lihat lebih banyak postingan', ], 'recent_activity' => [ 'title' => 'Aktivitas Terkini', diff --git a/resources/lang/it/artist.php b/resources/lang/it/artist.php index bd6a03a1e0b..f9f52e4973f 100644 --- a/resources/lang/it/artist.php +++ b/resources/lang/it/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'ricerca tracce', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Ricerca Avanzata', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Minimo', 'bpm_lte' => 'BPM Massimo', 'empty' => 'Non ci sono tracce corrispondenti ai criteri di ricerca.', + 'exclusive_only' => '', 'genre' => 'Genere', 'genre_all' => 'Qualsiasi', 'length_gte' => 'Durata Minima', diff --git a/resources/lang/it/chat.php b/resources/lang/it/chat.php index 5ed36da4f52..b9afdc2b8b9 100644 --- a/resources/lang/it/chat.php +++ b/resources/lang/it/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Vuoi nascondere questo canale? Riceverai ancora messaggi da questo canale.', 'create' => 'crea un annuncio', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'I canali pubblici in cui entri attraverso osu!lazer saranno visibili anche qui.', 'title' => 'nessuna conversazione al momento', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/it/common.php b/resources/lang/it/common.php index 141a7c43eb0..1b6114023fe 100644 --- a/resources/lang/it/common.php +++ b/resources/lang/it/common.php @@ -39,6 +39,7 @@ 'pin' => 'fissa', 'post' => 'Posta', 'read_more' => 'leggi di più', + 'refresh' => '', 'reply' => 'Rispondi', 'reply_reopen' => 'Rispondi e Riapri', 'reply_resolve' => 'Rispondi e Risolvi', diff --git a/resources/lang/it/page_title.php b/resources/lang/it/page_title.php index 5b7bedef018..a9aad10aa32 100644 --- a/resources/lang/it/page_title.php +++ b/resources/lang/it/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'tornei', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informazioni giocatore', 'create' => 'crea account', diff --git a/resources/lang/it/user_cover_presets.php b/resources/lang/it/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/it/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/it/users.php b/resources/lang/it/users.php index dd4bee1c9ad..b88aab97d12 100644 --- a/resources/lang/it/users.php +++ b/resources/lang/it/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Il tuo account non è stato utilizzato per molto tempo.", ], ], diff --git a/resources/lang/ja/artist.php b/resources/lang/ja/artist.php index 150f9d89030..4aacab1e62d 100644 --- a/resources/lang/ja/artist.php +++ b/resources/lang/ja/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'トラック検索', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => '高度な検索', 'album' => 'アルバム', @@ -52,6 +57,7 @@ 'bpm_gte' => '最低BPM', 'bpm_lte' => '最高BPM', 'empty' => '条件に当てはまるトラックが見つかりませんでした。', + 'exclusive_only' => '', 'genre' => 'ジャンル', 'genre_all' => '全て', 'length_gte' => '最短の再生時間', diff --git a/resources/lang/ja/chat.php b/resources/lang/ja/chat.php index 15112508a7d..607cda5a63e 100644 --- a/resources/lang/ja/chat.php +++ b/resources/lang/ja/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'このチャンネルを隠したいです?まだこのチャンネルからメッセージが送れられています。', 'create' => 'お知らせを作成', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'あなたがosu!lazerで参加している公開チャンネルもここに表示されます。', 'title' => 'まだトークはありません', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/ja/common.php b/resources/lang/ja/common.php index e39fa3b087f..32c429f0d4d 100644 --- a/resources/lang/ja/common.php +++ b/resources/lang/ja/common.php @@ -39,6 +39,7 @@ 'pin' => 'ピン', 'post' => '投稿', 'read_more' => '続きを読む', + 'refresh' => '', 'reply' => '返信', 'reply_reopen' => '返信して再開', 'reply_resolve' => '返信して解決', diff --git a/resources/lang/ja/page_title.php b/resources/lang/ja/page_title.php index 8e2b9141d2a..aaa3c819a08 100644 --- a/resources/lang/ja/page_title.php +++ b/resources/lang/ja/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'トーナメント', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'プレイヤー情報', 'create' => 'アカウント作成', diff --git a/resources/lang/ja/user_cover_presets.php b/resources/lang/ja/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/ja/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/ja/users.php b/resources/lang/ja/users.php index c0dcf462a3c..57cc07a23ff 100644 --- a/resources/lang/ja/users.php +++ b/resources/lang/ja/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "あなたのアカウントは長期間使用されていません。", ], ], diff --git a/resources/lang/kk-KZ/artist.php b/resources/lang/kk-KZ/artist.php index 45300032725..1c46239e82c 100644 --- a/resources/lang/kk-KZ/artist.php +++ b/resources/lang/kk-KZ/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'өлең іздеу', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Кең ауқымды Іздеу', 'album' => 'Альбом', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Минималды BPM', 'bpm_lte' => 'Максималды BPM', 'empty' => 'Іздеу критерийлеріне келетін өлеңдер табылмады.', + 'exclusive_only' => '', 'genre' => 'Жанры', 'genre_all' => 'Бәрі', 'length_gte' => 'Минималды Ұзақтығы', diff --git a/resources/lang/kk-KZ/chat.php b/resources/lang/kk-KZ/chat.php index e17466c9da2..720b47cfb0f 100644 --- a/resources/lang/kk-KZ/chat.php +++ b/resources/lang/kk-KZ/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '', 'create' => '', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => '', 'title' => '', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/kk-KZ/common.php b/resources/lang/kk-KZ/common.php index 57deb34d804..8dc455b4c2c 100644 --- a/resources/lang/kk-KZ/common.php +++ b/resources/lang/kk-KZ/common.php @@ -39,6 +39,7 @@ 'pin' => '', 'post' => '', 'read_more' => '', + 'refresh' => '', 'reply' => '', 'reply_reopen' => '', 'reply_resolve' => '', diff --git a/resources/lang/kk-KZ/page_title.php b/resources/lang/kk-KZ/page_title.php index 63320de7de0..2a6fbe54c4f 100644 --- a/resources/lang/kk-KZ/page_title.php +++ b/resources/lang/kk-KZ/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '', 'create' => '', diff --git a/resources/lang/kk-KZ/user_cover_presets.php b/resources/lang/kk-KZ/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/kk-KZ/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/kk-KZ/users.php b/resources/lang/kk-KZ/users.php index 3b45b26a689..ea3b1692fe2 100644 --- a/resources/lang/kk-KZ/users.php +++ b/resources/lang/kk-KZ/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "", ], ], diff --git a/resources/lang/ko/artist.php b/resources/lang/ko/artist.php index f89fb5c4976..b57521dd42b 100644 --- a/resources/lang/ko/artist.php +++ b/resources/lang/ko/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => '트랙 검색', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => '고급 검색', 'album' => '앨범', @@ -52,6 +57,7 @@ 'bpm_gte' => '최소 BPM', 'bpm_lte' => '최대 BPM', 'empty' => '검색 조건에 맞는 트랙을 찾을 수 없습니다.', + 'exclusive_only' => '', 'genre' => '장르', 'genre_all' => '전체', 'length_gte' => '최소 길이', diff --git a/resources/lang/ko/chat.php b/resources/lang/ko/chat.php index beb4c397794..a340f91e59e 100644 --- a/resources/lang/ko/chat.php +++ b/resources/lang/ko/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '이 채널을 숨길까요? 채널을 숨기더라도 메시지는 계속 수신받습니다.', 'create' => '공지사항 작성', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'osu!lazer에서 참가하는 공개 채널이 여기서도 보입니다.', 'title' => '아직 아무런 대화가 없습니다', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/ko/common.php b/resources/lang/ko/common.php index aaa3aec5c31..c47517f8be4 100644 --- a/resources/lang/ko/common.php +++ b/resources/lang/ko/common.php @@ -39,6 +39,7 @@ 'pin' => '고정', 'post' => '게시하기', 'read_more' => '자세히 보기', + 'refresh' => '', 'reply' => '답변하기', 'reply_reopen' => '답변하고 토론 재개하기', 'reply_resolve' => '답변하고 토론 마무리하기', diff --git a/resources/lang/ko/page_title.php b/resources/lang/ko/page_title.php index fc16e6aae25..e2888828445 100644 --- a/resources/lang/ko/page_title.php +++ b/resources/lang/ko/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '토너먼트', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '플레이어 정보', 'create' => '계정 만들기', diff --git a/resources/lang/ko/user_cover_presets.php b/resources/lang/ko/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/ko/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/ko/users.php b/resources/lang/ko/users.php index a523728968b..b907c264f5f 100644 --- a/resources/lang/ko/users.php +++ b/resources/lang/ko/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "당신의 계정은 오랫동안 사용되지 않았네요.", ], ], diff --git a/resources/lang/lt/artist.php b/resources/lang/lt/artist.php index a1f0b740b47..95650ae1869 100644 --- a/resources/lang/lt/artist.php +++ b/resources/lang/lt/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'takelių paieška', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Išplėstinė paieška', 'album' => 'Albumas', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimalus BPM', 'bpm_lte' => 'Maksimalus BPM', 'empty' => 'Takelių atitinkančių paieškos kriterijus nerasta.', + 'exclusive_only' => '', 'genre' => 'Žanras', 'genre_all' => 'Visi', 'length_gte' => 'Minimali Trukmė', diff --git a/resources/lang/lt/chat.php b/resources/lang/lt/chat.php index e875c28b4b6..f8b20721c3b 100644 --- a/resources/lang/lt/chat.php +++ b/resources/lang/lt/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Ar norite paslėpti šį kanalą? Jūs toliau gausite žinutes iš šio kanalo.', 'create' => 'kurti skelbimą', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Vieši kanalai prisijungti per osu!lazer bus matomi ir čia.', 'title' => 'kol kas pokalbių nėra', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/lt/common.php b/resources/lang/lt/common.php index 33abe311a04..debf901e3b7 100644 --- a/resources/lang/lt/common.php +++ b/resources/lang/lt/common.php @@ -39,6 +39,7 @@ 'pin' => 'prisegti', 'post' => 'Publikuoti', 'read_more' => 'skaityti daugiau', + 'refresh' => '', 'reply' => 'Atsakymai', 'reply_reopen' => 'Atsakyti ir atidaryti', 'reply_resolve' => 'Atsakyti ir išspręsti', diff --git a/resources/lang/lt/page_title.php b/resources/lang/lt/page_title.php index 7dcb932f447..482d9f45658 100644 --- a/resources/lang/lt/page_title.php +++ b/resources/lang/lt/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnyrai', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'žaidėjo informacija', 'create' => 'kurti paskyrą', diff --git a/resources/lang/lt/user_cover_presets.php b/resources/lang/lt/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/lt/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/lt/users.php b/resources/lang/lt/users.php index 49a39e174ef..59dd2bba16f 100644 --- a/resources/lang/lt/users.php +++ b/resources/lang/lt/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Jūsų paskyra buvo nenaudojama ilga laiką.", ], ], diff --git a/resources/lang/lv-LV/artist.php b/resources/lang/lv-LV/artist.php index 32016a0670c..c1f4ab5c1c3 100644 --- a/resources/lang/lv-LV/artist.php +++ b/resources/lang/lv-LV/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'dziesmu meklēšana', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Paplašināta Meklēšana', 'album' => 'Albums', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Minimums', 'bpm_lte' => 'BPM Maksimums', 'empty' => 'Netika atrasta neviena dziesma, kas atbilstu meklēšanas kritērijiem.', + 'exclusive_only' => '', 'genre' => 'Žanrs', 'genre_all' => 'Viss', 'length_gte' => 'Minimālais Garums', diff --git a/resources/lang/lv-LV/chat.php b/resources/lang/lv-LV/chat.php index acf4ccf70cc..60db5e8dfba 100644 --- a/resources/lang/lv-LV/chat.php +++ b/resources/lang/lv-LV/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Vai vēlaties paslēpt šo kanālu? Jūs joprojām saņemsiet ziņas no šī kanāla.', 'create' => 'izveidot paziņojumu', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Publiskie kanāli, kuriem pievienojaties, izmantojot osu!lazer, būs redzami arī šeit.', 'title' => 'pagaidām nav sarunu', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/lv-LV/common.php b/resources/lang/lv-LV/common.php index 7a52897b58c..49d491fb2b3 100644 --- a/resources/lang/lv-LV/common.php +++ b/resources/lang/lv-LV/common.php @@ -39,6 +39,7 @@ 'pin' => 'piespraust', 'post' => 'Publicēt', 'read_more' => 'lasīt vairāk', + 'refresh' => '', 'reply' => 'Atbildēt', 'reply_reopen' => 'Atbildēt un Atvērt atkārtoti', 'reply_resolve' => 'Atbildēt un atrisināt', diff --git a/resources/lang/lv-LV/page_title.php b/resources/lang/lv-LV/page_title.php index 63320de7de0..2a6fbe54c4f 100644 --- a/resources/lang/lv-LV/page_title.php +++ b/resources/lang/lv-LV/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '', 'create' => '', diff --git a/resources/lang/lv-LV/user_cover_presets.php b/resources/lang/lv-LV/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/lv-LV/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/lv-LV/users.php b/resources/lang/lv-LV/users.php index c5b268b4849..f2f9e7db1da 100644 --- a/resources/lang/lv-LV/users.php +++ b/resources/lang/lv-LV/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "", ], ], diff --git a/resources/lang/ms-MY/artist.php b/resources/lang/ms-MY/artist.php index 5d7a7d0139d..1ffac391914 100644 --- a/resources/lang/ms-MY/artist.php +++ b/resources/lang/ms-MY/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'carian lagu', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Carian Lanjutan', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Minimum', 'bpm_lte' => 'BPM Maksimum', 'empty' => 'Tiada lagu yang sesuai dengan ukur tara pencarian yang ditentukan.', + 'exclusive_only' => '', 'genre' => 'Aliran', 'genre_all' => 'Semua', 'length_gte' => 'Tempoh Minimum', diff --git a/resources/lang/ms-MY/chat.php b/resources/lang/ms-MY/chat.php index e17466c9da2..720b47cfb0f 100644 --- a/resources/lang/ms-MY/chat.php +++ b/resources/lang/ms-MY/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '', 'create' => '', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => '', 'title' => '', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/ms-MY/common.php b/resources/lang/ms-MY/common.php index 57deb34d804..8dc455b4c2c 100644 --- a/resources/lang/ms-MY/common.php +++ b/resources/lang/ms-MY/common.php @@ -39,6 +39,7 @@ 'pin' => '', 'post' => '', 'read_more' => '', + 'refresh' => '', 'reply' => '', 'reply_reopen' => '', 'reply_resolve' => '', diff --git a/resources/lang/ms-MY/page_title.php b/resources/lang/ms-MY/page_title.php index 63320de7de0..2a6fbe54c4f 100644 --- a/resources/lang/ms-MY/page_title.php +++ b/resources/lang/ms-MY/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '', 'create' => '', diff --git a/resources/lang/ms-MY/user_cover_presets.php b/resources/lang/ms-MY/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/ms-MY/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/ms-MY/users.php b/resources/lang/ms-MY/users.php index 13b1176973e..99cfe54344e 100644 --- a/resources/lang/ms-MY/users.php +++ b/resources/lang/ms-MY/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "", ], ], diff --git a/resources/lang/nl/artist.php b/resources/lang/nl/artist.php index 8ef147fa1ab..4eb2d3ec0bf 100644 --- a/resources/lang/nl/artist.php +++ b/resources/lang/nl/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'nummers zoeken', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Geavanceerd Zoeken', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimale BPM', 'bpm_lte' => 'Maximale BPM', 'empty' => 'Er zijn geen nummers gevonden die aan de zoekcriteria voldoen.', + 'exclusive_only' => '', 'genre' => 'Genre', 'genre_all' => 'Alles', 'length_gte' => 'Minimale Lengte', diff --git a/resources/lang/nl/chat.php b/resources/lang/nl/chat.php index 2453b818ad7..b2fdef503f2 100644 --- a/resources/lang/nl/chat.php +++ b/resources/lang/nl/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Wil je dit kanaal verbergen? Je ontvangt nog steeds berichten van dit kanaal.', 'create' => 'aankondiging maken', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Publieke kanalen die je kan joinen via osu!lazer zullen hier ook zichtbaar zijn.', 'title' => 'nog geen gesprekken', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/nl/common.php b/resources/lang/nl/common.php index 8c660928bc8..e4743b1b65e 100644 --- a/resources/lang/nl/common.php +++ b/resources/lang/nl/common.php @@ -39,6 +39,7 @@ 'pin' => 'vastzetten', 'post' => 'Plaatsen', 'read_more' => 'lees meer', + 'refresh' => '', 'reply' => 'Beantwoord', 'reply_reopen' => 'Beantwoord en Heropen', 'reply_resolve' => 'Beantwoord en Los op', diff --git a/resources/lang/nl/page_title.php b/resources/lang/nl/page_title.php index 3a44938624e..a034c56bff4 100644 --- a/resources/lang/nl/page_title.php +++ b/resources/lang/nl/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'toernooien', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'speler info', 'create' => 'account aanmaken', diff --git a/resources/lang/nl/user_cover_presets.php b/resources/lang/nl/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/nl/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/nl/users.php b/resources/lang/nl/users.php index 5596c95a522..a2eeb0a4ec9 100644 --- a/resources/lang/nl/users.php +++ b/resources/lang/nl/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Je account is lange tijd niet gebruikt.", ], ], diff --git a/resources/lang/no/artist.php b/resources/lang/no/artist.php index 28f28cfd23a..a20649f9591 100644 --- a/resources/lang/no/artist.php +++ b/resources/lang/no/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'sporsøk', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Avansert Søk', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minste BPM', 'bpm_lte' => 'Maks BPM', 'empty' => 'Fant ingen spor som samsvarer med søkekriteriene.', + 'exclusive_only' => '', 'genre' => 'Sjanger', 'genre_all' => 'Alle', 'length_gte' => 'Minste Lengde', diff --git a/resources/lang/no/chat.php b/resources/lang/no/chat.php index dc1eb09564f..b15fd019f43 100644 --- a/resources/lang/no/chat.php +++ b/resources/lang/no/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Vil du skjule denne kanalen? Du vil fortsatt motta meldinger fra denne kanalen.', 'create' => 'lag kunngjøring', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Offentlige kanaler som du deltar i via osu!lazer vil også bli synlig her.', 'title' => 'ingen samtaler ennå', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/no/common.php b/resources/lang/no/common.php index 4bc328ea97b..f810019363b 100644 --- a/resources/lang/no/common.php +++ b/resources/lang/no/common.php @@ -39,6 +39,7 @@ 'pin' => 'fest', 'post' => 'Del', 'read_more' => 'les mer', + 'refresh' => '', 'reply' => 'Svar', 'reply_reopen' => 'Svar og åpne igjen', 'reply_resolve' => 'Svar og marker som løst', diff --git a/resources/lang/no/page_title.php b/resources/lang/no/page_title.php index 70f95fde9c2..9231ab4491f 100644 --- a/resources/lang/no/page_title.php +++ b/resources/lang/no/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turneringer', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'spillerinfo', 'create' => 'opprett konto', diff --git a/resources/lang/no/user_cover_presets.php b/resources/lang/no/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/no/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/no/users.php b/resources/lang/no/users.php index 4efae845369..85b14cc35ed 100644 --- a/resources/lang/no/users.php +++ b/resources/lang/no/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Kontoen din har ikke blitt brukt på lang tid.", ], ], diff --git a/resources/lang/pl/artist.php b/resources/lang/pl/artist.php index 29931a234b2..c1ad274fce1 100644 --- a/resources/lang/pl/artist.php +++ b/resources/lang/pl/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'wyszukiwarka utworów', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Wyszukiwanie zaawansowane', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimalne BPM', 'bpm_lte' => 'Maksymalne BPM', 'empty' => 'Brak utworów spełniających wybrane kryteria wyszukiwania.', + 'exclusive_only' => '', 'genre' => 'Gatunek', 'genre_all' => 'Wszystkie', 'length_gte' => 'Minimalna długość', diff --git a/resources/lang/pl/chat.php b/resources/lang/pl/chat.php index a43e68c157b..3425fca929b 100644 --- a/resources/lang/pl/chat.php +++ b/resources/lang/pl/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Czy na pewno chcesz ukryć ten kanał? Wciąż będziesz otrzymywać z niego wiadomości.', 'create' => 'utwórz ogłoszenie', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Publiczne kanały, do których dołączysz poprzez osu!lazer, także będą tutaj widoczne.', 'title' => 'brak konwersacji', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/pl/common.php b/resources/lang/pl/common.php index f588c14d189..0c0937edfd9 100644 --- a/resources/lang/pl/common.php +++ b/resources/lang/pl/common.php @@ -39,6 +39,7 @@ 'pin' => 'przypnij', 'post' => 'Opublikuj', 'read_more' => 'czytaj więcej', + 'refresh' => '', 'reply' => 'Odpowiedz', 'reply_reopen' => 'Odpowiedz i otwórz ponownie', 'reply_resolve' => 'Odpowiedz i rozwiąż', diff --git a/resources/lang/pl/page_title.php b/resources/lang/pl/page_title.php index 969e6c99e8f..a8c7ed9eaf1 100644 --- a/resources/lang/pl/page_title.php +++ b/resources/lang/pl/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnieje', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informacje o użytkowniku', 'create' => 'rejestracja', diff --git a/resources/lang/pl/score_tokens.php b/resources/lang/pl/score_tokens.php index 88f37c38cc3..7562e57c5c3 100644 --- a/resources/lang/pl/score_tokens.php +++ b/resources/lang/pl/score_tokens.php @@ -5,7 +5,7 @@ return [ 'create' => [ - 'beatmap_hash_invalid' => '', + 'beatmap_hash_invalid' => 'złe lub brakujące beatmap_hash', 'submission_disabled' => '', ], ]; diff --git a/resources/lang/pl/user_cover_presets.php b/resources/lang/pl/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/pl/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/pl/users.php b/resources/lang/pl/users.php index 7a3dd5111ae..1553beef62b 100644 --- a/resources/lang/pl/users.php +++ b/resources/lang/pl/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Brak aktywności przez długi czas.", ], ], diff --git a/resources/lang/pt-br/artist.php b/resources/lang/pt-br/artist.php index d524e8b713f..f149a7288ea 100644 --- a/resources/lang/pt-br/artist.php +++ b/resources/lang/pt-br/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'pesquisa de faixas', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Pesquisa Avançada', 'album' => 'Álbum', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Mínimo', 'bpm_lte' => 'BPM Máximo', 'empty' => 'Não foram encontradas músicas dentro do critério específico.', + 'exclusive_only' => '', 'genre' => 'Gênero', 'genre_all' => 'Todos', 'length_gte' => 'Duração Mínima ', diff --git a/resources/lang/pt-br/chat.php b/resources/lang/pt-br/chat.php index ec0b4aa8cca..d60eac60322 100644 --- a/resources/lang/pt-br/chat.php +++ b/resources/lang/pt-br/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Você quer esconder este canal? Você ainda receberá mensagens dele.', 'create' => 'criar anúncio', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Canais públicos que você entrar via osu!lazer também serão visíveis aqui.', 'title' => 'sem conversas no momento', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/pt-br/common.php b/resources/lang/pt-br/common.php index 655b52e03e9..3d85bb33c15 100644 --- a/resources/lang/pt-br/common.php +++ b/resources/lang/pt-br/common.php @@ -39,6 +39,7 @@ 'pin' => 'fixar', 'post' => 'Enviar', 'read_more' => 'leia mais', + 'refresh' => '', 'reply' => 'Responder', 'reply_reopen' => 'Responder e Reabrir', 'reply_resolve' => 'Responder e Resolver', diff --git a/resources/lang/pt-br/page_title.php b/resources/lang/pt-br/page_title.php index 22c2c5c1a6c..0007817a135 100644 --- a/resources/lang/pt-br/page_title.php +++ b/resources/lang/pt-br/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'torneios', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informações do jogador', 'create' => 'criar conta', diff --git a/resources/lang/pt-br/user_cover_presets.php b/resources/lang/pt-br/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/pt-br/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/pt-br/users.php b/resources/lang/pt-br/users.php index 032ef45f44d..07f4fc7a88b 100644 --- a/resources/lang/pt-br/users.php +++ b/resources/lang/pt-br/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Sua conta não foi usada há muito tempo.", ], ], diff --git a/resources/lang/pt/artist.php b/resources/lang/pt/artist.php index e090e77eb9a..cc32256ee9c 100644 --- a/resources/lang/pt/artist.php +++ b/resources/lang/pt/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'pesquisa de faixas', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Pesquisa avançada', 'album' => 'Álbum', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM mínimos', 'bpm_lte' => 'BPM máximos', 'empty' => 'Não foram encontradas faixas que correspondessem aos critérios de pesquisa.', + 'exclusive_only' => '', 'genre' => 'Género', 'genre_all' => 'Todas', 'length_gte' => 'Duração mínima', diff --git a/resources/lang/pt/chat.php b/resources/lang/pt/chat.php index 4ecf1c93994..442ad8f537e 100644 --- a/resources/lang/pt/chat.php +++ b/resources/lang/pt/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Queres ocultar este canal? Ainda vais receber mensagens deste canal.', 'create' => 'criar notícia', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Canais públicos que te juntes via osu!lazer também serão visíveis aqui.', 'title' => 'ainda sem conversações', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/pt/common.php b/resources/lang/pt/common.php index 42d21ee5750..da93be40646 100644 --- a/resources/lang/pt/common.php +++ b/resources/lang/pt/common.php @@ -39,6 +39,7 @@ 'pin' => 'afixar', 'post' => 'Publicar', 'read_more' => 'ler mais', + 'refresh' => '', 'reply' => 'Resposta', 'reply_reopen' => 'Responder e reabrir', 'reply_resolve' => 'Responder e resolver', diff --git a/resources/lang/pt/page_title.php b/resources/lang/pt/page_title.php index 643a4c21cb7..d72015bbfbe 100644 --- a/resources/lang/pt/page_title.php +++ b/resources/lang/pt/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'torneios', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informação do jogador', 'create' => 'criar conta', diff --git a/resources/lang/pt/user_cover_presets.php b/resources/lang/pt/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/pt/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/pt/users.php b/resources/lang/pt/users.php index 9f26b157897..9b4971cee45 100644 --- a/resources/lang/pt/users.php +++ b/resources/lang/pt/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "A tua conta não foi usada há muito tempo.", ], ], diff --git a/resources/lang/ro/artist.php b/resources/lang/ro/artist.php index 845b0c3d9d5..e448e7a4090 100644 --- a/resources/lang/ro/artist.php +++ b/resources/lang/ro/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'căutare melodii', + 'exclusive_only' => [ + 'all' => 'Toate', + 'exclusive_only' => 'creat pentru osu!', + ], + 'form' => [ 'advanced' => 'Căutare Avansată', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Minim', 'bpm_lte' => 'BPM Maxim', 'empty' => 'Nu au fost găsite melodii potrivite pentru criteriile de căutare.', + 'exclusive_only' => 'Tip', 'genre' => 'Gen', 'genre_all' => 'Toate', 'length_gte' => 'Lungime Minimă', diff --git a/resources/lang/ro/authorization.php b/resources/lang/ro/authorization.php index 179846b1a72..91f67ed6c0f 100644 --- a/resources/lang/ro/authorization.php +++ b/resources/lang/ro/authorization.php @@ -157,8 +157,8 @@ 'topic_cover' => [ 'edit' => [ - 'uneditable' => 'Fundalul specificat este invalidă.', - 'not_owner' => 'Numai creatorul poate edita fundalul.', + 'uneditable' => 'Coperta specificată este invalidă.', + 'not_owner' => 'Doar proprietarul poate edita coperta.', ], 'store' => [ 'forum_not_allowed' => 'Acest forum nu acceptă fundale pentru subiecte.', diff --git a/resources/lang/ro/chat.php b/resources/lang/ro/chat.php index 6599d8b805f..60468e6b61a 100644 --- a/resources/lang/ro/chat.php +++ b/resources/lang/ro/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Dorești să ascunzi acest canal? Vei primi în continuare mesaje de pe acest canal.', 'create' => 'creează anunţ', + 'join' => 'alătură-te canalului', + 'none' => 'nu există canale', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Canalele publice cărora li te alături prin osu!lazer vor fi vizibile aici.', 'title' => 'nu sunt conversații încă', ], + + 'join_channels' => [ + 'loading' => 'Se încarcă canalele...', + ], ]; diff --git a/resources/lang/ro/common.php b/resources/lang/ro/common.php index 7d2516cb9b2..a96445013c0 100644 --- a/resources/lang/ro/common.php +++ b/resources/lang/ro/common.php @@ -39,6 +39,7 @@ 'pin' => 'fixează', 'post' => 'Postează', 'read_more' => 'citește mai mult', + 'refresh' => 'Actualizează', 'reply' => 'Răspunde', 'reply_reopen' => 'Răspundeți și deschideți din nou', 'reply_resolve' => 'Răspunde și rezolvă', diff --git a/resources/lang/ro/forum.php b/resources/lang/ro/forum.php index ea47317eee2..60f35e3bbef 100644 --- a/resources/lang/ro/forum.php +++ b/resources/lang/ro/forum.php @@ -10,7 +10,7 @@ 'title' => 'Forum-uri osu!', 'covers' => [ - 'edit' => 'Editează fundalul', + 'edit' => 'Editează coperta', 'create' => [ '_' => 'Setează imaginea de fundal', diff --git a/resources/lang/ro/page_title.php b/resources/lang/ro/page_title.php index ee2327b417c..5600f77ca96 100644 --- a/resources/lang/ro/page_title.php +++ b/resources/lang/ro/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnee', ], + 'user_cover_presets_controller' => [ + '_' => 'preset-uri coperți utilizator', + ], 'users_controller' => [ '_' => 'info jucător', 'create' => 'creează cont', diff --git a/resources/lang/ro/user_cover_presets.php b/resources/lang/ro/user_cover_presets.php new file mode 100644 index 00000000000..c13d41a89fe --- /dev/null +++ b/resources/lang/ro/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => 'Dezactivează Selecția', + 'batch_enable' => 'Activează Selecția', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => 'o copertă|:count_delimited coperți|:count_delimited de coperți', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => 'Activat', + 'disabled' => 'Dezactivat', + 'image_store' => 'Setează Imaginea', + 'image_update' => 'Înlocuiește Imaginea', + ], + ], + 'store' => [ + 'failed' => 'A apărut o eroare la crearea coperții: :error', + 'ok' => 'Coperți create', + ], +]; diff --git a/resources/lang/ro/users.php b/resources/lang/ro/users.php index 5e8797541b3..d8c8766fe6d 100644 --- a/resources/lang/ro/users.php +++ b/resources/lang/ro/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "Contul tău nu a fost folosit de mult timp.", 'inactive_different_country' => "Contul dumneavoastră nu a fost folosit de mult timp.", ], ], @@ -192,13 +193,13 @@ 'count' => 'un comentariu|:count_delimited comentarii|:count_delimited de comentarii', ], 'cover' => [ - 'to_0' => 'Ascunde fundal', - 'to_1' => 'Afișează fundal', + 'to_0' => 'Ascunde coperta', + 'to_1' => 'Afișează coperta', ], 'edit' => [ 'cover' => [ 'button' => 'Schimbă coperta de profil', - 'defaults_info' => 'Mai multe opțiuni de coperte vor fi disponibile în viitor', + 'defaults_info' => 'Mai multe opțiuni pentru coperte vor fi disponibile în viitor', 'upload' => [ 'broken_file' => 'Imposibil de procesat imaginea. Verifică imaginea încărcată și încearcă din nou.', 'button' => 'Încarcă imaginea', diff --git a/resources/lang/ru/artist.php b/resources/lang/ru/artist.php index 09c9d3d57e5..e98e2672819 100644 --- a/resources/lang/ru/artist.php +++ b/resources/lang/ru/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'поиск треков', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Расширенный поиск', 'album' => 'Альбом', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Минимальный BPM', 'bpm_lte' => 'Максимальный BPM', 'empty' => 'Треков, соответствующих критериям поиска, не найдено.', + 'exclusive_only' => '', 'genre' => 'Жанр', 'genre_all' => 'Все', 'length_gte' => 'Минимальная длина', diff --git a/resources/lang/ru/chat.php b/resources/lang/ru/chat.php index d1ed4c6dcd1..724147e090d 100644 --- a/resources/lang/ru/chat.php +++ b/resources/lang/ru/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Вы точно хотите скрыть этот канал? Вы всё ещё будете получать сообщения из него.', 'create' => 'создать объявление', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Общие каналы, к которым Вы присоединились через osu!lazer, также видны здесь.', 'title' => 'диалогов нет', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/ru/common.php b/resources/lang/ru/common.php index 38d75842edb..5647ce6e783 100644 --- a/resources/lang/ru/common.php +++ b/resources/lang/ru/common.php @@ -39,6 +39,7 @@ 'pin' => 'закрепить', 'post' => 'Отправить', 'read_more' => 'читать дальше', + 'refresh' => '', 'reply' => 'Ответить', 'reply_reopen' => 'Ответить и переоткрыть', 'reply_resolve' => 'Ответить и решить', diff --git a/resources/lang/ru/page_title.php b/resources/lang/ru/page_title.php index f047ad6d998..05ae2d43860 100644 --- a/resources/lang/ru/page_title.php +++ b/resources/lang/ru/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'турниры', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'информация об игроке', 'create' => 'создать аккаунт', diff --git a/resources/lang/ru/user_cover_presets.php b/resources/lang/ru/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/ru/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/ru/users.php b/resources/lang/ru/users.php index b86eba318f4..e5b5a2bc982 100644 --- a/resources/lang/ru/users.php +++ b/resources/lang/ru/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Вы не пользовались аккаунтом в течение долгого времени.", ], ], diff --git a/resources/lang/si-LK/artist.php b/resources/lang/si-LK/artist.php index 4783fef3356..7ecad38752f 100644 --- a/resources/lang/si-LK/artist.php +++ b/resources/lang/si-LK/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => '', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => '', 'album' => '', @@ -52,6 +57,7 @@ 'bpm_gte' => '', 'bpm_lte' => '', 'empty' => '', + 'exclusive_only' => '', 'genre' => '', 'genre_all' => '', 'length_gte' => '', diff --git a/resources/lang/si-LK/chat.php b/resources/lang/si-LK/chat.php index e17466c9da2..720b47cfb0f 100644 --- a/resources/lang/si-LK/chat.php +++ b/resources/lang/si-LK/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '', 'create' => '', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => '', 'title' => '', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/si-LK/common.php b/resources/lang/si-LK/common.php index 57deb34d804..8dc455b4c2c 100644 --- a/resources/lang/si-LK/common.php +++ b/resources/lang/si-LK/common.php @@ -39,6 +39,7 @@ 'pin' => '', 'post' => '', 'read_more' => '', + 'refresh' => '', 'reply' => '', 'reply_reopen' => '', 'reply_resolve' => '', diff --git a/resources/lang/si-LK/page_title.php b/resources/lang/si-LK/page_title.php index 63320de7de0..2a6fbe54c4f 100644 --- a/resources/lang/si-LK/page_title.php +++ b/resources/lang/si-LK/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '', 'create' => '', diff --git a/resources/lang/si-LK/user_cover_presets.php b/resources/lang/si-LK/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/si-LK/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/si-LK/users.php b/resources/lang/si-LK/users.php index 13b1176973e..99cfe54344e 100644 --- a/resources/lang/si-LK/users.php +++ b/resources/lang/si-LK/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "", ], ], diff --git a/resources/lang/sk/artist.php b/resources/lang/sk/artist.php index edeba59c72c..ef53e2ce012 100644 --- a/resources/lang/sk/artist.php +++ b/resources/lang/sk/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'vyhľadávanie skladieb', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Rozšírené Vyhľadávanie', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimálne BPM', 'bpm_lte' => 'MaximálneBPM', 'empty' => 'Nenašli sa žiadne skladby zodpovedajúce kritériám vyhľadávania.', + 'exclusive_only' => '', 'genre' => 'Žáner', 'genre_all' => 'Všetko', 'length_gte' => 'Minimálna dĺžka', diff --git a/resources/lang/sk/chat.php b/resources/lang/sk/chat.php index d99dc3822c2..1b0cfa7a1fb 100644 --- a/resources/lang/sk/chat.php +++ b/resources/lang/sk/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '', 'create' => '', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Verejné kanály na ktoré sa pripojíte cez osu!lazer budú viditeľné aj tu.', 'title' => 'zatiaľ žiadne konverzácie', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/sk/common.php b/resources/lang/sk/common.php index 8cfcaca4d43..7bdc0c3c3f1 100644 --- a/resources/lang/sk/common.php +++ b/resources/lang/sk/common.php @@ -39,6 +39,7 @@ 'pin' => 'pripnúť', 'post' => 'Príspevok', 'read_more' => 'čítať viac', + 'refresh' => '', 'reply' => 'Odpovedať', 'reply_reopen' => 'Odpovedať a znova otvoriť', 'reply_resolve' => 'Odpovedať a archivovať', diff --git a/resources/lang/sk/page_title.php b/resources/lang/sk/page_title.php index adf94e2ec65..530bdfe3fed 100644 --- a/resources/lang/sk/page_title.php +++ b/resources/lang/sk/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '', 'create' => '', diff --git a/resources/lang/sk/user_cover_presets.php b/resources/lang/sk/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/sk/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/sk/users.php b/resources/lang/sk/users.php index 8e38c613f3a..c858d11642d 100644 --- a/resources/lang/sk/users.php +++ b/resources/lang/sk/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "", ], ], diff --git a/resources/lang/sl/artist.php b/resources/lang/sl/artist.php index 99663c98375..434b2c84ebc 100644 --- a/resources/lang/sl/artist.php +++ b/resources/lang/sl/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'iskanje pesmi', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Napredno iskanje', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimalni BPM', 'bpm_lte' => 'Maksimalni BPM', 'empty' => 'Najdena ni bila nobena pesem, ki bi ustrezala iskalnim kriterijem.', + 'exclusive_only' => '', 'genre' => 'Žanr', 'genre_all' => 'Vse', 'length_gte' => 'Minimalno trajanje', diff --git a/resources/lang/sl/chat.php b/resources/lang/sl/chat.php index 3a557ea8267..73410d4a675 100644 --- a/resources/lang/sl/chat.php +++ b/resources/lang/sl/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Ali želiš skriti ta kanal? Kljub temu boš še vedno dobil sporočila iz tega kanala.', 'create' => 'ustvari obvestilo', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Javni kanali, ki se jim pridružiš preko osu!lazer bodo tudi prikazani tukaj.', 'title' => 'še ni pogovorov', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/sl/common.php b/resources/lang/sl/common.php index 83dea57c650..6f587160f8b 100644 --- a/resources/lang/sl/common.php +++ b/resources/lang/sl/common.php @@ -39,6 +39,7 @@ 'pin' => 'pripni', 'post' => 'Objavi', 'read_more' => 'preberi več', + 'refresh' => '', 'reply' => 'Odgovori', 'reply_reopen' => 'Odgovori in ponovno odpri', 'reply_resolve' => 'Odgovori in reši', diff --git a/resources/lang/sl/page_title.php b/resources/lang/sl/page_title.php index 3fdc96cdba8..1e6e365fac3 100644 --- a/resources/lang/sl/page_title.php +++ b/resources/lang/sl/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnirji', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'informacije o igralcu', 'create' => 'ustvari račun', diff --git a/resources/lang/sl/user_cover_presets.php b/resources/lang/sl/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/sl/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/sl/users.php b/resources/lang/sl/users.php index 2a8b0d66e06..43483bec368 100644 --- a/resources/lang/sl/users.php +++ b/resources/lang/sl/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Tvoj račun že dlje časa ni bil uporabljen.", ], ], diff --git a/resources/lang/sr/artist.php b/resources/lang/sr/artist.php index acb7ce592f1..5eba5cd6815 100644 --- a/resources/lang/sr/artist.php +++ b/resources/lang/sr/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'тражење песме', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Напредна претрага', 'album' => 'Албум', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM Минимум', 'bpm_lte' => 'BPM Максимум', 'empty' => 'Не постоји која песма која одговара вашем критеријуму.', + 'exclusive_only' => '', 'genre' => 'Жанр', 'genre_all' => 'Све', 'length_gte' => 'Минимална Дужина', diff --git a/resources/lang/sr/chat.php b/resources/lang/sr/chat.php index 6fa543a5706..6faf3b18136 100644 --- a/resources/lang/sr/chat.php +++ b/resources/lang/sr/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Да ли желите да сакријете овај канал? И даље ће те примати поруке са овог канала.', 'create' => 'креирај најаву', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Јавни канали којима се придружите преко osu!lazer ће такође бити видљиви овде.', 'title' => 'још нема разговора', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/sr/common.php b/resources/lang/sr/common.php index fe3436d1090..3387c7cdde8 100644 --- a/resources/lang/sr/common.php +++ b/resources/lang/sr/common.php @@ -39,6 +39,7 @@ 'pin' => 'закачи', 'post' => 'Објавите', 'read_more' => 'прочитај више', + 'refresh' => '', 'reply' => 'Одговорите', 'reply_reopen' => 'Одговорите и отворите поново', 'reply_resolve' => 'Одговорите и решите', diff --git a/resources/lang/sr/page_title.php b/resources/lang/sr/page_title.php index a5c8e7bcb72..779343d5bf6 100644 --- a/resources/lang/sr/page_title.php +++ b/resources/lang/sr/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'турнири', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'информације о играчу', 'create' => 'креирај налог', diff --git a/resources/lang/sr/user_cover_presets.php b/resources/lang/sr/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/sr/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/sr/users.php b/resources/lang/sr/users.php index 8e4953e8a90..ea6c4478625 100644 --- a/resources/lang/sr/users.php +++ b/resources/lang/sr/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Ваш налог није коришћен дужи период времена.", ], ], diff --git a/resources/lang/sv/accounts.php b/resources/lang/sv/accounts.php index ce14b848918..b2df747f42a 100644 --- a/resources/lang/sv/accounts.php +++ b/resources/lang/sv/accounts.php @@ -71,7 +71,7 @@ 'error' => [ 'already_linked' => 'Detta GitHub-konto är redan kopplat till en annan användare.', 'no_contribution' => 'Kan inte länka GitHub-konto utan någon medverkningshistorik i osu! utvecklingskataloger.', - 'unverified_email' => '', + 'unverified_email' => 'Vänligen verifiera din primära e-postadress på GitHub, försök sedan att länka ditt konto igen.', ], ], diff --git a/resources/lang/sv/artist.php b/resources/lang/sv/artist.php index 7b4e5da0deb..d152b5cfb6a 100644 --- a/resources/lang/sv/artist.php +++ b/resources/lang/sv/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'spårsökning', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Avancerad sökning', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minsta BPM', 'bpm_lte' => 'Högsta BPM', 'empty' => 'Inga spår som matchade sökfiltret hittades.', + 'exclusive_only' => '', 'genre' => 'Genre', 'genre_all' => 'Alla', 'length_gte' => 'Minsta längd', diff --git a/resources/lang/sv/authorization.php b/resources/lang/sv/authorization.php index a16a94c64b4..7da26c46d2f 100644 --- a/resources/lang/sv/authorization.php +++ b/resources/lang/sv/authorization.php @@ -81,7 +81,7 @@ ], 'contest' => [ - 'judging_not_active' => '', + 'judging_not_active' => 'Bedömning för denna tävling är ej aktiv.', 'voting_over' => 'Du kan inte ändra din röst efter att röstperioden för den här tävlingen har avslutas.', 'entry' => [ @@ -188,7 +188,7 @@ ], ], 'update_email' => [ - 'locked' => '', + 'locked' => 'e-postaddress är låst', ], ], ]; diff --git a/resources/lang/sv/beatmaps.php b/resources/lang/sv/beatmaps.php index 85ecb129c41..d7e66b03c5c 100644 --- a/resources/lang/sv/beatmaps.php +++ b/resources/lang/sv/beatmaps.php @@ -82,7 +82,7 @@ 'disqualify' => '', 'hype' => '', 'mapper_note' => '', - 'nomination_reset' => '', + 'nomination_reset' => 'Ta bort alla Nomineringar', 'praise' => '', 'problem' => '', 'problem_warning' => '', diff --git a/resources/lang/sv/chat.php b/resources/lang/sv/chat.php index 4d6b7c12957..86803aa4ec6 100644 --- a/resources/lang/sv/chat.php +++ b/resources/lang/sv/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Vill du dölja denna kanal? Du kommer fortfarande ta emot meddelanden från denna kanal.', 'create' => 'skapa meddelande', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Offentliga kanaler du går med i via osu!lazer kommer också att synas här.', 'title' => 'inga konversationer ännu', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/sv/comments.php b/resources/lang/sv/comments.php index 0c781dcad8b..3660b6e8c71 100644 --- a/resources/lang/sv/comments.php +++ b/resources/lang/sv/comments.php @@ -44,7 +44,7 @@ ], 'ogp' => [ - 'title' => '', + 'title' => 'kommentar av :user', ], 'placeholder' => [ diff --git a/resources/lang/sv/common.php b/resources/lang/sv/common.php index a44bab5d0cf..9d654d87493 100644 --- a/resources/lang/sv/common.php +++ b/resources/lang/sv/common.php @@ -39,6 +39,7 @@ 'pin' => 'fäst', 'post' => 'Lägg upp', 'read_more' => 'läs mer', + 'refresh' => '', 'reply' => 'Svara', 'reply_reopen' => 'Svara och öppna igen', 'reply_resolve' => 'Svara och lös', diff --git a/resources/lang/sv/contest.php b/resources/lang/sv/contest.php index 5ca455e7783..98c1e21e2f0 100644 --- a/resources/lang/sv/contest.php +++ b/resources/lang/sv/contest.php @@ -14,27 +14,27 @@ ], 'judge' => [ - 'hide_judged' => '', - 'nav_title' => '', - 'no_current_vote' => '', - 'update' => '', + 'hide_judged' => 'dölj bedömda bidrag', + 'nav_title' => 'bedöm', + 'no_current_vote' => 'du har inte röstat ännu.', + 'update' => 'uppdatera', 'validation' => [ 'missing_score' => '', - 'contest_vote_judged' => '', + 'contest_vote_judged' => 'kan inte rösta i bedömda tävlingar', ], - 'voted' => '', + 'voted' => 'Du har redan röstat på detta bidrag.', ], 'judge_results' => [ - '_' => '', - 'creator' => '', - 'score' => '', - 'total_score' => '', + '_' => 'Bedömningsresultat', + 'creator' => 'skapare', + 'score' => 'Poäng', + 'total_score' => 'total poäng', ], 'voting' => [ - 'judge_link' => '', - 'judged_notice' => '', + 'judge_link' => 'Du är domare i denna tävling. Bedöm bidragen här!', + 'judged_notice' => 'Denna tävling använder bedömningssystemet, bedömarna bearbetar för närvarande bidragen.', 'login_required' => 'Var vänlig logga in för att rösta.', 'over' => 'Möjligheten att rösta i denna tävling har avslutats', 'show_voted_only' => 'Visa röstade', diff --git a/resources/lang/sv/page_title.php b/resources/lang/sv/page_title.php index 49850686c75..cfc64350162 100644 --- a/resources/lang/sv/page_title.php +++ b/resources/lang/sv/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turneringar', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'spelarinformation', 'create' => 'skapa konto', diff --git a/resources/lang/sv/password_reset.php b/resources/lang/sv/password_reset.php index 0da9a25e61b..828da47484e 100644 --- a/resources/lang/sv/password_reset.php +++ b/resources/lang/sv/password_reset.php @@ -37,7 +37,7 @@ 'username' => 'Fyll i din e-postadress eller ditt användarnamn', 'reason' => [ - 'inactive_different_country' => "", + 'inactive_different_country' => "Ditt konto har inte använts på länge. För att säkerställa ditt kontos säkerhet vänligen återställ ditt lösenord.", ], 'support' => [ '_' => 'Behöver du mer hjälp? Kontakta oss via vår :button.', diff --git a/resources/lang/sv/scores.php b/resources/lang/sv/scores.php index e0bb4ba7497..af3b6ec0a49 100644 --- a/resources/lang/sv/scores.php +++ b/resources/lang/sv/scores.php @@ -5,7 +5,7 @@ return [ 'show' => [ - 'non_preserved' => '', + 'non_preserved' => 'Detta resultat är markerat för borttagning och kommer att försvinna snart.', 'title' => ':username på :title [:version]', 'beatmap' => [ @@ -24,10 +24,10 @@ ], 'status' => [ - 'non_best' => 'Endast personbästa poäng ger pp', - 'non_passing' => 'Endast godkända poäng ger pp', - 'no_pp' => '', - 'processing' => 'Poängsiffran räknas ännu ut och kommer snart att visas', - 'no_rank' => '', + 'non_best' => 'Endast personbästa resultat ger pp', + 'non_passing' => 'Endast godkända resultat ger pp', + 'no_pp' => 'pp tilldelas inte för detta resultat', + 'processing' => 'Detta resultat räknas ännu ut och kommer snart att visas', + 'no_rank' => 'Detta resultat har ingen rank eftersom den är orankad eller markerad för borttagning', ], ]; diff --git a/resources/lang/sv/store.php b/resources/lang/sv/store.php index 3062f48cbbb..62612ac4f0f 100644 --- a/resources/lang/sv/store.php +++ b/resources/lang/sv/store.php @@ -49,36 +49,36 @@ ], 'discount' => 'spara :percent%', - 'free' => '', + 'free' => 'gratis!', 'invoice' => [ - 'contact' => '', - 'date' => '', + 'contact' => 'Kontakt:', + 'date' => 'Datum:', 'echeck_delay' => 'Eftersom din betalning var en eCheck, vänligen tillåt upp till 10 extra dagar för betalningen att accepteras via PayPal! ', 'hide_from_activity' => 'osu!supporter taggar i denna ordning visas inte i dina senaste aktiviteter.', - 'sent_via' => '', - 'shipping_to' => '', - 'title' => '', + 'sent_via' => 'Skickat Via:', + 'shipping_to' => 'Levereras Till:', + 'title' => 'Faktura', 'title_compact' => 'faktura', 'status' => [ 'cancelled' => [ - 'title' => '', + 'title' => 'Din beställning har avbrutits', 'line_1' => [ '_' => "", - 'link_text' => '', + 'link_text' => 'osu!store support', ], ], 'delivered' => [ 'title' => '', 'line_1' => [ - '_' => '', - 'link_text' => '', + '_' => 'Om du har några problem med ditt köp, vänligen kontakta :link.', + 'link_text' => 'osu!store support', ], ], 'prepared' => [ - 'title' => '', - 'line_1' => '', + 'title' => 'Din beställning förbereds!', + 'line_1' => 'Vänligen vänta lite längre på att den ska skickas. Spårningsinformation kommer att visas här när beställningen har behandlats och skickats. Detta kan ta upp till 5 dagar (men oftast mindre!) beroende på hur upptagna vi är.', 'line_2' => '', ], 'processing' => [ @@ -90,11 +90,11 @@ ], ], 'shipped' => [ - 'title' => '', - 'tracking_details' => '', + 'title' => 'Din beställning har skickats!', + 'tracking_details' => 'Spårningsinformation följer:', 'no_tracking_details' => [ '_' => "", - 'link_text' => '', + 'link_text' => 'skicka oss ett e-post', ], ], ], @@ -108,18 +108,18 @@ 'no_orders' => 'Inga beställningar att visa.', 'paid_on' => 'Beställning slutförd :date', 'resume' => 'Återuppta transaktionen', - 'shipping_and_handling' => '', + 'shipping_and_handling' => 'Frakt & Hantering', 'shopify_expired' => 'Kassalänken för denna beställning har utgått.', - 'subtotal' => '', - 'total' => '', + 'subtotal' => 'Delsumma', + 'total' => 'Summa', 'details' => [ - 'order_number' => '', - 'payment_terms' => '', - 'salesperson' => '', - 'shipping_method' => '', - 'shipping_terms' => '', - 'title' => '', + 'order_number' => 'Beställning #', + 'payment_terms' => 'Betalningsvillkor', + 'salesperson' => 'Försäljare', + 'shipping_method' => 'Leveransmetod', + 'shipping_terms' => 'Leveransvillkor', + 'title' => 'Beställningsinformation', ], 'item' => [ @@ -151,14 +151,14 @@ 'paid' => 'Betalt', 'processing' => 'Väntar på bekräftelse', 'shipped' => 'Skickad', - 'title' => '', + 'title' => 'Beställningsstatus', ], 'thanks' => [ - 'title' => '', + 'title' => 'Tack för din beställning!', 'line_1' => [ - '_' => '', - 'link_text' => '', + '_' => 'Du kommer att få ett bekräftelsemail snart. Om du har några frågor, vänligen :link!', + 'link_text' => 'kontakta oss', ], ], ], @@ -193,9 +193,9 @@ 'username_change' => [ 'check' => 'Skriv in ett användarnamn för att kontrollera tillgänglighet!', 'checking' => 'Kontrollerar om :username är tillgängligt...', - 'placeholder' => '', - 'label' => '', - 'current' => '', + 'placeholder' => 'Begärt Användarnamn', + 'label' => 'Nytt Användarnamn', + 'current' => 'Ditt nuvarande användarnamn är ":username".', 'require_login' => [ '_' => 'Du behöver var :link för att ändra ditt namn!', diff --git a/resources/lang/sv/user_cover_presets.php b/resources/lang/sv/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/sv/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/sv/users.php b/resources/lang/sv/users.php index 37e0c1906b7..8724c3ae0c9 100644 --- a/resources/lang/sv/users.php +++ b/resources/lang/sv/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Ditt konto har inte använts på länge.", ], ], @@ -124,8 +125,8 @@ ], 'ogp' => [ - 'modding_description' => '', - 'modding_description_empty' => '', + 'modding_description' => 'Beatmaps: :counts', + 'modding_description_empty' => 'Användare har inga beatmaps...', 'description' => [ '_' => 'Rank (:regelset): :global | :land', diff --git a/resources/lang/sv/wiki.php b/resources/lang/sv/wiki.php index 18ad67d2b0a..afe66aa45b9 100644 --- a/resources/lang/sv/wiki.php +++ b/resources/lang/sv/wiki.php @@ -7,17 +7,17 @@ 'show' => [ 'fallback_translation' => 'Den begärda sidan är inte översatt till det valda språket (:language). Visar den engelska versionen.', 'incomplete_or_outdated' => 'Innehållet på denna sidan är ofullständigt eller föråldrat. Om du har möjligheten att hjälpa till, vänligen överväg att uppdatera artikeln!', - 'missing' => 'Begärd sida kunde ej hittas.', + 'missing' => 'Den begärda sidan ":keyword" kunde inte hittas.', 'missing_title' => 'Hittades inte', 'missing_translation' => 'Den begärda sidan kunde inte hittas för nuvarande valt språk.', 'needs_cleanup_or_rewrite' => 'Denna sidan lever inte upp till standarden för osu!-wikin och behöver städas upp eller skrivas om. Om du har möjlighet att hjälpa till, vänligen överväg att uppdatera artikeln!', - 'search' => 'Sök befintliga sidor för: länk.', + 'search' => 'Sök befintliga sidor för :link.', 'stub' => 'Den här artikeln är ofullständig och väntar på att någon ska utöka den.', 'toc' => 'Innehåll', 'edit' => [ 'link' => 'Visa på GitHub', - 'refresh' => 'Ladda om', + 'refresh' => 'Uppdatera', ], 'translation' => [ diff --git a/resources/lang/tg-TJ/artist.php b/resources/lang/tg-TJ/artist.php index 4783fef3356..7ecad38752f 100644 --- a/resources/lang/tg-TJ/artist.php +++ b/resources/lang/tg-TJ/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => '', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => '', 'album' => '', @@ -52,6 +57,7 @@ 'bpm_gte' => '', 'bpm_lte' => '', 'empty' => '', + 'exclusive_only' => '', 'genre' => '', 'genre_all' => '', 'length_gte' => '', diff --git a/resources/lang/tg-TJ/chat.php b/resources/lang/tg-TJ/chat.php index e17466c9da2..720b47cfb0f 100644 --- a/resources/lang/tg-TJ/chat.php +++ b/resources/lang/tg-TJ/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '', 'create' => '', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => '', 'title' => '', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/tg-TJ/common.php b/resources/lang/tg-TJ/common.php index 57deb34d804..8dc455b4c2c 100644 --- a/resources/lang/tg-TJ/common.php +++ b/resources/lang/tg-TJ/common.php @@ -39,6 +39,7 @@ 'pin' => '', 'post' => '', 'read_more' => '', + 'refresh' => '', 'reply' => '', 'reply_reopen' => '', 'reply_resolve' => '', diff --git a/resources/lang/tg-TJ/page_title.php b/resources/lang/tg-TJ/page_title.php index 63320de7de0..2a6fbe54c4f 100644 --- a/resources/lang/tg-TJ/page_title.php +++ b/resources/lang/tg-TJ/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '', 'create' => '', diff --git a/resources/lang/tg-TJ/user_cover_presets.php b/resources/lang/tg-TJ/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/tg-TJ/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/tg-TJ/users.php b/resources/lang/tg-TJ/users.php index 13b1176973e..99cfe54344e 100644 --- a/resources/lang/tg-TJ/users.php +++ b/resources/lang/tg-TJ/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "", ], ], diff --git a/resources/lang/th/artist.php b/resources/lang/th/artist.php index 199e3e5e3aa..04ce8614d93 100644 --- a/resources/lang/th/artist.php +++ b/resources/lang/th/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'ค้นหาแทร็ก', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'การค้นหาขั้นสูง', 'album' => 'อัลบั้ม', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM ต่ำสุด', 'bpm_lte' => 'BPM สูงสุด', 'empty' => 'ไม่พบแทร็กที่ตรงกับเกณฑ์การค้นหา', + 'exclusive_only' => '', 'genre' => 'ประเภท', 'genre_all' => 'ทั้งหมด', 'length_gte' => 'ความยาวต่ำสุด', diff --git a/resources/lang/th/chat.php b/resources/lang/th/chat.php index 84beb8bd325..1b8d8420ffb 100644 --- a/resources/lang/th/chat.php +++ b/resources/lang/th/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'คุณต้องการที่จะซ่อนช่องนี้หรือไม่ คุณจะยังคงได้รับข้อความจากช่องนี้', 'create' => 'สร้างการประกาศ', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'แชแนลสาธารณะที่คุณเข้าผ่าน osu!lazer จะมาแสดงที่นี่', 'title' => 'ยังไม่มีการสนทนา', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/th/common.php b/resources/lang/th/common.php index a3349579089..e636d096048 100644 --- a/resources/lang/th/common.php +++ b/resources/lang/th/common.php @@ -39,6 +39,7 @@ 'pin' => 'ปักหมุด', 'post' => 'โพสต์', 'read_more' => 'อ่านเพิ่มเติม', + 'refresh' => '', 'reply' => 'ตอบกลับ', 'reply_reopen' => 'ตอบกลับ และเปิดใหม่', 'reply_resolve' => 'ตอบกลับ และแก้ไข', diff --git a/resources/lang/th/page_title.php b/resources/lang/th/page_title.php index 4798f2d9a3c..0df766ff9d1 100644 --- a/resources/lang/th/page_title.php +++ b/resources/lang/th/page_title.php @@ -111,6 +111,9 @@ '_' => ' ทัวร์นาเมนต์', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'ข้อมูลผู้เล่น', 'create' => 'สร้างบัญชี', diff --git a/resources/lang/th/user_cover_presets.php b/resources/lang/th/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/th/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/th/users.php b/resources/lang/th/users.php index 122696317c4..3524f156db0 100644 --- a/resources/lang/th/users.php +++ b/resources/lang/th/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "บัญชีของคุณไม่ได้ใช้งานมานาน", ], ], diff --git a/resources/lang/tr/artist.php b/resources/lang/tr/artist.php index 34fefe33738..886ce790e7f 100644 --- a/resources/lang/tr/artist.php +++ b/resources/lang/tr/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'parça arama', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Gelişmiş Arama', 'album' => 'Albüm', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Minimum BPM', 'bpm_lte' => 'Maksimum BPM', 'empty' => 'Arama kriterine uyan parça bulunamadı.', + 'exclusive_only' => '', 'genre' => 'Tür', 'genre_all' => 'Tümü', 'length_gte' => 'Minimum Uzunluk', diff --git a/resources/lang/tr/chat.php b/resources/lang/tr/chat.php index 5d51e9ba637..a76a8190e3d 100644 --- a/resources/lang/tr/chat.php +++ b/resources/lang/tr/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Bu kanalı gizlemek istiyor musunuz? Bu kanaldan mesaj almaya devam edeceksiniz.', 'create' => 'duyuru oluştur', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'osu!lazer aracılığıyla katıldığınız herkese açık kanallar burada da görünür olacak.', 'title' => 'henüz konuşma yok', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/tr/common.php b/resources/lang/tr/common.php index a39c7af2dae..1e1368161ff 100644 --- a/resources/lang/tr/common.php +++ b/resources/lang/tr/common.php @@ -39,6 +39,7 @@ 'pin' => 'sabitle', 'post' => 'Gönder', 'read_more' => 'daha fazlası', + 'refresh' => '', 'reply' => 'Cevapla', 'reply_reopen' => 'Cevapla ve Yeniden Aç', 'reply_resolve' => 'Yanıtla ve Çözümle', diff --git a/resources/lang/tr/page_title.php b/resources/lang/tr/page_title.php index d2d5db51a2b..6f991874bc6 100644 --- a/resources/lang/tr/page_title.php +++ b/resources/lang/tr/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'turnuvalar', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'oyuncu bilgisi', 'create' => 'hesap oluştur', diff --git a/resources/lang/tr/user_cover_presets.php b/resources/lang/tr/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/tr/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/tr/users.php b/resources/lang/tr/users.php index de62d5a56d5..33e44fb165b 100644 --- a/resources/lang/tr/users.php +++ b/resources/lang/tr/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Hesabın uzun bir zamandır kullanılmamıştır.", ], ], diff --git a/resources/lang/uk/artist.php b/resources/lang/uk/artist.php index 5d1e3759331..d05b4c5235d 100644 --- a/resources/lang/uk/artist.php +++ b/resources/lang/uk/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'пошук треків', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Розширений Пошук ', 'album' => 'Альбом ', @@ -52,6 +57,7 @@ 'bpm_gte' => 'Мінімальний BPM', 'bpm_lte' => 'Максимальний BPM', 'empty' => 'Не знайдено жодного треку, що відповідає критеріям пошуку.', + 'exclusive_only' => '', 'genre' => 'Жанр ', 'genre_all' => 'Всі ', 'length_gte' => 'Мінімальна Тривалість', diff --git a/resources/lang/uk/chat.php b/resources/lang/uk/chat.php index 7b65d8bd82b..2a42dbe7f2f 100644 --- a/resources/lang/uk/chat.php +++ b/resources/lang/uk/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Ви хочете сховати цей канал? Ви все ще будете отримувати з нього повідомлення.', 'create' => 'створити оголошення', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Публічні канали до яких ви долучилися через osu!lazer також будуть видимі тут.', 'title' => 'немає розмов', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/uk/common.php b/resources/lang/uk/common.php index 7ff58be965a..5cc72518afc 100644 --- a/resources/lang/uk/common.php +++ b/resources/lang/uk/common.php @@ -39,6 +39,7 @@ 'pin' => 'закрiпити', 'post' => 'Опублікувати', 'read_more' => 'докладніше', + 'refresh' => '', 'reply' => 'Відповісти', 'reply_reopen' => 'Відповісти та відновити', 'reply_resolve' => 'Відповісти та дозволити', diff --git a/resources/lang/uk/page_title.php b/resources/lang/uk/page_title.php index 9d73f5a4028..bb843327882 100644 --- a/resources/lang/uk/page_title.php +++ b/resources/lang/uk/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => 'турніри', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'інформація про гравця', 'create' => 'створити обліковий запис', diff --git a/resources/lang/uk/user_cover_presets.php b/resources/lang/uk/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/uk/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/uk/users.php b/resources/lang/uk/users.php index cbb00fe34e6..1904f7e2f40 100644 --- a/resources/lang/uk/users.php +++ b/resources/lang/uk/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Ваш обліковий запис вже давно не використовується.", ], ], diff --git a/resources/lang/vi/artist.php b/resources/lang/vi/artist.php index 59b109b15ed..e9333a5768c 100644 --- a/resources/lang/vi/artist.php +++ b/resources/lang/vi/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => 'tìm nhạc', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => 'Tìm Kiếm Nâng Cao', 'album' => 'Album', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM tối thiểu', 'bpm_lte' => 'BPM tối đa', 'empty' => 'Không có bài hát nào khớp với bộ lọc tìm kiếm.', + 'exclusive_only' => '', 'genre' => 'Thể loại', 'genre_all' => 'Tất cả', 'length_gte' => 'Độ dài tối thiểu', diff --git a/resources/lang/vi/chat.php b/resources/lang/vi/chat.php index 528f511a0e1..50daf2a120f 100644 --- a/resources/lang/vi/chat.php +++ b/resources/lang/vi/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => 'Bạn có muốn ẩn kênh này không? Bạn vẫn sẽ nhận được tin nhắn từ kênh này.', 'create' => 'tạo thông báo ', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => 'Những kênh công khai bạn tham gia qua osu!lazer cũng sẽ hiển thị tại đây.', 'title' => 'chưa có cuộc trò chuyện nào', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/vi/common.php b/resources/lang/vi/common.php index 7ae16f65766..4384711dfa6 100644 --- a/resources/lang/vi/common.php +++ b/resources/lang/vi/common.php @@ -40,6 +40,7 @@ 'pin' => 'ghim', 'post' => 'Đăng', 'read_more' => 'hiển thị thêm', + 'refresh' => '', 'reply' => 'Trả lời', 'reply_reopen' => 'Trả lời và mở lại', 'reply_resolve' => 'Trả lời và giải quyết', diff --git a/resources/lang/vi/page_title.php b/resources/lang/vi/page_title.php index a70ca623aed..68db75dd6c2 100644 --- a/resources/lang/vi/page_title.php +++ b/resources/lang/vi/page_title.php @@ -111,6 +111,9 @@ 'tournaments_controller' => [ '_' => 'giải đấu', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => 'thông tin người chơi', 'create' => 'tạo tài khoản', diff --git a/resources/lang/vi/user_cover_presets.php b/resources/lang/vi/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/vi/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/vi/users.php b/resources/lang/vi/users.php index 00bd1e94d32..e61761c9211 100644 --- a/resources/lang/vi/users.php +++ b/resources/lang/vi/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "Tài khoản của bạn đã không sử dụng trong một thời gian dài.", ], ], diff --git a/resources/lang/zh-tw/artist.php b/resources/lang/zh-tw/artist.php index 9f991b4a3c4..5ce4f5e4129 100644 --- a/resources/lang/zh-tw/artist.php +++ b/resources/lang/zh-tw/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => '歌曲搜尋', + 'exclusive_only' => [ + 'all' => '', + 'exclusive_only' => '', + ], + 'form' => [ 'advanced' => '進階搜尋', 'album' => '專輯', @@ -52,6 +57,7 @@ 'bpm_gte' => 'BPM 最小值', 'bpm_lte' => 'BPM 最大值', 'empty' => '找不到符合條件的歌曲。', + 'exclusive_only' => '', 'genre' => '曲風', 'genre_all' => '全部', 'length_gte' => '長度最小值', diff --git a/resources/lang/zh-tw/chat.php b/resources/lang/zh-tw/chat.php index abb338b8344..972430308c5 100644 --- a/resources/lang/zh-tw/chat.php +++ b/resources/lang/zh-tw/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '您是否要隱藏本頻道?您仍然會收到這個頻道的訊息。', 'create' => '建立公告', + 'join' => '', + 'none' => '', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => '您通過 osu! lazer 加入的公開頻道也會顯示在這裡。', 'title' => '還沒有聊天過', ], + + 'join_channels' => [ + 'loading' => '', + ], ]; diff --git a/resources/lang/zh-tw/common.php b/resources/lang/zh-tw/common.php index 15ff2669cdc..b9a80e895a4 100644 --- a/resources/lang/zh-tw/common.php +++ b/resources/lang/zh-tw/common.php @@ -39,6 +39,7 @@ 'pin' => '置頂', 'post' => '發表', 'read_more' => '查看詳情', + 'refresh' => '', 'reply' => '回覆', 'reply_reopen' => '回覆並重新打開', 'reply_resolve' => '回覆並標記為已解決', diff --git a/resources/lang/zh-tw/page_title.php b/resources/lang/zh-tw/page_title.php index 356c813e64e..fd63fcccc8c 100644 --- a/resources/lang/zh-tw/page_title.php +++ b/resources/lang/zh-tw/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '官方比賽', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '玩家資訊', 'create' => '註冊帳號', diff --git a/resources/lang/zh-tw/user_cover_presets.php b/resources/lang/zh-tw/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/zh-tw/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/zh-tw/users.php b/resources/lang/zh-tw/users.php index 49869d76e2e..8cad0c0bbe0 100644 --- a/resources/lang/zh-tw/users.php +++ b/resources/lang/zh-tw/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "", 'inactive_different_country' => "你的帳號已經一段時間沒有登入了", ], ], diff --git a/resources/lang/zh/artist.php b/resources/lang/zh/artist.php index ea8e5ca63a2..940c26555d9 100644 --- a/resources/lang/zh/artist.php +++ b/resources/lang/zh/artist.php @@ -45,6 +45,11 @@ 'index' => [ '_' => '曲目搜索', + 'exclusive_only' => [ + 'all' => '全部', + 'exclusive_only' => 'osu! 原创', + ], + 'form' => [ 'advanced' => '高级搜索', 'album' => '专辑', @@ -52,6 +57,7 @@ 'bpm_gte' => '最低 BPM', 'bpm_lte' => '最高 BPM', 'empty' => '未找到符合条件的曲目。', + 'exclusive_only' => '类型', 'genre' => '流派', 'genre_all' => '全部', 'length_gte' => '时长下限', diff --git a/resources/lang/zh/beatmaps.php b/resources/lang/zh/beatmaps.php index 9dad84bb236..0a700691aa1 100644 --- a/resources/lang/zh/beatmaps.php +++ b/resources/lang/zh/beatmaps.php @@ -213,7 +213,7 @@ 'rank_estimate' => [ '_' => '谱面正位于 :queue 中第 :position 位。如果没有问题,谱面将 :date 上架 (Ranked)。', - 'unresolved_problems' => '直到解决 :problems 之前,这张谱面会一直被冻结在过审 (Qualified) 区。', + 'unresolved_problems' => '这张谱面会一直被冻结在过审 (Qualified) 区,直到 :problems 解决。', 'problems' => '这些问题', 'on' => '在 :date', 'queue' => '谱面上架队列', diff --git a/resources/lang/zh/chat.php b/resources/lang/zh/chat.php index 231139f4011..65935233477 100644 --- a/resources/lang/zh/chat.php +++ b/resources/lang/zh/chat.php @@ -18,6 +18,8 @@ 'channels' => [ 'confirm_part' => '您想要隐藏此频道吗?您仍然会收到来自此频道的消息。', 'create' => '创建公告', + 'join' => '加入频道', + 'none' => '无频道', 'list' => [ 'title' => [ @@ -60,4 +62,8 @@ 'lazer' => '在 osu!lazer 中加入的频道将在这里显示。', 'title' => '暂无对话', ], + + 'join_channels' => [ + 'loading' => '正在加载频道列表......', + ], ]; diff --git a/resources/lang/zh/common.php b/resources/lang/zh/common.php index 81c0a2a31a9..e6706c2f490 100644 --- a/resources/lang/zh/common.php +++ b/resources/lang/zh/common.php @@ -39,6 +39,7 @@ 'pin' => '置顶', 'post' => '发表', 'read_more' => '阅读更多', + 'refresh' => '刷新', 'reply' => '回复', 'reply_reopen' => '回复并要求重审', 'reply_resolve' => '回复并标记为已解决', diff --git a/resources/lang/zh/model_validation.php b/resources/lang/zh/model_validation.php index 8860b0931a6..6de2f6c26dd 100644 --- a/resources/lang/zh/model_validation.php +++ b/resources/lang/zh/model_validation.php @@ -29,13 +29,13 @@ 'guest' => '登录后才能推荐。', 'hyped' => '你已经推荐了这张谱面。', 'limit_exceeded' => '你已经用光了推荐次数。', - 'not_hypeable' => '这张谱面无法推荐', + 'not_hypeable' => '这张谱面无法被推荐', 'owner' => '不能推荐你自己的谱面。', ], 'timestamp' => [ 'exceeds_beatmapset_length' => '指定的时间戳不在谱面范围内。', - 'negative' => "无法定位时间戳。", + 'negative' => "时间戳的值不能为负。", ], ], diff --git a/resources/lang/zh/page_title.php b/resources/lang/zh/page_title.php index 8ed2adc0475..36e13f91a57 100644 --- a/resources/lang/zh/page_title.php +++ b/resources/lang/zh/page_title.php @@ -110,6 +110,9 @@ 'tournaments_controller' => [ '_' => '比赛', ], + 'user_cover_presets_controller' => [ + '_' => '', + ], 'users_controller' => [ '_' => '玩家信息', 'create' => '创建账户', diff --git a/resources/lang/zh/user_cover_presets.php b/resources/lang/zh/user_cover_presets.php new file mode 100644 index 00000000000..bc8670fbd74 --- /dev/null +++ b/resources/lang/zh/user_cover_presets.php @@ -0,0 +1,37 @@ +. Licensed under the GNU Affero General Public License v3.0. +// See the LICENCE file in the repository root for full licence text. + +return [ + 'index' => [ + 'batch_disable' => '', + 'batch_enable' => '', + + 'batch_confirm' => [ + '_' => '', + 'disable' => '', + 'enable' => '', + 'items' => '', + ], + + 'create_form' => [ + 'files' => '', + 'submit' => '', + 'title' => '', + ], + + 'item' => [ + 'click_to_disable' => '', + 'click_to_enable' => '', + 'enabled' => '', + 'disabled' => '', + 'image_store' => '', + 'image_update' => '', + ], + ], + 'store' => [ + 'failed' => '', + 'ok' => '', + ], +]; diff --git a/resources/lang/zh/users.php b/resources/lang/zh/users.php index 06909e3001b..8282f6b1e69 100644 --- a/resources/lang/zh/users.php +++ b/resources/lang/zh/users.php @@ -97,6 +97,7 @@ 'force_reactivation' => [ 'reason' => [ + 'inactive' => "您已经很长时间没有使用您的账户了。", 'inactive_different_country' => "您已经很长时间没有使用您的账户了。", ], ], @@ -424,7 +425,7 @@ 'reason_2' => '该账号由于安全或滥用问题暂时不可用。', 'reason_3' => '你可能输错用户名了!', 'reason_header' => '可能是由于以下原因:', - 'title' => '找不到指定的用户', + 'title' => '找不到指定的用户!;_;', ], 'page' => [ 'button' => '修改资料页面', From 6973283a367ab9763e1148d1a2498bf298fe3c73 Mon Sep 17 00:00:00 2001 From: bakaneko Date: Wed, 10 Apr 2024 18:21:17 +0900 Subject: [PATCH 69/69] exclude scores with no pp from user best performance --- app/Models/Traits/UserScoreable.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/Traits/UserScoreable.php b/app/Models/Traits/UserScoreable.php index 1afe54df899..145f8b9c655 100644 --- a/app/Models/Traits/UserScoreable.php +++ b/app/Models/Traits/UserScoreable.php @@ -19,6 +19,7 @@ trait UserScoreable public function aggregatedScoresBest(string $mode, null | true $legacyOnly, int $size): array { return (new FetchDedupedScores('beatmap_id', ScoreSearchParams::fromArray([ + 'exclude_without_pp' => true, 'is_legacy' => $legacyOnly, 'limit' => $size, 'ruleset_id' => Beatmap::MODES[$mode],