diff --git a/vocodes/src/common/ApiConfig.ts b/vocodes/src/common/ApiConfig.ts index f50abb542c..2e1fef83b0 100644 --- a/vocodes/src/common/ApiConfig.ts +++ b/vocodes/src/common/ApiConfig.ts @@ -230,6 +230,10 @@ class ApiConfig { return `${this.getScheme()}://${this.getNewApiHost()}/events`; } + getLeaderboard() : string { + return `${this.getScheme()}://${this.getNewApiHost()}/leaderboard`; + } + getModerationIpBanList(): string { return `${this.getScheme()}://${this.getNewApiHost()}/moderation/ip_bans/list`; } diff --git a/vocodes/src/v2/api/misc/GetLeaderboard.ts b/vocodes/src/v2/api/misc/GetLeaderboard.ts new file mode 100644 index 0000000000..ed15b6c3a9 --- /dev/null +++ b/vocodes/src/v2/api/misc/GetLeaderboard.ts @@ -0,0 +1,72 @@ +import { ApiConfig } from "../../../common/ApiConfig"; + +export interface Leaderboard { + success: boolean, + tts_leaderboard: Array + w2l_leaderboard: Array +} + +export interface LeaderboardEntryForList { + username: string, + display_name: string, + gravatar_hash: string, + uploaded_count: number, +} + +export enum LeaderboardLookupError { + NotFound, + ServerError, + FrontendError, +} + +export type GetLeaderboardResponse = Leaderboard | LeaderboardLookupError; + +export function GetLeaderboardIsOk(response: GetLeaderboardResponse): response is Leaderboard { + return response.hasOwnProperty('tts_leaderboard'); +} + +export function GetLeaderboardIsErr(response: GetLeaderboardResponse): response is LeaderboardLookupError { + return !response.hasOwnProperty('tts_leaderboard'); +} + +interface LeaderboardResponsePayload { + success: boolean, + error_reason?: string, + tts_leaderboard?: Leaderboard, + w2l_leaderboard?: Leaderboard, +} + + +export async function GetLeaderboard() : Promise +{ + const endpoint = new ApiConfig().getLeaderboard(); + + return await fetch(endpoint, { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, + credentials: 'include', + }) + .then(res => res.json()) + .then(res => { + const response : LeaderboardResponsePayload = res; + + if (response?.success) { + return res as Leaderboard; // TODO: This is not the way. + } + + if (response?.success === false) { + if (response.error_reason?.includes("not found")) { + return LeaderboardLookupError.NotFound; + } else { + return LeaderboardLookupError.ServerError; + } + } + + return LeaderboardLookupError.FrontendError; + }) + .catch(e => { + return LeaderboardLookupError.FrontendError; + }); +} diff --git a/vocodes/src/v2/view/NewTopNavFc.tsx b/vocodes/src/v2/view/NewTopNavFc.tsx index 88d407b22a..b827766159 100644 --- a/vocodes/src/v2/view/NewTopNavFc.tsx +++ b/vocodes/src/v2/view/NewTopNavFc.tsx @@ -22,12 +22,12 @@ function NewTopNavFc(props: Props) {
Text to Speech️ + >TTS
Lip Sync Video + >Video
Upload
- {/*Pending*/} My Data @@ -47,6 +44,11 @@ function NewTopNavFc(props: Props) { className="button is-link is-medium is-inverted" >Feed
+
+ Leaderboard +

diff --git a/vocodes/src/v2/view/NewVocodesContainer.tsx b/vocodes/src/v2/view/NewVocodesContainer.tsx index 3658abb4a5..8aa49ef418 100644 --- a/vocodes/src/v2/view/NewVocodesContainer.tsx +++ b/vocodes/src/v2/view/NewVocodesContainer.tsx @@ -38,6 +38,7 @@ import { TtsModelListFc } from './tts/tts_model_list/TtsModelListFc'; import { TtsModelListItem } from '../api/tts/ListTtsModels'; import { ProfileBanFc } from './profile/profile_ban/ProfileBanFc'; import { ModerationUserListFc } from './moderation/moderation_user_list/ModerationUserList'; +import { LeaderboardFc } from './leaderboard/LeaderboardFc'; interface Props { sessionWrapper: SessionWrapper, @@ -97,6 +98,12 @@ class NewVocodesContainer extends React.Component { /> + + + + (undefined); + const [ttsLeaderboard, setTtsLeaderboard] = useState|undefined>(undefined); + const [w2lLeaderboard, setW2lLeaderboard] = useState|undefined>(undefined); + + const getLeaderboard = useCallback(async () => { + const leaderboardReponse = await GetLeaderboard(); + + if (GetLeaderboardIsOk(leaderboardReponse)) { + setLeaderboard(leaderboardReponse); + setTtsLeaderboard(leaderboardReponse.tts_leaderboard); + setW2lLeaderboard(leaderboardReponse.w2l_leaderboard); + } else { + } + }, []); + + useEffect(() => { + getLeaderboard(); + }, [getLeaderboard]); + + if (!leaderboard) { + return
; + } + + let ttsRows : Array = []; + + if (ttsLeaderboard) { + ttsLeaderboard.forEach(ttsEntry => { + ttsRows.push( + + + + +   + {ttsEntry.display_name} + + + {ttsEntry.uploaded_count} + + ) + }) + } + + let w2lRows : Array = []; + + if (w2lLeaderboard) { + w2lLeaderboard.forEach(w2lEntry => { + w2lRows.push( + + + + +   + {w2lEntry.display_name} + + + {w2lEntry.uploaded_count} + + ) + }) + } + + return ( +
+

Leaderboard

+ + + + + + + + {ttsRows} + + + + + + {w2lRows} + + +
+

TTS Models Uploaded

+
+
+

W2L Templates Uploaded

+
+
+ ) +} + +export { LeaderboardFc } \ No newline at end of file