Skip to content

Commit

Permalink
Merge pull request #11451 from notbakaneko/feature/refactor-username-…
Browse files Browse the repository at this point in the history
…input-lookup

Refactor username input lookup component
  • Loading branch information
nanaya authored Aug 30, 2024
2 parents c956625 + c4c18a4 commit a380f26
Show file tree
Hide file tree
Showing 17 changed files with 328 additions and 195 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- name: Install js dependencies
run: yarn --frozen-lockfile

- run: 'yarn lint --max-warnings 88 > /dev/null'
- run: 'yarn lint --max-warnings 87 > /dev/null'

- run: yarn pretty

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@

declare(strict_types=1);

namespace App\Http\Controllers\Chat;
namespace App\Http\Controllers\Users;

use App\Http\Controllers\Controller as BaseController;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Transformers\UserCompactTransformer;

class UsersController extends BaseController
class LookupController extends Controller
{
public function index()
public function __construct()
{
priv_check('ChatAnnounce')->ensureCan();
$this->middleware('auth');
$this->middleware('throttle:30,1');
}

public function lookup()
{
// TODO: referer check?
$params = get_params(request()->all(), null, ['ids:string[]'], ['null_missing' => true]);
$ids = array_slice($params['ids'], 0, 50);

Expand All @@ -26,11 +31,12 @@ public function index()
if (ctype_digit($id)) {
$numericIds[] = $id;
} elseif (present($id)) {
$stringIds[] = $id;
$stringIds[] = $id[0] === '@' ? substr($id, 1) : $id;
}
}

$users = User::where(fn ($q) => $q->whereIn('user_id', $numericIds)->orWhereIn('username', $stringIds))
->where('group_id', '<>', app('groups')->byIdentifier('no_profile')->getKey())
->default()
->with(UserCompactTransformer::CARD_INCLUDES_PRELOAD)
->get();
Expand Down
9 changes: 0 additions & 9 deletions app/Http/Controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ public function __construct()
$this->middleware('guest', ['only' => ['create', 'store', 'storeWeb']]);
$this->middleware('auth', ['only' => [
'checkUsernameAvailability',
'checkUsernameExists',
'report',
'me',
'posts',
Expand Down Expand Up @@ -159,14 +158,6 @@ public function checkUsernameAvailability()
];
}

public function checkUsernameExists()
{
$username = get_string(request('username'));
$user = User::lookup($username, 'username') ?? UserNotFound::instance();

return json_item($user, 'UserCompact', ['cover', 'country']);
}

public function extraPages($_id, $page)
{
// TODO: counts basically duplicated from UserCompactTransformer
Expand Down
1 change: 1 addition & 0 deletions resources/css/bem-index.less
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@
@import "bem/user-verification";
@import "bem/user-verification-popup";
@import "bem/username-change";
@import "bem/username-input";
@import "bem/value-display";
@import "bem/warning-box";
@import "bem/wiki-main-page";
Expand Down
12 changes: 0 additions & 12 deletions resources/css/bem/chat-form.less
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,6 @@
flex: 1;
resize: none;
}

&--users {
flex: 1;
// can't do much about wrapping the input itself...
min-width: 40px;
}
}

&__spinner {
display: inline-flex;
align-items: center;
min-width: 1em;
}

&__title {
Expand Down
30 changes: 30 additions & 0 deletions resources/css/bem/username-input.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

.username-input {
display: flex;
flex: 1;
flex-wrap: wrap;
gap: 2px;
justify-content: flex-start;
min-width: 0; // so user-card-brick overflows properly.

&--beatmap-owner-editor {
min-height: 20px;
}

&__input {
.reset-input();
color: hsl(var(--hsl-c1));

flex: 1;
// can't do much about wrapping the input itself...
min-width: 40px;
}

&__spinner {
display: inline-flex;
align-items: center;
min-width: 1em;
}
}
14 changes: 6 additions & 8 deletions resources/js/beatmap-discussions/beatmap-owner-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import { onErrorWithCallback } from 'utils/ajax';
import { classWithModifiers } from 'utils/css';
import { transparentGif } from 'utils/html';
import { trans } from 'utils/lang';
import { apiLookupUsers } from 'utils/user';
import DiscussionsState from './discussions-state';

interface XhrCollection {
updateOwner: JQuery.jqXHR<BeatmapsetWithDiscussionsJson>;
userLookup: JQuery.jqXHR<UserJson>;
userLookup: ReturnType<typeof apiLookupUsers>;
}

interface Props {
Expand Down Expand Up @@ -260,13 +261,10 @@ export default class BeatmapOwnerEditor extends React.Component<Props> {

if (currentCheckingUser == null) return;

this.xhr.userLookup = $.ajax(route('users.check-username-exists'), {
data: { username: currentCheckingUser },
method: 'POST',
});
this.xhr.userLookup.done((user) => runInAction(() => {
if (user.id > 0) {
this.props.userByName.set(currentCheckingUser, user);
this.xhr.userLookup = apiLookupUsers([currentCheckingUser]);
this.xhr.userLookup.done((response) => runInAction(() => {
if (response.users.length > 0) {
this.props.userByName.set(currentCheckingUser, response.users[0]);
}
})).fail(
onErrorWithCallback(this.userLookup),
Expand Down
55 changes: 14 additions & 41 deletions resources/js/chat/create-announcement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

import BigButton from 'components/big-button';
import InputContainer from 'components/input-container';
import { Spinner } from 'components/spinner';
import UserCardBrick from 'components/user-card-brick';
import UsernameInput from 'components/username-input';
import UserJson from 'interfaces/user-json';
import { action, computed, makeObservable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
Expand All @@ -15,14 +15,10 @@ import { trans } from 'utils/lang';

type Props = Record<string, never>;

const BusySpinner = ({ busy }: { busy: boolean }) => (
<div className='chat-form__spinner'>
{busy && <Spinner />}
</div>
);

@observer
export default class CreateAnnouncement extends React.Component<Props> {
private readonly usernameInputInitialProps;

@computed
private get canSend() {
return core.dataStore.chatState.isReady && !core.dataStore.chatState.isAddingChannel && this.model.isValid;
Expand All @@ -41,6 +37,8 @@ export default class CreateAnnouncement extends React.Component<Props> {
runInAction(() => {
this.model.initialize();
});

this.usernameInputInitialProps = runInAction(() => this.model.propsForUsernameInput);
}

render() {
Expand Down Expand Up @@ -84,18 +82,15 @@ export default class CreateAnnouncement extends React.Component<Props> {
>
<div className='chat-form__users'>
<UserCardBrick user={core.currentUserOrFail} />
{this.renderValidUsers()}
<input
className='chat-form__input chat-form__input--users'
<UsernameInput
id='chat-form-users'
ignoreCurrentUser
name='users'
onBlur={this.handleBlur}
onChange={this.handleUsersInputChange}
onKeyDown={this.handleUsersInputKeyDown}
onPaste={this.handleUsersInputPaste}
value={this.model.inputs.users}
onValidUsersChanged={this.handleValidUsersChanged}
onValueChanged={this.handleUsernameInputValueChanged}
{...this.usernameInputInitialProps}
/>
<BusySpinner busy={this.model.lookingUpUsers} />
</div>
</InputContainer>
<InputContainer
Expand Down Expand Up @@ -152,34 +147,12 @@ export default class CreateAnnouncement extends React.Component<Props> {
};

@action
private readonly handleRemoveUser = (user: UserJson) => {
this.model.validUsers.delete(user.id);
private readonly handleUsernameInputValueChanged = (value: string) => {
this.model.inputs.users = value;
};

@action
private readonly handleUsersInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.model.updateUsers(e.currentTarget.value, false);
private readonly handleValidUsersChanged = (value: Map<number, UserJson>) => {
this.model.validUsers = value;
};

@action
private readonly handleUsersInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
const elem = e.currentTarget;
if (e.key === 'Backspace' && elem.selectionStart === 0 && elem.selectionEnd === 0) {
const last = [...this.model.validUsers.keys()].pop();
if (last != null) {
this.model.validUsers.delete(last);
}
}
};

@action
private readonly handleUsersInputPaste = (e: React.SyntheticEvent<HTMLInputElement>) => {
this.model.updateUsers(e.currentTarget.value, true);
};

private renderValidUsers() {
return [...this.model.validUsers.values()].map((user) => (
<UserCardBrick key={user.id} onRemoveClick={this.handleRemoveUser} user={user} />
));
}
}
Loading

0 comments on commit a380f26

Please sign in to comment.