the browser-facing portion of osu!
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
2// See the LICENCE file in the repository root for full licence text.
3
4import CurrentUserJson from 'interfaces/current-user-json';
5import { route } from 'laroute';
6import OsuCore from 'osu-core';
7import { fileuploadFailCallback } from 'utils/ajax';
8
9const hoverClass = 'js-account-edit-avatar--hover';
10const savingClass = 'js-account-edit-avatar--saving';
11const startClass = 'js-account-edit-avatar--start';
12
13export default class AccountEditAvatar {
14 private dragging = false;
15 private element: HTMLElement | null = null;
16 private overlayLeaveTimeout?: number;
17
18 constructor(private readonly core: OsuCore) {
19 $(document).on('turbo:load', this.initialize);
20 $(document).on('turbo:before-cache', this.rollback);
21
22 $.subscribe('dragenterGlobal', this.overlayStart);
23 $.subscribe('dragendGlobal', this.overlayEnd);
24 $(document).on('dragenter', '.js-account-edit-avatar', this.overlayEnter);
25 $(document).on('dragover', '.js-account-edit-avatar', this.overlayHover);
26 }
27
28 private readonly initialize = () => {
29 this.element = document.querySelector('.js-account-edit-avatar');
30 const element = this.element;
31 if (element == null) return;
32
33 $('.js-account-edit-avatar__button').fileupload({
34 always: () => {
35 element.classList.remove(savingClass);
36 },
37 dataType: 'json',
38 done: (_e: unknown, data: { result: CurrentUserJson }) => {
39 this.core.setCurrentUser(data.result);
40 },
41 dropZone: $(element),
42 fail: fileuploadFailCallback,
43 submit: () => {
44 element.classList.add(savingClass);
45 $.publish('dragendGlobal');
46 },
47 url: route('account.avatar'),
48 });
49
50 $('.js-account-edit-avatar--reset')
51 .on('ajax:send', () => {
52 element.classList.add(savingClass);
53 }).on('ajax:complete', () => {
54 element.classList.remove(savingClass);
55 }).on('ajax:success', (_e: unknown, data: CurrentUserJson) => {
56 this.core.setCurrentUser(data);
57 });
58 };
59
60 private readonly overlayEnd = () => {
61 this.element?.classList.remove(startClass);
62 };
63
64 private readonly overlayEnter = () => {
65 this.dragging = true;
66 };
67
68 private readonly overlayHover = () => {
69 if (!this.dragging) return;
70
71 this.element?.classList.add(hoverClass);
72
73 // see GlobalDrag
74 window.clearTimeout(this.overlayLeaveTimeout);
75 this.overlayLeaveTimeout = window.setTimeout(this.overlayLeave, 100);
76 };
77
78 private readonly overlayLeave = () => {
79 this.dragging = false;
80 this.element?.classList.remove(hoverClass);
81 };
82
83 private readonly overlayStart = () => {
84 this.element?.classList.add(startClass);
85 };
86
87 private readonly rollback = () => {
88 if (this.element == null) return;
89 $('.js-account-edit-avatar__button').fileupload('destroy');
90 };
91}