Skip to content

Commit d0abfa9

Browse files
VitalyVitaly
authored andcommitted
Stay with only swiss tournament
1 parent b23e17d commit d0abfa9

File tree

14 files changed

+611
-565
lines changed

14 files changed

+611
-565
lines changed

services/app/apps/codebattle/assets/js/widgets/pages/tournament/Tournament.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ function Tournament({ waitingRoomMachine }) {
189189
useEffect(() => {
190190
if (tournament.isLive) {
191191
const channel = connectToChat(tournament.useChat, 'channel')(dispatch);
192-
193192
return () => {
194193
if (channel) {
195194
channel.leave();
@@ -199,7 +198,7 @@ function Tournament({ waitingRoomMachine }) {
199198

200199
return () => {};
201200
// eslint-disable-next-line react-hooks/exhaustive-deps
202-
}, []);
201+
}, [tournament.isLive]);
203202

204203
useEffect(() => {
205204
if (matchConfirmationModalShowing) {

services/app/apps/codebattle/assets/js/widgets/pages/tournament/TournamentChat.jsx

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, {
22
memo,
33
useCallback,
44
useRef,
5+
useEffect,
56
} from 'react';
67

78
import { useSelector } from 'react-redux';
@@ -36,34 +37,48 @@ function TournamentChat() {
3637

3738
useChatRooms('channel');
3839

40+
const messagesContainerRef = useRef(null);
41+
42+
// Auto-scroll to bottom when messages change
43+
useEffect(() => {
44+
if (messagesContainerRef.current) {
45+
const { scrollHeight, clientHeight } = messagesContainerRef.current;
46+
messagesContainerRef.current.scrollTop = scrollHeight - clientHeight;
47+
}
48+
}, [messages]);
49+
3950
return (
4051
<ChatContextMenu menuId={menuId} inputRef={inputRef} request={menuRequest}>
41-
<div className="my-2 mt-lg-0 sticky-top bg-white rounded-lg position-relative">
42-
<div className="rounded-top shadow-sm" style={{ height: '350px' }}>
52+
<div className="my-2 mt-lg-0 sticky-top bg-white rounded-lg position-relative d-flex flex-column" style={{ height: '450px' }}>
53+
<div className="d-flex border-bottom align-items-center p-2">
54+
<Rooms disabled={!isOnline} />
55+
{currentUserIsAdmin && (
56+
<button
57+
type="button"
58+
className="btn btn-sm btn-link text-danger"
59+
onClick={handleCleanBanned}
60+
disabled={!isOnline}
61+
>
62+
Clean banned
63+
</button>
64+
)}
65+
</div>
66+
<div className="px-2">
67+
<small className="text-muted text-nowrap">Please, be nice in chat</small>
68+
</div>
69+
<div className="flex-grow-1 overflow-hidden d-flex flex-column">
4370
<div
44-
className="overflow-auto h-100 text-break"
71+
ref={messagesContainerRef}
72+
className="overflow-auto h-100"
4573
id="new-chat-message"
74+
style={{ scrollBehavior: 'smooth' }}
4675
>
47-
<div className="d-flex border-bottom align-items-center">
48-
<Rooms disabled={!isOnline} />
49-
{currentUserIsAdmin && (
50-
<button
51-
type="button"
52-
className="btn btn-sm btn-link text-danger"
53-
onClick={handleCleanBanned}
54-
disabled={!isOnline}
55-
>
56-
Clean banned
57-
</button>
58-
)}
59-
</div>
60-
<div>
61-
<small className="pl-3 text-muted text-nowrap">Please, be nice in chat</small>
62-
</div>
6376
<Messages displayMenu={displayMenu} messages={messages} />
6477
</div>
6578
</div>
66-
<TournamentChatInput disabled={!isOnline} />
79+
<div className="border-top p-2">
80+
<TournamentChatInput disabled={!isOnline} />
81+
</div>
6782
</div>
6883
</ChatContextMenu>
6984
);

services/app/apps/codebattle/assets/js/widgets/pages/tournament/TournamentChatInput.jsx

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,75 @@
1-
import React, { useState, useCallback, useRef } from 'react';
1+
import React, {
2+
useState, useCallback, useRef, useEffect,
3+
} from 'react';
24

5+
import BadWordsNext from 'bad-words-next';
6+
7+
import messageTypes from '../../config/messageTypes';
38
import { addMessage } from '../../middlewares/Chat';
49

510
export default function TournamentChatInput({ disabled }) {
611
const [message, setMessage] = useState('');
12+
const [badwordsReady, setBadwordsReady] = useState(false);
13+
714
const inputRef = useRef(null);
8-
const handleChange = useCallback(({ target: { value } }) => {
9-
setMessage(value);
10-
}, [setMessage]);
11-
12-
const handleSubmit = useCallback(e => {
13-
e.preventDefault();
14-
addMessage(message);
15-
setMessage('');
16-
}, [message]);
15+
const badwordsRef = useRef(new BadWordsNext());
16+
const handleChange = useCallback(
17+
({ target: { value } }) => {
18+
setMessage(value);
19+
},
20+
[setMessage],
21+
);
22+
23+
const handleSubmit = useCallback(
24+
e => {
25+
e.preventDefault();
26+
let filteredText = message;
27+
28+
if (badwordsReady) {
29+
try {
30+
filteredText = badwordsRef.current.filter(filteredText);
31+
} catch (error) {
32+
console.error('Error filtering text:', error);
33+
}
34+
}
35+
36+
const msg = {
37+
text: filteredText,
38+
meta: { type: messageTypes.general },
39+
};
40+
41+
addMessage(msg);
42+
setMessage('');
43+
},
44+
[message, badwordsReady],
45+
);
46+
47+
useEffect(() => {
48+
let mounted = true;
49+
async function loadBadwords() {
50+
try {
51+
// Import without extension to let webpack resolve the correct file
52+
const enData = await import('bad-words-next/lib/en');
53+
const ruData = await import('bad-words-next/lib/ru');
54+
const rlData = await import('bad-words-next/lib/ru_lat');
55+
56+
if (mounted) {
57+
badwordsRef.current.add(enData.default || enData);
58+
badwordsRef.current.add(ruData.default || ruData);
59+
badwordsRef.current.add(rlData.default || rlData);
60+
setBadwordsReady(true);
61+
}
62+
} catch (error) {
63+
console.error('Error loading bad words dictionaries:', error);
64+
}
65+
}
66+
67+
loadBadwords();
68+
69+
return () => {
70+
mounted = false;
71+
};
72+
}, []);
1773

1874
return (
1975
<form className="m-0" onSubmit={handleSubmit}>

services/app/apps/codebattle/lib/codebattle/tournament/context.ex

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ defmodule Codebattle.Tournament.Context do
104104
Repo.all(
105105
from(t in Tournament,
106106
order_by: t.starts_at,
107-
where: t.event_id == ^event_id and t.state in ["waiting_participants", "active", "finished"]
107+
where:
108+
t.event_id == ^event_id and t.state in ["waiting_participants", "active", "finished"]
108109
)
109110
)
110111
end
@@ -252,67 +253,17 @@ defmodule Codebattle.Tournament.Context do
252253
_ -> nil
253254
end
254255

255-
event_id =
256-
case params[:event_id] do
257-
nil -> nil
258-
"" -> nil
259-
str when is_binary(str) -> String.to_integer(str)
260-
int -> int
261-
end
262-
263256
show_results = params[:show_results] || true
264257

265258
Map.merge(params, %{
266259
access_token: access_token,
267260
match_timeout_seconds: match_timeout_seconds,
268261
starts_at: starts_at,
269-
meta: get_meta_from_params(params),
270-
event_id: event_id,
262+
meta: %{},
271263
show_results: show_results
272264
})
273265
end
274266

275-
defp get_meta_from_params(%{meta: %{} = meta}) do
276-
meta
277-
end
278-
279-
defp get_meta_from_params(%{meta_json: meta_json}) do
280-
meta_json
281-
|> Jason.decode!()
282-
|> AtomizedMap.atomize()
283-
end
284-
285-
defp get_meta_from_params(params) do
286-
case params[:type] do
287-
"show" ->
288-
rounds_config =
289-
params
290-
|> Map.get(:rounds_config_json)
291-
|> cast_json_value()
292-
293-
game_passwords =
294-
params
295-
|> Map.get(:game_passwords_json)
296-
|> cast_json_value()
297-
298-
%{
299-
game_passwords: game_passwords,
300-
rounds_config: rounds_config
301-
}
302-
303-
"versus" ->
304-
%{rounds_limit: 1000}
305-
306-
type when type in ["arena", "swiss", "squad", "top200"] ->
307-
rounds_limit = params |> Map.get(:rounds_limit, "3") |> String.to_integer()
308-
309-
%{rounds_limit: rounds_limit}
310-
311-
_ ->
312-
%{}
313-
end
314-
end
315-
316267
def get_tournament_for_restore do
317268
@states_from_restore
318269
|> get_db_tournaments()
@@ -341,12 +292,4 @@ defmodule Codebattle.Tournament.Context do
341292
defp generate_access_token do
342293
17 |> :crypto.strong_rand_bytes() |> Base.url_encode64() |> binary_part(0, 17)
343294
end
344-
345-
defp cast_json_value(value) do
346-
case value do
347-
nil -> nil
348-
"" -> nil
349-
value -> value |> Jason.decode!() |> AtomizedMap.atomize()
350-
end
351-
end
352295
end

services/app/apps/codebattle/lib/codebattle/tournament/strategy/base.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ defmodule Codebattle.Tournament.Base do
957957
use_waiting_room?(tournament) or tournament.type in ["top200"] ->
958958
min(seconds_to_end_round(tournament), tournament.match_timeout_seconds)
959959

960-
:default ->
960+
true ->
961961
get_round_timeout_seconds(tournament)
962962
end
963963
end

services/app/apps/codebattle/lib/codebattle/tournament/task_provider.ex

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,36 @@ defmodule Codebattle.Tournament.TaskProvider do
1111
level |> Codebattle.Task.get_tasks_by_level() |> Enum.shuffle()
1212
end
1313

14-
def get_all_tasks(%{task_provider: "task_pack", task_pack_name: tp_name}) when not is_nil(tp_name) do
14+
def get_all_tasks(%{task_provider: "task_pack", task_pack_name: tp_name})
15+
when not is_nil(tp_name) do
1516
TaskPack.get_tasks_by_pack_name(tp_name)
1617
end
1718

18-
def get_all_tasks(%{task_provider: "task_pack_per_round", task_pack_name: tp_name}) when not is_nil(tp_name) do
19+
def get_all_tasks(%{task_provider: "task_pack_per_round", task_pack_name: tp_name})
20+
when not is_nil(tp_name) do
1921
tp_name
2022
|> get_task_pack_names()
2123
|> Enum.map(&TaskPack.get_tasks_by_pack_name/1)
2224
|> Enum.with_index(&{&2, &1})
2325
|> Map.new()
2426
end
2527

26-
def get_round_task_ids(%{task_provider: "task_pack", task_strategy: "sequential", task_pack_name: tp_name}, _round)
28+
def get_round_task_ids(
29+
%{task_provider: "task_pack", task_strategy: "sequential", task_pack_name: tp_name},
30+
_round
31+
)
2732
when not is_nil(tp_name) do
2833
[name: tp_name]
2934
|> TaskPack.get_by!()
3035
|> Map.get(:task_ids)
3136
end
3237

3338
def get_round_task_ids(
34-
%{task_provider: "task_pack_per_round", task_strategy: "sequential", task_pack_name: tp_name},
39+
%{
40+
task_provider: "task_pack_per_round",
41+
task_strategy: "sequential",
42+
task_pack_name: tp_name
43+
},
3544
round
3645
)
3746
when not is_nil(tp_name) do
@@ -64,8 +73,6 @@ defmodule Codebattle.Tournament.TaskProvider do
6473
Tasks.get_random_task_id_by_level(tournament, level)
6574
end
6675

67-
def get_common_round_task_id(%{task_strategy: "random_per_game"}, _params), do: nil
68-
6976
def get_common_round_task_id(%{task_strategy: "random_per_round"} = tournament, _params) do
7077
safe_random(tournament.round_task_ids)
7178
end
@@ -92,7 +99,10 @@ defmodule Codebattle.Tournament.TaskProvider do
9299
end
93100
end
94101

95-
def get_task(%{task_provider: "task_pack_per_round", task_strategy: "sequential"} = tournament, nil) do
102+
def get_task(
103+
%{task_provider: "task_pack_per_round", task_strategy: "sequential"} = tournament,
104+
nil
105+
) do
96106
tournament.round_task_ids
97107
|> Enum.at(0)
98108
|> case do

services/app/apps/codebattle/lib/codebattle/tournament/tournament.ex

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defmodule Codebattle.Tournament do
66
import Ecto.Changeset
77

88
alias Codebattle.Event
9-
alias Codebattle.Tournament.Individual
9+
alias Codebattle.Tournament.Swiss
1010
alias Runner.AtomizedMap
1111

1212
@type t :: %__MODULE__{}
@@ -56,10 +56,10 @@ defmodule Codebattle.Tournament do
5656
@levels ~w(elementary easy medium hard)
5757
@states ~w(waiting_participants canceled active timeout finished)
5858
@task_providers ~w(level task_pack task_pack_per_round all)
59-
@task_strategies ~w(random_per_game random_per_round sequential)
59+
@task_strategies ~w(random_per_round sequential)
6060
@ranking_types ~w(void by_clan by_percentile by_win_loss)
61-
@types ~w(individual show swiss arena versus squad top200)
62-
@public_types ~w(individual top200 swiss arena versus)
61+
@types ~w(swiss)
62+
@public_types ~w(swiss)
6363

6464
@default_match_timeout Application.compile_env(:codebattle, :tournament_match_timeout)
6565

@@ -87,16 +87,17 @@ defmodule Codebattle.Tournament do
8787
field(:players_limit, :integer)
8888
field(:ranking_type, :string, default: "by_player")
8989
field(:round_timeout_seconds, :integer)
90+
field(:rounds_limit, :integer)
9091
field(:show_results, :boolean, default: true)
9192
field(:started_at, :utc_datetime)
9293
field(:starts_at, :utc_datetime)
9394
field(:state, :string, default: "waiting_participants")
9495
field(:stats, AtomizedMap, default: %{})
9596
field(:task_pack_name, :string)
9697
field(:task_provider, :string, default: "level")
97-
field(:task_strategy, :string, default: "random_per_game")
98+
field(:task_strategy, :string, default: "random_per_round")
9899
field(:tournament_timeout_seconds, :integer)
99-
field(:type, :string, default: "individual")
100+
field(:type, :string, default: "swiss")
100101
field(:use_chat, :boolean, default: true)
101102
field(:use_clan, :boolean, default: false)
102103
field(:use_event_ranking, :boolean, default: false)
@@ -112,7 +113,7 @@ defmodule Codebattle.Tournament do
112113
field(:tasks_table, :string, virtual: true)
113114

114115
field(:is_live, :boolean, virtual: true, default: false)
115-
field(:module, :any, virtual: true, default: Individual)
116+
field(:module, :any, virtual: true, default: Swiss)
116117
field(:played_pair_ids, EctoMapSet, of: {:array, :integer}, virtual: true, default: [])
117118
field(:players_count, :integer, virtual: true, default: 0)
118119
field(:event_ranking, :map, virtual: true, default: %{})

0 commit comments

Comments
 (0)