<commit_before>// Copyright (c) ppy Pty Ltd <<EMAIL>>. 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 UserAvatar from 'components/user-avatar';
import BeatmapJson from 'interfaces/beatmap-json';
import BeatmapsetExtendedJson from 'interfaces/beatmapset-extended-json';
import UserJson from 'interfaces/user-json';
import { route } from 'laroute';
import { makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { onErrorWithCallback } from 'utils/ajax';
import { classWithModifiers } from 'utils/css';
import { transparentGif } from 'utils/html';

type BeatmapsetWithDiscussionJson = BeatmapsetExtendedJson;

interface Props {
  beatmap: BeatmapJson;
  beatmapsetUser: UserJson;
  user: UserJson;
  userByName: Map<string, UserJson>;
}

@observer
export default class BeatmapOwnerEditor extends React.Component<Props> {
  @observable private checkingUser = false;
  @observable private editing = false;
  private inputRef = React.createRef<HTMLInputElement>();
  @observable private inputUser?: UserJson;
  @observable private inputUsername: string;
  private shouldFocusInputOnNextRender = false;
  @observable private updatingOwner = false;
  private userLookupTimeout?: number;
  private xhr: Partial<Record<string, JQuery.jqXHR>> = {};

  constructor(props: Props) {
    super(props);

    this.inputUsername = props.user.username;
    this.inputUser = props.user;

    makeObservable(this);
  }

  componentDidUpdate() {
    if (this.shouldFocusInputOnNextRender) {
      this.shouldFocusInputOnNextRender = false;
      this.inputRef.current?.focus();
    }
  }

  componentWillUnmount() {
    window.clearTimeout(this.userLookupTimeout);
    Object.values(this.xhr).forEach((xhr) => xhr?.abort());
  }

  render() {
    const blockClass = classWithModifiers('beatmap-owner-editor', {
      editing: this.editing,
    });

    return (
      <div className={blockClass}>
        <div className='beatmap-owner-editor__col'>
          <span className='beatmap-owner-editor__mode'>
            <span className={`fal fa-fw fa-extra-mode-${this.props.beatmap.mode}`} />
          </span>
        </div>

        <div className='beatmap-owner-editor__col beatmap-owner-editor__col--version'>
          <span className='u-ellipsis-overflow'>
            {this.props.beatmap.version}
          </span>
        </div>

        <div className='beatmap-owner-editor__col beatmap-owner-editor__col--avatar'>
          {this.renderAvatar()}
        </div>

        <div className='beatmap-owner-editor__col'>
          {this.renderUsername()}
        </div>

        <div className='beatmap-owner-editor__col beatmap-owner-editor__col--buttons'>
          {this.renderButtons()}
        </div>
      </div>
    );
  }

  private handleCancelEditingClick = () => {
    this.editing = false;
  };

  private handleResetClick = () => {
    if (!confirm(osu.trans('beatmap_discussions.owner_editor.reset_confirm'))) return;

    this.editing = false;
    this.updateOwner(this.props.beatmapsetUser.id);
  };

  private handleSaveClick = () => {
    if (this.inputUser == null) return;

    this.updateOwner(this.inputUser.id);
  };

  private handleStartEditingClick = () => {
    this.editing = true;
    this.shouldFocusInputOnNextRender = true;
    this.inputUser = this.props.user;
    this.inputUsername = this.props.user.username;
  };

  private handleUsernameInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.inputUsername = e.currentTarget.value.trim();

    this.inputUser = this.props.userByName.get(this.inputUsername);

    window.clearTimeout(this.userLookupTimeout);

    if (this.inputUser == null && this.inputUsername !== '') {
      this.checkingUser = true;
      window.setTimeout(this.userLookup, 500);
    }
  };

  private handleUsernameInputKeyup = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.keyCode === 13) this.handleSaveClick();
  };<commit_msg>move trim<commit_after>// Copyright (c) ppy Pty Ltd <<EMAIL>>. 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 UserAvatar from 'components/user-avatar';
import BeatmapJson from 'interfaces/beatmap-json';
import BeatmapsetExtendedJson from 'interfaces/beatmapset-extended-json';
import UserJson from 'interfaces/user-json';
import { route } from 'laroute';
import { makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { onErrorWithCallback } from 'utils/ajax';
import { classWithModifiers } from 'utils/css';
import { transparentGif } from 'utils/html';

type BeatmapsetWithDiscussionJson = BeatmapsetExtendedJson;

interface Props {
  beatmap: BeatmapJson;
  beatmapsetUser: UserJson;
  user: UserJson;
  userByName: Map<string, UserJson>;
}

@observer
export default class BeatmapOwnerEditor extends React.Component<Props> {
  @observable private checkingUser = false;
  @observable private editing = false;
  private inputRef = React.createRef<HTMLInputElement>();
  @observable private inputUser?: UserJson;
  @observable private inputUsername: string;
  private shouldFocusInputOnNextRender = false;
  @observable private updatingOwner = false;
  private userLookupTimeout?: number;
  private xhr: Partial<Record<string, JQuery.jqXHR>> = {};

  constructor(props: Props) {
    super(props);

    this.inputUsername = props.user.username;
    this.inputUser = props.user;

    makeObservable(this);
  }

  componentDidUpdate() {
    if (this.shouldFocusInputOnNextRender) {
      this.shouldFocusInputOnNextRender = false;
      this.inputRef.current?.focus();
    }
  }

  componentWillUnmount() {
    window.clearTimeout(this.userLookupTimeout);
    Object.values(this.xhr).forEach((xhr) => xhr?.abort());
  }

  render() {
    const blockClass = classWithModifiers('beatmap-owner-editor', {
      editing: this.editing,
    });

    return (
      <div className={blockClass}>
        <div className='beatmap-owner-editor__col'>
          <span className='beatmap-owner-editor__mode'>
            <span className={`fal fa-fw fa-extra-mode-${this.props.beatmap.mode}`} />
          </span>
        </div>

        <div className='beatmap-owner-editor__col beatmap-owner-editor__col--version'>
          <span className='u-ellipsis-overflow'>
            {this.props.beatmap.version}
          </span>
        </div>

        <div className='beatmap-owner-editor__col beatmap-owner-editor__col--avatar'>
          {this.renderAvatar()}
        </div>

        <div className='beatmap-owner-editor__col'>
          {this.renderUsername()}
        </div>

        <div className='beatmap-owner-editor__col beatmap-owner-editor__col--buttons'>
          {this.renderButtons()}
        </div>
      </div>
    );
  }

  private handleCancelEditingClick = () => {
    this.editing = false;
  };

  private handleResetClick = () => {
    if (!confirm(osu.trans('beatmap_discussions.owner_editor.reset_confirm'))) return;

    this.editing = false;
    this.updateOwner(this.props.beatmapsetUser.id);
  };

  private handleSaveClick = () => {
    if (this.inputUser == null) return;

    this.updateOwner(this.inputUser.id);
  };

  private handleStartEditingClick = () => {
    this.editing = true;
    this.shouldFocusInputOnNextRender = true;
    this.inputUser = this.props.user;
    this.inputUsername = this.props.user.username;
  };

  private handleUsernameInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.inputUsername = e.currentTarget.value;

    this.inputUser = this.props.userByName.get(this.inputUsername.trim());

    window.clearTimeout(this.userLookupTimeout);

    if (this.inputUser == null && this.inputUsername !== '') {
      this.checkingUser = true;
      window.setTimeout(this.userLookup, 500);
    }
  };

  private handleUsernameInputKeyup = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.keyCode === 13) this.handleSaveClick();
  };
{'fraction_non_alphanumeric': 0.1276595744680851, 'fraction_numerical': 0.0017119100024455857, 'mean_word_length': 3.5846412556053813, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4939864', 'n_tokens_mistral': 2502, 'n_tokens_neox': 2445, 'n_words': 493}